OWIN OpenID connect authorization fails to authorize secured controller / actions

11,144

TL;DR: use ResponseType = "id_token token" and it should work.

In OpenID Connect, response_type=token is not considered as a legal value: http://openid.net/specs/openid-connect-core-1_0.html#Authentication.

Sometimes implemented for backward compatibility reasons, response_type=token is not supported by the OIDC middleware developed by MSFT: an exception is always thrown when no id_token is returned by the OpenID Connect provider (which also excludes the valid code flow). You can find more information on this other SO post.

(remark: in SecurityTokenValidated, you're replacing the ticket created by the OIDC middleware using n.AuthenticationTicket = new AuthenticationTicket(...): it's not the recommended approach and will result in a ClaimsIdentity missing the essential claims. You should consider removing the assignation and simply add new claims like you do for the access_token claim)

Share:
11,144
TejSoft
Author by

TejSoft

Updated on July 20, 2022

Comments

  • TejSoft
    TejSoft almost 2 years

    I am working on a project where a third party provider will act as an Oauth2 based Authorization Server. An Asp.net MVC 5 based client which will send the user to the authorization server to authenticate (using login / password) and the auth server will return an access token back to the MVC client. Any further calls to resource servers (APIs) will be made using the access token.

    To achieve this I am using Microsoft.Owin.Security.OpenIdConnect and the UseOpenIdConnectAuthentication extension. I am able to successfully redirect and get the access token from the auth server but the client is not creating an Authentication Cookie. Every time I try to access a secured page, I get the callback page with access token.

    What am I missing here? My current code is below.

    The secured controller action:

    namespace MvcWebApp.Controllers
    {    
        public class SecuredController : Controller
        {
            // GET: Secured
            [Authorize]
            public ActionResult Index()
            {
                return View((User as ClaimsPrincipal).Claims);
            }
        }
    }
    

    The Startup Class:

    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)
            });
    
            // ***************************************************************************
            // Approach 1 : ResponseType = "id_token token"
            // ***************************************************************************
            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
                SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
                Authority = "https://thirdparty.com.au/oauth2",
                ClientId = "_Th4GVMa0JSrJ8RKcZrzbcexk5ca",
                ClientSecret = "a3GVJJbLHkrn9nJRj3IGNvk5eGQa",
                RedirectUri = "http://mvcwebapp.local/",
                ResponseType = "id_token token",
                Scope = "openid",
    
                Configuration = new OpenIdConnectConfiguration
                {
                    AuthorizationEndpoint = "https://thirdparty.com.au/oauth2/authorize",
                    TokenEndpoint = "https://thirdparty.com.au/oauth2/token",
                    UserInfoEndpoint = "https://thirdparty.com.au/oauth2/userinfo",
                },
    
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = n =>
                    {
                        var token = n.ProtocolMessage.AccessToken;
    
                        // persist access token in cookie
                        if (!string.IsNullOrEmpty(token))
                        {
                            n.AuthenticationTicket.Identity.AddClaim(
                                new Claim("access_token", token));
                        }
    
                        return Task.FromResult(0);
                    },
    
                    AuthenticationFailed = notification =>
                    {
                        if (string.Equals(notification.ProtocolMessage.Error, "access_denied", StringComparison.Ordinal))
                        {
                            notification.HandleResponse();
    
                            notification.Response.Redirect("/");
                        }
    
                        return Task.FromResult<object>(null);
                    }
                }
            });
    
            // ***************************************************************************
            // Approach 2 : ResponseType = "code"
            // ***************************************************************************
            //app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            //{
            //    AuthenticationMode = AuthenticationMode.Active,
            //    AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
            //    SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
            //    Authority = "https://thirdparty.com.au/oauth2",
            //    ClientId = "_Th4GVMa0JSrJ8RKcZrzbcexk5ca",
            //    ClientSecret = "a3GVJJbLHkrn9nJRj3IGNvk5eGQa",
            //    RedirectUri = "http://mvcwebapp.local/",
            //    ResponseType = "code",
            //    Scope = "openid",
    
            //    Configuration = new OpenIdConnectConfiguration
            //    {
            //        AuthorizationEndpoint = "https://thirdparty.com.au/oauth2/authorize",
            //        TokenEndpoint = "https://thirdparty.com.au/oauth2/token",
            //        UserInfoEndpoint = "https://thirdparty.com.au/oauth2/userinfo",
            //    },
    
            //    Notifications = new OpenIdConnectAuthenticationNotifications
            //    {
            //        AuthorizationCodeReceived = async (notification) =>
            //        {
            //            using (var client = new HttpClient())
            //            {
            //                var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);                                        
            //                var request = new HttpRequestMessage(HttpMethod.Get, configuration.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.ResponseType, "token"},
            //                    {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)));
            //            }
            //        }
            //    }
    
            //});
    
        }
    }             
    
  • TejSoft
    TejSoft over 8 years
    I tried response_type = "id_token token", but it looks like the identity server I am using (WSO2 5.0.0) does support it. I will have to wait till the next version comes out for full openid connect features. When I changed the response response_type = "code" it completes the first part of the auth process and returns with a code in the URL but the next request to get the access token doesn't happen and no identity created in the OWIN pipeline.
  • Kévin Chalet
    Kévin Chalet over 8 years
    Indeed, it doesn't seem to be supported: stackoverflow.com/a/29140396/542757. Sadly, you can't use the code flow, as the OIDC middleware doesn't support it (it's a shame and I said it multiple times to the ASP.NET team). Would migrating to ASP.NET 5 be an option?
  • TejSoft
    TejSoft over 8 years
    Is moving to ASP.net 5 going to be helpful if WSO2 is not ready yet? The id server will support multiple sites, some existing and some new (being built). The new ones can use ASP.net 5 but old ones are on 4.5. I might wait for few more weeks as WSO2 will release the next version soon. I have updated the question with the two approaches I made.
  • Kévin Chalet
    Kévin Chalet over 8 years
    "Is moving to ASP.net 5 going to be helpful if WSO2 is not ready yet?": the new OIDC middleware fully supports the authorization code flow (which is supposed to be available in WSO2), so yes.
  • user1754675
    user1754675 over 8 years
    I have a similar issue and using WSO2 5.1.0 stackoverflow.com/questions/34773419 and added a breakpoint in SecurityTokenValidated but it did not hit on debug. Please advise?