尝试通过 Postman 调用 OWIN OAuth 安全 Web Api 来获取 JWT 时出现“错误”:“unsupported_grant_type”
Posted
技术标签:
【中文标题】尝试通过 Postman 调用 OWIN OAuth 安全 Web Api 来获取 JWT 时出现“错误”:“unsupported_grant_type”【英文标题】:Getting "error": "unsupported_grant_type" when trying to get a JWT by calling an OWIN OAuth secured Web Api via Postman 【发布时间】:2022-03-03 04:36:37 【问题描述】:我已经按照this article 实现了一个 OAuth 授权服务器。但是,当我使用 post man 获取令牌时,响应中出现错误:
“错误”:“unsupported_grant_type”
我在某处读到 Postman 中的数据需要使用 Content-type:application/x-www-form-urlencoded
发布。我已经在 Postman 中准备好所需的设置:
然而我的标题是这样的:
这是我的代码
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
context.Validated();
return Task.FromResult<object>(null);
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
if (context.OwinContext.Request.Method == "OPTIONS" && context.IsTokenEndpoint)
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Methods", new[] "POST" );
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] "accept", "authorization", "content-type" );
context.OwinContext.Response.StatusCode = 200;
context.RequestCompleted();
return Task.FromResult<object>(null);
return base.MatchEndpoint(context);
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
string allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] allowedOrigin );
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] "Content-Type" );
Models.TheUser user = new Models.TheUser();
user.UserName = context.UserName;
user.FirstName = "Sample first name";
user.LastName = "Dummy Last name";
ClaimsIdentity identity = new ClaimsIdentity("JWT");
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
foreach (string claim in user.Claims)
identity.AddClaim(new Claim("Claim", claim));
var ticket = new AuthenticationTicket(identity, null);
context.Validated(ticket);
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
private readonly string _issuer = string.Empty;
public CustomJwtFormat(string issuer)
_issuer = issuer;
public string Protect(AuthenticationTicket data)
string audienceId = ConfigurationManager.AppSettings["AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
public AuthenticationTicket Unprotect(string protectedText)
throw new NotImplementedException();
在上面的 CustomJWTFormat 类中,只有构造函数中的断点被命中。在 CustomOauth 类中,GrantResourceOwnerCredentials 方法中的断点永远不会被命中。其他人都这样做。
启动类:
public class Startup
public void Configuration(IAppBuilder app)
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
app.UseWebApi(config);
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
var OAuthServerOptions = new OAuthAuthorizationServerOptions()
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["Issuer"])
;
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
string issuer = ConfigurationManager.AppSettings["Issuer"];
string audienceId = ConfigurationManager.AppSettings["AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] audienceId ,
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
);
我需要在 web api 代码的其他地方设置Content-type:application/x-www-form-urlencoded
吗?有什么问题?请帮忙。
【问题讨论】:
我不想传递用户名密码,我想使用 twitter 消费者密钥和消费者秘密等外部提供者进行验证,我该怎么做? 感谢您,尽管您的问题和最终答案实际上并不是我想要的,但内联 sn-p 似乎已经为我解决了这个问题。我正在努力解决受客户端 ID/秘密保护的 OPTIONS 身份验证令牌点。你救了我! 确保您的授权码没有过期。我正在使用邮递员来测试我的代码。该请求包含旧的授权码。 【参考方案1】:回复有点晚了——但万一以后有人遇到问题……
从上面的屏幕截图 - 似乎您将 url 数据(用户名、密码、授权类型)添加到标题而不是正文元素。
点击body标签,然后选择“x-www-form-urlencoded”单选按钮,下面应该有一个key-value列表,可以输入请求数据
【讨论】:
我怎样才能从角度服务调用中发出相同的请求? 我犯了同样的错误。通过在当前版本的 Postman 中选择“x-www-form-urlencoded”,“Content-Type”键将自动添加到 Headers 选项卡中。其他参数需要在 Body 选项卡中添加。 为什么它不能使用 body 的表单数据来代替 x-www-form-urlencoded?【参考方案2】:使用 Postman,选择 Body 选项卡并选择 raw 选项并输入以下内容:
grant_type=password&username=yourusername&password=yourpassword
【讨论】:
对于包含p@ssword
等特殊字符的正文,是否需要将“@”替换为“%40”?
@GregDegruy 看起来只有密码必须是 url 编码的。用户名可以包含“@”,但我必须将密码中的 & 替换为 %26【参考方案3】:
-
注意网址:
localhost:55828/token
(不是localhost:55828/API/token
)
记下请求数据。它不是 json 格式,它只是没有双引号的纯数据。
userName=xxx@gmail.com&password=Test123$&grant_type=password
注意内容类型。 Content-Type: 'application/x-www-form-urlencoded' (不是 Content-Type: 'application/json')
当您使用 javascript 发出 post 请求时,您可以使用以下方式:
$http.post("localhost:55828/token",
"userName=" + encodeURIComponent(email) +
"&password=" + encodeURIComponent(password) +
"&grant_type=password",
headers: 'Content-Type': 'application/x-www-form-urlencoded'
).success(function (data) //...
请看下面 Postman 的截图:
【讨论】:
我正在发送与上述相同的请求,但仍然收到 invalid_grant。请提出解决方案。 很好的答案!步骤有所帮助。 Postman 根据选择的正文自动选择内容类型的标题。输入文本内容时不能改成application/x-www-form-urlencoded【参考方案4】:如果你使用 AngularJS,你需要将 body 参数作为字符串传递:
factory.getToken = function(person_username)
console.log('Getting DI Token');
var url = diUrl + "/token";
return $http(
method: 'POST',
url: url,
data: 'grant_type=password&username=myuser@user.com&password=mypass',
responseType:'json',
headers: 'Content-Type': 'application/x-www-form-urlencoded'
);
;
【讨论】:
也与角度 2 相关,我试图将数据变量作为对象传递,但出现错误,将数据作为字符串传递解决了它。【参考方案5】:尝试将其添加到您的有效负载中
grant_type=password&username=pippo&password=pluto
【讨论】:
【参考方案6】:我也遇到了这个错误,原因最终是错误的调用 url。如果其他人碰巧混合了网址并收到此错误,我将把这个答案留在这里。我花了几个小时才意识到我的网址有误。
我得到的错误(HTTP 代码 400):
"error": "unsupported_grant_type",
"error_description": "grant type not supported"
我在打电话:
https://MY_INSTANCE.lightning.force.com
虽然正确的 URL 应该是:
https://MY_INSTANCE.cs110.my.salesforce.com
【讨论】:
【参考方案7】:老问题,但对于angular 6
,这需要在您使用HttpClient
时完成
我在这里公开公开令牌数据,但如果通过只读属性访问会很好。
import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable, of from 'rxjs';
import delay, tap from 'rxjs/operators';
import Router from '@angular/router';
@Injectable()
export class AuthService
isLoggedIn: boolean = false;
url = "token";
tokenData = ;
username = "";
AccessToken = "";
constructor(private http: HttpClient, private router: Router)
login(username: string, password: string): Observable<object>
let model = "username=" + username + "&password=" + password + "&grant_type=" + "password";
return this.http.post(this.url, model).pipe(
tap(
data =>
console.log('Log In succesful')
//console.log(response);
this.isLoggedIn = true;
this.tokenData = data;
this.username = data["username"];
this.AccessToken = data["access_token"];
console.log(this.tokenData);
return true;
,
error =>
console.log(error);
return false;
)
);
【讨论】:
【参考方案8】:此“unsupported_grant_type”错误的另一个常见原因是将 API 调用为 GET 而不是 POST。
【讨论】:
【参考方案9】:使用grant_type=您的密码
【讨论】:
请解释这是如何回答发布的问题的?以上是关于尝试通过 Postman 调用 OWIN OAuth 安全 Web Api 来获取 JWT 时出现“错误”:“unsupported_grant_type”的主要内容,如果未能解决你的问题,请参考以下文章
通过 Postman 调用 National Rail SOAP XML API
Api 调用适用于从 Postman 获取的令牌,但不适用于通过 nodejs 应用程序获取的令牌
修改 OWIN OAuth 中间件以使用 JWT 不记名令牌