14

I am working on an asp.net MVC application with identity server 4 as token service. I have an api as well which has some secure resources. I want to implement roles (Authorization) for api. I want to make sure that only an authorized resource with valid role can access an api end point otherwise get 401 (unauthorized error).

Here are my configurations:

Client

         new Client()
            {
                ClientId = "mvcClient",
                ClientName = "MVC Client",                    
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                ClientSecrets = new List<Secret>()
                {
                    new Secret("secret".Sha256())
                },

                RequireConsent = false;

                // where to redirect to after login
                RedirectUris = { "http://localhost:5002/signin-oidc" },
                // where to redirect to after logout
                PostLogoutRedirectUris = { "http://localhost:5002" },

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    StandardScopes.OfflineAccess.Name,
                    StandardScopes.Roles.Name,
                    "API"
                }
            }

Scopes

 return new List<Scope>()
            {
                StandardScopes.OpenId, // subject id
                StandardScopes.Profile, // first name, last name
                StandardScopes.OfflineAccess,  // requesting refresh tokens for long lived API access
               StandardScopes.Roles,
                new Scope()
                {
                    Name = "API",
                    Description = "API desc",
                     Type = ScopeType.Resource,
                    Emphasize = true,
                    IncludeAllClaimsForUser = true,
                    Claims = new List<ScopeClaim>
                    {
                        new ScopeClaim(ClaimTypes.Name),      
                        new ScopeClaim(ClaimTypes.Role)
                    }
                }
            };

User

new InMemoryUser()
                {
                    Subject = "1",
                    Username = "testuser",
                    Password = "password",
                    Claims = new List<Claim>()
                    {
                        new Claim("name", "Alice"),
                        new Claim("Website", "http://alice.com"),
                         new Claim(JwtClaimTypes.Role, "admin")

                    }
                }

and in server startup i added this:

services.AddIdentityServer() .AddTemporarySigningCredential() .AddSigningCredential(cert) .AddInMemoryClients(Config.GetClients()) .AddInMemoryScopes(Config.GetScopes()) .AddInMemoryUsers(Config.GetUsers())

in api startup, i have this:

  app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions()
            {
                Authority = "http://localhost:5000",
                ScopeName = "NamfusAPI",
                RequireHttpsMetadata = false
            });

in api controller, i have this:

 [Authorize(Roles = "admin")]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new {c.Type, c.Value });
        }

in MVC client startup, i have this:

 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

 app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationScheme = "Cookies"
            });


            var oidcOptions = new OpenIdConnectOptions()
            {
                AuthenticationScheme = "oidc",
                SignInScheme = "Cookies",

                Authority = "http://localhost:5000",
                RequireHttpsMetadata = false,

                ClientId = "mvcClient",
                ClientSecret = "secret",
                SaveTokens = true,
                GetClaimsFromUserInfoEndpoint = true,
                ResponseType = "code id_token", // hybrid flow

            };


            oidcOptions.Scope.Clear();
            oidcOptions.Scope.Add("openid");
            oidcOptions.Scope.Add("profile");
            oidcOptions.Scope.Add("NamfusAPI");
            oidcOptions.Scope.Add("offline_access");
            oidcOptions.Scope.Add("roles");

I am trying to call the api like this:

public async Task<IActionResult> CallApiUsingUserAccessToken()
        {
            var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");

            var client = new HttpClient();
            client.SetBearerToken(accessToken);
            var content = await client.GetStringAsync("http://localhost:5001/identity");

            ViewBag.Json = JArray.Parse(content).ToString();
            return View("json");
        } 

I get access token but when call is made to api (identity/get), I get 302 error Forbidden (in chrome network it shows 500 internal server error). If I change API Authorize attribute from

  [Authorize(Roles = "admin")]
            public IActionResult Get()

to (without role):

 [Authorize]
        public IActionResult Get()

it works and I get data from api in mvc app. How can I apply roles in this code.

Please suggest.

Mohammad Olfatmiri
  • 1,605
  • 5
  • 31
  • 57
Asif Hameed
  • 1,353
  • 9
  • 25
  • 46

1 Answers1

8

First, you need to request "API" scope in your OpenIdConnectOptions().

oidcOptions.Scope.Add("API");

or

Scope = { "API", "offline_access",..},

Then you need to check if the role claim is included in the claims list available to your API controler(don't apply the roles filter in authorize attribute yet. Put a debug point inside controller method and expand User property). Check if the type of the role claim you received(listed in Claims Collection) matches User.Identity.RoleClaimType property

enter image description here

If the role claim type you have and User.Identity.RoleClaimType doesn't match, authorize attribute with roles filter won't work. You can set the correct RoleClaimType in IdentityServerAuthenticationOptions() like follows

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        {
            Authority = "http://localhost:5000",
            ScopeName = "API",
            RoleClaimType = ClaimTypes.Role,
            RequireHttpsMetadata = false
        });
rawel
  • 2,923
  • 21
  • 33
  • rawel thanks for the response. I see RoleClaimType is not available when i try RoleClaimType = ClaimTypes.Role, in mvc client like this `code`var oidcOptions = new OpenIdConnectOptions() { AuthenticationScheme = "oidc", SignInScheme = "Cookies", Authority = "http://localhost:5000", RequireHttpsMetadata = false, GetClaimsFromUserInfoEndpoint = true, RoleClaimType = ClaimTypes.Role, }; `code` – Asif Hameed Nov 24 '16 at 05:13
  • This should be added to Api. To IdentityServerAuthenticationOptions – rawel Nov 24 '16 at 05:42
  • i see that User.Claims property in controller is null where as RoleCalimtype in identity has same value shown above. – Asif Hameed Nov 24 '16 at 07:29
  • Sorry, I have no idea how Claims collection can be null. If claims are not available, authorize filter will not work. Check your configuration in the API. Or start with a clean working sample from Identity Server repository. – rawel Nov 24 '16 at 08:06
  • its not showing users claims which i created like name etc which i configured for in memory user. – Asif Hameed Nov 24 '16 at 08:09
  • It should show role claim you have configured in identity server. If not check if you are referring the same scope in 4 places(scope definition, client definition, mvc config, api config). In your question, it looks like you're using 2 scopes "API" and "NamfusAPI". – rawel Nov 24 '16 at 08:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/128916/discussion-between-asif-hameed-and-rawel). – Asif Hameed Nov 24 '16 at 08:17
  • How can I use asp.net web form client in above case? I tried identity server 3 client but it is doing everything in startup file. Not sure how to get token from identity server and then use this token to call web form which is not in asp.net core. I posted at http://stackoverflow.com/questions/40792984/asp-net-web-form-client-with-identity-server-4 – Asif Hameed Nov 24 '16 at 18:55
  • @rewel I am facing one issue, I noticed that when role:"admin" is added in JWT token, then this causes problem. how to parse/validate jwt token with admin role? – Asif Hameed Dec 14 '16 at 18:50