通过Postman调用OWIN OAuth安全Web Api尝试获取JWT时获取"错误":"unsupported_grant_type"

use*_*358 64 c# oauth-2.0 asp.net-web-api owin

我已按照本文实现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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的CustomJWTFormat类中,只有构造函数中的断点被命中.在CustomOauth类中,GrantResourceOwnerCredentials方法中的断点永远不会被命中.其他人呢.

Startup类:

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)
                }
            });
    }
}
Run Code Online (Sandbox Code Playgroud)

我是否需要Content-type:application/x-www-form-urlencoded在web api代码中的其他位置设置?可能有什么不对?请帮忙.

Mar*_*rkP 118

答案有点晚 - 但如果将来有人有问题......

从上面的屏幕截图中 - 您似乎将URL数据(用户名,密码,grant_type)添加到标题而不是body元素.

单击正文选项卡,然后选择"x-www-form-urlencoded"单选按钮,下面应该有一个键值列表,您可以在其中输入请求数据

  • 如何从角度服务电话中提出相同的请求? (3认同)

小智 53

使用Postman,选择Body选项卡并选择原始选项并键入以下内容:

grant_type=password&username=yourusername&password=yourpassword
Run Code Online (Sandbox Code Playgroud)

  • 这对我来说很好,谢谢! (2认同)

Chi*_*rag 17

  1. 注意URL :( localhost:55828/token不是localhost:55828/API/token)
  2. 请注意请求数据.它不是json格式,它只是没有双引号的普通数据. userName=xxx@gmail.com&password=Test123$&grant_type=password
  3. 请注意内容类型.内容类型:'application/x-www-form-urlencoded'(不是Content-Type:'application/json')
  4. 当您使用JavaScript发布帖子请求时,您可以使用以下内容:

    $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) {//...
    
    Run Code Online (Sandbox Code Playgroud)

请参阅Postman下面的截图:

邮差要求

邮递员请求标题

  • 我正在发送与上述相同的请求,但仍然收到invalid_grant。请提出解决方案。 (2认同)

Gre*_*ory 5

如果您使用的是AngularJS,则需要将body params作为字符串传递:

    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' }
    });
};
Run Code Online (Sandbox Code Playgroud)

  • 也与angular 2有关,我试图将数据变量作为对象传递,我得到一个错误,将数据作为字符串传递解决了它. (2认同)