如何在角度 http 请求中实现 client_credentials 授权类型?

Posted

技术标签:

【中文标题】如何在角度 http 请求中实现 client_credentials 授权类型?【英文标题】:How to implement a client_credentials grant type in an angular http request? 【发布时间】:2017-04-24 18:48:59 【问题描述】:

我创建了一个 OAUTH2 授权服务器,它使用客户端凭据进行身份验证,并负责发布 JWT 令牌。当我使用邮递员发出请求时,我按预期获得了 JWT 令牌,但是当我将请求放入 Angular 时,我收到错误消息:“unsupported_grant_type”。

授权服务器的相关代码如下:

Startup.cs

public void Configuration(IAppBuilder app)
            
            // HTTP Configuration
            HttpConfiguration config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            ConfigureOAuth(app);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        

private void ConfigureOAuth(IAppBuilder app)

    OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
    
        //For Dev enviroment only (on production should be AllowInsecureHttp = false)
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/oauth2/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
        Provider = new CustomOAuthProvider(ActiveKernel),
        AccessTokenFormat = new CustomJwtFormat("http://localhost:62790", ActiveKernel)
    ;

    // OAuth 2.0 Bearer Access Token Generation
    app.UseOAuthAuthorizationServer(OAuthServerOptions);

CustomOAuthProvider.cs

public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        
            string clientId;
            string clientSecret;
            Guid clientIdGuid;        

            if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            
                context.TryGetFormCredentials(out clientId, out clientSecret);
            

            if (null == context.ClientId || null == clientSecret  || !Guid.TryParse(clientId, out clientIdGuid))
            
                context.SetError("invalid_credentials", "A valid client_Id and client_Secret must be provided");
                return Task.FromResult<object>(null);
            

            // change to async
            var client = _clientRepo.GetClient(clientIdGuid, clientSecret);

            if (client == null)
            
                context.SetError("invalid_credentials", "A valid client_Id and client_Secret must be provided");
                return Task.FromResult<object>(null);
            

            context.Validated();
            return Task.FromResult<object>(0);
        

        public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        
            var allowedOrigin = "*";

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[]  allowedOrigin );

            Guid clientId;
            Guid.TryParse(context.ClientId, out clientId);

            var client = _clientRepo.GetByClientId(clientId);

            if (client == null)
            
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            

            var identity = new ClaimsIdentity("JWT");
            identity.AddClaim(new Claim("fakeClaim", client.someField.ToString()));

            var props = new AuthenticationProperties(new Dictionary<string, string>
                
                    
                         "audience", (context.ClientId == null) ? string.Empty : context.ClientId
                    
                );

            var ticket = new AuthenticationTicket(identity, props);
            context.Validated(ticket);
        

当我在邮递员中使用授权类型 Oauth2 向 http://localhost:55555/oauth2/Token 发出请求时,我会返回一个有效的 JWT 令牌,我可以将其添加到我的请求中:

但是,当我尝试使用 Angular 获取访问令牌时,我收到一条错误消息,指示:“unsupported_grant_type”。我想要的grant_type 是client_credentials。我已经尝试了多种角度方法来让它工作但没有运气。这是 Angular 中的当前请求:

    function getLoginToken() 

        var auth = btoa("89C30F1E-DEE3-4C67-8P2E-9C974R5A35EA:B9wXE8Vo+FEkm2AnqFZlS+KJiwYc+bSnarpq90lGyBo=");

        var loginToken = $http(
            url: 'http://localhost:55555/oauth2/Token',
            method: "POST",
            data: 
                grant_type: 'client_credentials'
            ,
            withCredentials: true,
            headers: 
                "Authorization": "Basic " + auth,
'Content-Type': 'application/x-www-form-urlencoded'
            
        ).then(function(data) 
            return data;
        ).catch(function (errorResponse) 
            throw errorResponse;
        );
        return loginToken;
    ;

我尝试将 'Content-Type': 'application/x-www-form-urlencoded' 添加到标头并将grant_type 添加到 URL,但结果是相同的。如果我使用邮递员在不使用授权功能的情况下发出请求;但是使用 Body 'x-www-form-urlencoded' 我会收到与 angular 中相同的错误消息。

在 Angular 和第二个邮递员示例中,我实际上在 ValidateClientAuthentication 方法中得到了正确的 client_id 和 client_secret 并且上下文得到了验证。

这是两个请求的提琴手图像:

来自 Angular 的请求中断:

工作邮递员要求:

关于这里可能出了什么问题以及我如何能够解决问题的任何建议?

提前致谢!

【问题讨论】:

【参考方案1】:

为了更清楚,原始请求的最小值必须是:

内容类型:application/x-www-form-urlencoded

grant_type=password&username=USERNAME&password=PASSWORD

【讨论】:

【参考方案2】:

我通过添加以下代码行解决了这个问题:

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
            var payload = $.param( grant_type: 'client_credentials' );

grant_type 现在作为键出现,“client_credentials”是值。整个请求如下所示:

    function getLoginToken() 

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
            var payload = $.param( grant_type: 'client_credentials' );

        var auth = btoa("89C30F1E-DEE3-4C67-8P2E-9C974R5A35EA:B9wXE8Vo+FEkm2AnqFZlS+KJiwYc+bSnarpq90lGyBo=");

        var loginToken = $http(
            url: 'http://localhost:55555/oauth2/Token',
            method: "POST",
            data: payload,
            withCredentials: true,
            headers: 
                "Authorization": "Basic " + auth,
'Content-Type': 'application/x-www-form-urlencoded'
            
        ).then(function(data) 
            return data;
        ).catch(function (errorResponse) 
            throw errorResponse;
        );
        return loginToken;
    ;

我希望这可以帮助某人!

【讨论】:

让我问你为什么要为公共客户做这个流程? Angular 是公共客户端,在公共客户端场景中不应该使用客户端凭据。 不建议对 spa 应用使用客户端凭据授予类型。根据RFC 6749,它只能由机密客户使用。

以上是关于如何在角度 http 请求中实现 client_credentials 授权类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何在角度材料中实现 Sidenav、页眉和页脚?

我应该如何在角度材料中实现多项选择选项?

在vertx中实现长轮询

如何在 laravel 5.4 中实现 HTTPS

如何在android中实现请求超时?

如何在 grails 中实现请求超时?