获取 "error":"unsupported_grant_type" 尝试通过邮递员调用 OWIN OAuth 安全 Web Api 获取 JWT
Getting "error": "unsupported_grant_type" when trying to get a JWT by calling an OWIN OAuth secured Web Api via Postman
我已按照 this article 实施 OAuth 授权服务器。但是,当我使用 post man 获取令牌时,响应中出现错误:
"error": "unsupported_grant_type"
我在某处读到 Postman 中的数据需要使用 Content-type:application/x-www-form-urlencoded
编辑 post。我已经在 Postman 中准备好所需的设置:
然而我的 headers 是这样的:
这是我的代码
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 class 中,只有构造函数中的断点被命中。在 CustomOauth class 中,永远不会命中 GrantResourceOwnerCredentials 方法中的断点。其他人做。
初创公司class:
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)
}
});
}
}
我是否需要在网络 api 代码的其他地方设置 Content-type:application/x-www-form-urlencoded
?有什么问题吗?请帮忙。
回复有点晚了 - 但万一以后有人遇到这个问题...
从上面的屏幕截图来看 - 似乎您正在将 url 数据(用户名、密码、grant_type)添加到 header 而不是 body元素.
单击 body 选项卡,然后单击 select "x-www-form-urlencoded" 单选按钮,下面应该有一个 key-value 列表,您可以在其中输入请求数据
尝试将其添加到您的负载中
grant_type=password&username=pippo&password=pluto
使用 Postman,select 正文选项卡并选择原始选项并键入以下内容:
grant_type=password&username=yourusername&password=yourpassword
- 注意 URL:
localhost:55828/token
(不是 localhost:55828/API/token
)
- 记下请求数据。它不是 json 格式,它只是没有双引号的纯数据。
userName=xxx@gmail.com&password=Test123$&grant_type=password
- 注意内容类型。内容类型:'application/x-www-form-urlencoded'(不是内容类型:'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 的屏幕截图:
使用grant_type={您的密码}
如果您使用 AngularJS,您需要将正文参数作为字符串传递:
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' }
});
};
老问题,但是对于 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;
}
)
);
}
}
我也遇到了这个错误,最终原因是调用错误 url。如果其他人碰巧混合 urls 并收到此错误,我将把这个答案留在这里。我花了几个小时才意识到我错了 URL.
我遇到的错误(HTTP 代码 400):
{
"error": "unsupported_grant_type",
"error_description": "grant type not supported"
}
我正在打电话:
https://MY_INSTANCE.lightning.force.com
而正确的 URL 应该是:
此 'unsupported_grant_type' 错误的另一个常见原因是调用 API 作为 GET 而不是 POST。
我已按照 this article 实施 OAuth 授权服务器。但是,当我使用 post man 获取令牌时,响应中出现错误:
"error": "unsupported_grant_type"
我在某处读到 Postman 中的数据需要使用 Content-type:application/x-www-form-urlencoded
编辑 post。我已经在 Postman 中准备好所需的设置:
然而我的 headers 是这样的:
这是我的代码
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 class 中,只有构造函数中的断点被命中。在 CustomOauth class 中,永远不会命中 GrantResourceOwnerCredentials 方法中的断点。其他人做。
初创公司class:
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)
}
});
}
}
我是否需要在网络 api 代码的其他地方设置 Content-type:application/x-www-form-urlencoded
?有什么问题吗?请帮忙。
回复有点晚了 - 但万一以后有人遇到这个问题...
从上面的屏幕截图来看 - 似乎您正在将 url 数据(用户名、密码、grant_type)添加到 header 而不是 body元素.
单击 body 选项卡,然后单击 select "x-www-form-urlencoded" 单选按钮,下面应该有一个 key-value 列表,您可以在其中输入请求数据
尝试将其添加到您的负载中
grant_type=password&username=pippo&password=pluto
使用 Postman,select 正文选项卡并选择原始选项并键入以下内容:
grant_type=password&username=yourusername&password=yourpassword
- 注意 URL:
localhost:55828/token
(不是localhost:55828/API/token
) - 记下请求数据。它不是 json 格式,它只是没有双引号的纯数据。
userName=xxx@gmail.com&password=Test123$&grant_type=password
- 注意内容类型。内容类型:'application/x-www-form-urlencoded'(不是内容类型:'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 的屏幕截图:
使用grant_type={您的密码}
如果您使用 AngularJS,您需要将正文参数作为字符串传递:
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' }
});
};
老问题,但是对于 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;
}
)
);
}
}
我也遇到了这个错误,最终原因是调用错误 url。如果其他人碰巧混合 urls 并收到此错误,我将把这个答案留在这里。我花了几个小时才意识到我错了 URL.
我遇到的错误(HTTP 代码 400):
{
"error": "unsupported_grant_type",
"error_description": "grant type not supported"
}
我正在打电话:
https://MY_INSTANCE.lightning.force.com
而正确的 URL 应该是:
此 'unsupported_grant_type' 错误的另一个常见原因是调用 API 作为 GET 而不是 POST。