Search code examples
c#asp.net-mvc-4oauth-2.0openidowin

Use Bearer Token Authentication for API and OpenId authentication for MVC on the same application project


I am trying to use both OpenId and Bearer token authentication on my application through Identity Server.

The problem currently is that once I have authenticated the user, I still need to get a bearer token to be able to call any action methods for my Asp.Net MVC application.

Here is my startup file for the application

 public class Startup
{
     public void Configuration(IAppBuilder app)
     {            
         AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
         JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
         app.UseCookieAuthentication(new CookieAuthenticationOptions
         {
            AuthenticationType = "Cookies"
         });

         app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
         {
            Authority = "https://localhost:44301/identity",
            ClientId = "baseballStats",
            Scope = "openid profile roles baseballStatsApi",
            RedirectUri = "https://localhost:44300/",
            ResponseType = "id_token token",
            SignInAsAuthenticationType = "Cookies",
            UseTokenLifetime = false,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                SecurityTokenValidated = async n =>
                {
                    var userInfoClient = new UserInfoClient(
                                 new Uri(n.Options.Authority + "/connect/userinfo"),
                                 n.ProtocolMessage.AccessToken);

                    var userInfo = await userInfoClient.GetAsync();

                    // create new identity and set name and role claim type
                    var nid = new ClaimsIdentity(
                       n.AuthenticationTicket.Identity.AuthenticationType,
                        Constants.ClaimTypes.GivenName,
                        Constants.ClaimTypes.Role);

                    userInfo.Claims.ToList().ForEach(c => nid.AddClaim(new Claim(c.Item1, c.Item2)));

                    // keep the id_token for logout
                    nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

                    // add access token for sample API
                    nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));

                    // keep track of access token expiration
                    nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

                    // add some other app specific claim
                    nid.AddClaim(new Claim("app_specific", "some data"));

                    n.AuthenticationTicket = new AuthenticationTicket(
                        nid,
                        n.AuthenticationTicket.Properties);
                }
            }
         });



         app.UseResourceAuthorization(new AuthorizationManager());

         app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
         {
             Authority = "https://localhost:44301/identity",
             RequiredScopes = new[] { "baseballStatsApi" }
         });

         var config = new HttpConfiguration();
         config.MapHttpAttributeRoutes();
         app.UseWebApi(config);           
     }
}

I would like to restrict bearer token authentication to my api urls only, and use openID auth for everthing else. Is there a way to do that?


Solution

  • Ok, I found some information on the following post

    https://github.com/IdentityServer/IdentityServer3/issues/487

    The github repo that implements the concepts discussed in the link can be found here

    https://github.com/B3nCr/IdentityServer-Sample/blob/master/B3nCr.Communication/Startup.cs

    Basically you need to map the api url to a different configuration using app.Map(). In my case, I changed my startup file to look like this.

     public class Startup
    {
         public void Configuration(IAppBuilder app)
         {
             AntiForgeryConfig.UniqueClaimTypeIdentifier = Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Subject;
             JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
    
             app.UseCookieAuthentication(new CookieAuthenticationOptions
             {
                 AuthenticationType = "Cookies"
             });
    
             var openIdConfig = new OpenIdConnectAuthenticationOptions
             {
                 Authority = "https://localhost:44301/identity",
                 ClientId = "baseballStats",
                 Scope = "openid profile roles baseballStatsApi",
                 RedirectUri = "https://localhost:44300/",
                 ResponseType = "id_token token",
                 SignInAsAuthenticationType = "Cookies",                 
                 UseTokenLifetime = false,
                 Notifications = new OpenIdConnectAuthenticationNotifications
                 {
                     SecurityTokenValidated = async n =>
                     {
                         var userInfoClient = new UserInfoClient(
                                      new Uri(n.Options.Authority + "/connect/userinfo"),
                                      n.ProtocolMessage.AccessToken);
    
                         var userInfo = await userInfoClient.GetAsync();
    
                         // create new identity and set name and role claim type
                         var nid = new ClaimsIdentity(
                            n.AuthenticationTicket.Identity.AuthenticationType,
                             Thinktecture.IdentityServer.Core.Constants.ClaimTypes.GivenName,
                             Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Role);
    
                         userInfo.Claims.ToList().ForEach(c => nid.AddClaim(new Claim(c.Item1, c.Item2)));
    
                         // keep the id_token for logout
                         nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
    
                         // add access token for sample API
                         nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
    
                         // keep track of access token expiration
                         nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
    
                         // add some other app specific claim
                         nid.AddClaim(new Claim("app_specific", "some data"));
    
                         n.AuthenticationTicket = new AuthenticationTicket(
                             nid,
                             n.AuthenticationTicket.Properties);
    
                         n.Request.Headers.SetValues("Authorization ", new string[] { "Bearer ", n.ProtocolMessage.AccessToken });
                     }
                 }
             };
    
             app.UseOpenIdConnectAuthentication(openIdConfig);
    
             app.UseResourceAuthorization(new AuthorizationManager());
    
             app.Map("/api", inner =>
             {
                 var bearerTokenOptions = new IdentityServerBearerTokenAuthenticationOptions
                 {
                     Authority = "https://localhost:44301/identity",
                     RequiredScopes = new[] { "baseballStatsApi" }
                 };
    
                 inner.UseIdentityServerBearerTokenAuthentication(bearerTokenOptions);
                 var config = new HttpConfiguration();
                 config.MapHttpAttributeRoutes();
                 inner.UseWebApi(config);
             });                                                 
         }
    }
    

    That solved my problem. I can now access the MVC pages with cookies based authentication, and call the API with bearer token authentication.