如何在 Asp.net MVC 中编写 OAuth2 Web API 客户端

Posted

技术标签:

【中文标题】如何在 Asp.net MVC 中编写 OAuth2 Web API 客户端【英文标题】:How to write OAuth2 Web API Client in Asp.net MVC 【发布时间】:2015-10-01 07:27:56 【问题描述】:

我们开发了一组受授权服务器保护的 Web API (REST)。授权服务器已发布客户端 ID 和客户端密码。这些可用于获取访问令牌。可以在对资源服务器 (REST API) 的后续调用中使用有效的令牌。

我想编写一个基于 Web (Asp.net MVC 5) 的客户端来使用 API。是否有可以下载的 nuget 包来帮助我实现客户端 OAuth2 流程?谁能指导我一个关于 OAuth2 流的客户端实现的好例子(用 asp.net MVC 编写)?

更新 我能够使用下面的代码块获取访问令牌,但我想要的是“客户端凭据”oauth 2 流程,我不必输入登录名和密码。我现在的代码是:

public class Startup

    public void Configuration(IAppBuilder app)
    
        app.SetDefaultSignInAsAuthenticationType("ClientCookie");

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        
            AuthenticationMode = AuthenticationMode.Active,
            AuthenticationType = "ClientCookie",
            CookieName = CookieAuthenticationDefaults.CookiePrefix + "ClientCookie",
            ExpireTimeSpan = TimeSpan.FromMinutes(5)
        );

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        
            AuthenticationMode = AuthenticationMode.Active,
            AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,                
            SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
            ClientId = ConfigurationManager.AppSettings["AuthServer:ClientId"],
            ClientSecret = ConfigurationManager.AppSettings["AuthServer:ClientSecret"],
            RedirectUri = ConfigurationManager.AppSettings["AuthServer:RedirectUrl"],
            Configuration = new OpenIdConnectConfiguration
            
                AuthorizationEndpoint = "https://identityserver.com/oauth2/authorize",
                TokenEndpoint = "https://identityserver.com/oauth2/token"                                        
            ,

            //ResponseType = "client_credentials", // Doesn't work
            ResponseType = "token",

            Notifications = new OpenIdConnectAuthenticationNotifications
            
                AuthenticationFailed = notification =>
                
                    if (string.Equals(notification.ProtocolMessage.Error, "access_denied", StringComparison.Ordinal))
                    
                        notification.HandleResponse();

                        notification.Response.Redirect("/");
                    

                    return Task.FromResult<object>(null);
                ,

                AuthorizationCodeReceived = async notification =>
                
                    using (var client = new HttpClient())
                    
                        //var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);
                        String tokenEndPoint = "https://identityserver.com/oauth2/token";

                        //var request = new HttpRequestMessage(HttpMethod.Post, configuration.TokenEndpoint);
                        var request = new HttpRequestMessage(HttpMethod.Post, tokenEndPoint);
                        request.Content = new FormUrlEncodedContent(new Dictionary<string, string> 
                             OpenIdConnectParameterNames.ClientId, notification.Options.ClientId ,
                             OpenIdConnectParameterNames.ClientSecret, notification.Options.ClientSecret ,
                             OpenIdConnectParameterNames.Code, notification.ProtocolMessage.Code ,
                             OpenIdConnectParameterNames.GrantType, "authorization_code" ,
                             OpenIdConnectParameterNames.RedirectUri, notification.Options.RedirectUri 
                        );

                        var response = await client.SendAsync(request, notification.Request.CallCancelled);
                        response.EnsureSuccessStatusCode();

                        var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

                        // Add the access token to the returned ClaimsIdentity to make it easier to retrieve.
                        notification.AuthenticationTicket.Identity.AddClaim(new Claim(
                            type: OpenIdConnectParameterNames.AccessToken,
                            value: payload.Value<string>(OpenIdConnectParameterNames.AccessToken)));
                    
                
            
        );


    

【问题讨论】:

【参考方案1】:

要支持客户端凭据授予类型,您最好的选择可能是直接使用HttpClient

var request = new HttpRequestMessage(HttpMethod.Post, "http://server.com/token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string> 
     "client_id", "your client_id" ,
     "client_secret", "your client_secret" ,
     "grant_type", "client_credentials" 
);

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();

var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var token = payload.Value<string>("access_token");

对于交互式流程(如授权代码流程),有两种更好的方法:

如果您的授权服务器支持 OpenID Connect(基于 OAuth2),您可以简单地使用微软为 OWIN/Katana 3 开发的 OpenID Connect 中间件:https://www.nuget.org/packages/Microsoft.Owin.Security.OpenIdConnect/

如果您的授权服务器不支持 OpenID Connect,则一种选择是创建您自己的 OAuth2 客户端中间件。您可以查看此 SO 答案的最后一部分以获取更多信息:Registering Web API 2 external logins from multiple API clients with OWIN Identity

【讨论】:

我已经取得了一些进展,并且能够从身份服务器取回令牌。但我想要的是一个“客户端凭据”流程,我不需要输入任何登录名和密码。 Token 应该根据 Client Id 和 Client Secret 发布。不知何故,我无法配置它。我已经用我当前的代码更新了这个问题。 恐怕这是一个不同的问题。您最初的问题虽然使用了不精确的术语,但表明您正在使用授权代码流:“这些可用于获取授权密钥,而授权密钥又可用于获取访问令牌”。为 Katana 3 开发的 OIDC 中间件不支持非交互式流程,例如资源所有者密码凭证流程或客户端凭证流程。您必须直接使用HttpClientgrant_type=client_credentials。您使用的是哪个授权服务器? 是的,当我开始实施它时,事情变得更加清晰。我正在使用一个名为 WS02 的基于 Java 的身份服务器。如果 Katana 3 不能用于“客户端凭据”,您是否推荐任何其他 nuget 包?有什么例子吗? IdentityServer3 这太棒了,可以实现您需要的所有流程 Postman 有更多参数用于 GET NEW ACCESS TOKEN 1) Token Name 2) grant_type = client_credentials , authorization_code, implicit, password credentials 3) Access Token Url 4) Client Id 5) Client Secret 6) Scope: read:org 7) Client Authentication: Send client credentials in body, Send as Basic Auth Header

以上是关于如何在 Asp.net MVC 中编写 OAuth2 Web API 客户端的主要内容,如果未能解决你的问题,请参考以下文章

如何在 asp.net mvc4 中将 OpenID 迁移到 OAuth

如何在 ASP.NET MVC 5 和 WEB API 2 中实现 oauth2 服务器 [关闭]

处理 OAuth 2.0 身份验证 - 在 ASP.NET MVC 应用程序中获取令牌重定向令牌响应

如何使用 asp.net mvc 从 azure 获取访问令牌

从 ASP.NET MVC 5 中的 Facebook v2.4 API 访问 OAuth ExternalLoginCallback 中的电子邮件地址 [重复]

使用 ASP.NET MVC,如何最好地避免同时编写添加视图和编辑视图?