Получите токен-носитель из OWIN Cookie и поместите его в запросы API

Вот мой сценарий: у меня есть приложение MVC4.5 / WebApi2, которое использует OpenIdConnectAuthentication на основе поставщика Thinktecture.IdentityServer. Пока я могу пройти аутентификацию по MVC. Теперь я хочу пройти аутентификацию в 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);
        }
    }

Я даже пытаюсь установить промежуточное ПО OWIN после app.UseWebApi(ConfigureAPI());

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);
        }
    }

Итак, как я могу добиться аутентификации токена для моего веб-API на основе токена доступа в моем Owin Cookie ?.

Заранее спасибо.


person Kautsky Lozano    schedule 21.07.2015    source источник


Ответы (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("{0}{1}", 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);
            }
        }
person Kautsky Lozano    schedule 22.07.2015
comment
На самом деле мы хотим уйти от файлов cookie для веб-API - это анти-шаблон и уязвимо для атак CSRF. Если вы абсолютно хотите использовать cookie, используйте промежуточное программное обеспечение cookie в веб-API - в противном случае не используйте файлы cookie и сделайте это правильно с помощью явной аутентификации - lessprivilege.com/2015/04/01/ - person leastprivilege; 23.07.2015