从 OWIN Cookie 获取不记名令牌并将其放在 API 请求中

Posted

技术标签:

【中文标题】从 OWIN Cookie 获取不记名令牌并将其放在 API 请求中【英文标题】:Get bearer token from OWIN Cookie and put it on API Requests 【发布时间】:2015-10-11 13:49:47 【问题描述】:

这是我的场景: 我有一个 MVC4.5/WebApi2 应用程序,它使用基于 Thinktecture.IdentityServer 提供程序的 OpenIdConnectAuthentication。到目前为止,我可以针对 MVC 进行身份验证。现在我想使用 Bearer Token 对 WebApi 进行身份验证。这是我的配置

app.UseWebApi(ConfigureAPI());
app.UseCookieAuthentication(new CookieAuthenticationOptions() 
        AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
        CookieSecure = CookieSecureOption.Always,
        AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
        CookieHttpOnly = true
    );

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions() 
                EnableValidationResultCache = false,
                Authority = WebConfigurationManager.AppSettings["Authority"],
                AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive
            );

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions() 
                Authority = WebConfigurationManager.AppSettings["Authority"],
                ClientId = WebConfigurationManager.AppSettings["ClientId"],
                ClientSecret = WebConfigurationManager.AppSettings["ClientSecret"],
                ResponseType = "code id_token",
                Scope = "openid email profile", 
                SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
                Notifications = new OpenIdConnectAuthenticationNotifications 
                    AuthenticationFailed = OnAuthenticationFailed,
                    AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                    RedirectToIdentityProvider = OnRedirectToIdentityProvider
                
            ;
);

还有我的 WebApi 配置

public HttpConfiguration ConfigureAPI() 
            var httpConfig = new HttpConfiguration();
            // Configure Web API to use only bearer token authentication.
            httpConfig.SuppressDefaultHostAuthentication();
            httpConfig.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));    
            httpConfig.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

            // Web API routes
            httpConfig.MapHttpAttributeRoutes();

            httpConfig.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/controller/id",
                 defaults: new  id = RouteParameter.Optional 
            );
            return httpConfig;
        

由于我的 OWIN Cookie 中已经有访问令牌,我想在授权标头到达 API 之前将其添加到授权标头中,从而获得成功的身份验证。

这是我尝试过的

public class CustomAuthorizeAttribute : AuthorizeAttribute 
        protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext) 
            var cookies = actionContext.Request.Headers.GetCookies(".AspNet.Cookies");
            var cookie = cookies.First().Cookies.FirstOrDefault(c => c.Name == ".AspNet.Cookies");
            if (cookie != null)                
                var unprotectedTicket = Startup.OAuthOptions.TicketDataFormat.Unprotect(ticket);
                actionContext.Request.Headers.Add("Authorization", string.Format("Bearer 0", unprotectedTicket.Identity.Claims.First(c => c.Type == "access_token").Value));                  
            
            return base.IsAuthorized(actionContext);
        
    

我什至尝试使用放在app.UseWebApi(ConfigureAPI()); 之后的 OWIN 中间件

public class UseCookieToBearerAuthentication : OwinMiddleware 
        public UseCookieToBearerAuthentication(OwinMiddleware next) : base(next)  

        public async override Task Invoke(IOwinContext context) 
            //TODO Retrieve cookie name from somewhere like in FormsAuthentication.FormsCookieName          
            var cookies = context.Request.Cookies;
            var cookie = cookies.FirstOrDefault(c => c.Key == ".AspNet.Cookies");
            if (!cookie.Equals(default(KeyValuePair<string, string>))) 
                var ticket = cookie.Value;
                var unprotectedTicket = Startup.OAuthOptions.TicketDataFormat.Unprotect(ticket);
                context.Request.Headers.Add("Authorization", new string[]
                    string.Format("Bearer 0", unprotectedTicket.Identity.Claims.First(c => c.Type == "access_token").Value)
                );
            
            await Next.Invoke(context);
        
    

那么,如何根据我的 Owin Cookie 中的访问令牌为我的 web api 实现令牌身份验证?

提前致谢。

【问题讨论】:

【参考方案1】:

问题在于 IdentityServerBearerTokenAuthenticationOptions 默认使用 AuthenticationMode = ValidationMode.ValidationEndpoint;,而默认使用 Microsoft.Owin.Security.AuthenticationMode.Active 并且不能被覆盖。

所以我将 IdentityServerBearerTokenAuthenticationOptions 设置为 ValidationMode = ValidationMode.Local;AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive;,这很好,因为访问令牌是 JWT(自包含)。

我还使用 OWIN 中间件从请求上的 cookie 中获取访问令牌,并将其设置在自动化标头上。

public class UseCookieToBearerAuthentication : OwinMiddleware 
        public UseCookieToBearerAuthentication(OwinMiddleware next) : base(next)  



    public async override Task Invoke(IOwinContext context) 
                var x = Startup.OAuthOptions.CookieName;
                var cookieName = string.Format("01", CookieAuthenticationDefaults.CookiePrefix, CookieAuthenticationDefaults.AuthenticationType);
                var cookies = context.Request.Cookies;
                var cookie = cookies.FirstOrDefault(c => c.Key == ".AspNet.Cookies");
                if (!cookie.Equals(default(KeyValuePair<string, string>))) 
                    var ticket = cookie.Value;
                    var unprotectedTicket = Startup.OAuthOptions.TicketDataFormat.Unprotect(ticket);
                    context.Request.Headers.Add("Authorization", new string[]
                        string.Format("Bearer 0", unprotectedTicket.Identity.Claims.First(c => c.Type == "access_token").Value)
                    );
                
                await Next.Invoke(context);
            
        

【讨论】:

我们实际上希望摆脱用于 Web API 的 cookie - 这是一种反模式并且容易受到 CSRF 攻击。如果您绝对想使用 cookie,请使用 Web API 中的 cookie 中间件 - 否则不要使用 cookie 并通过显式身份验证正确执行 - leastprivilege.com/2015/04/01/…

以上是关于从 OWIN Cookie 获取不记名令牌并将其放在 API 请求中的主要内容,如果未能解决你的问题,请参考以下文章

Owin Web API Bearer Token 直通

实施 Identity 2.1 + OWIN OAuth JWT 不记名令牌时如何从 Web API 控制器端点进行身份验证

Symfony 在哪里存储和获取不记名令牌并做出反应?

一起使用不记名令牌和 cookie 身份验证

JWT 不记名令牌流

确定不记名令牌是不是已过期或刚刚被授权