muhammad waqas
muhammad waqas

Reputation: 740

Integrating IdentityServer4 With WebAPI

I am trying to integrate IdentityServer4 with ASP.NET MVC WebAPI.I want to achieve role based authorization. I have the following projects running.

  1. IdentityServer4 [a separate project]
  2. WebApi
  3. Javascript Application [using Extjs]

I have implemented the ResourceOwnerPassword flow and what I am trying to do is,

  1. Make a post request to AccountController of the WebApi containing username and password
  2. Inside the AccountController call the IdentityServer token endpoint for an access token and return the access token to the client (javascript app)
  3. Make a request to the WebApi containing the access token.

For the above part I am success full, here is the code

POSTMAN call for Login

enter image description here

AccountController

 [ActionName("Login")]
    [AllowAnonymous]
    [HttpPost]
    public async Task<BaseModel> Login(LoginModel model)
    {
        model.RememberMe = false;
        var status = await _security.Login(model.Email, model.Password, model.RememberMe);

        if (status.Status == LoginStatus.Succeded)
        {
            return new BaseModel { success = true, message = "login", data = status.Data };
        }
    }

SecurityService

  public async Task<LoginResponse> Login(string userName, string password, bool persistCookie = false)
    {
        // discover endpoints from metadata
        var disco = await DiscoveryClient.GetAsync("http://localhost:5000");

        // request token
        var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
        var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(userName, password, "api1");

        if (tokenResponse.IsError)
        {
            return new LoginResponse { Status = LoginStatus.Failed, Message = tokenResponse.Error };
        }

        return new LoginResponse { Status = LoginStatus.Succeded, Data = tokenResponse.Json };
    }

Security the API

I have two more actions inside the AccountController (just for testing) namely:

  1. values() [returns success and requires no authentication]
  2. SecureValues [returns success and requires authentication]

    [HttpGet]
    public BaseModel values()
    {
        return new BaseModel
        {
            success = true
        };
    }
    
    [Authorize]
    [HttpGet]
    public BaseModel SecureValues()
    {
        return new BaseModel
        {
            success = true
        };
    }
    

calling "Values" action returns the success which is quite obvious, calling the "SecureValues" gives following

enter image description here

Which means that the user is not Authenticated.

My IdentityServer4 configuration is as follows:

 public class Config
{
    // scopes define the resources in your system
    public static IEnumerable<Scope> GetScopes()
    {
        return new List<Scope>
        {
            StandardScopes.OpenId,
            StandardScopes.Profile,

            new Scope
            {
                Name = "api1",
                Description = "My API",
                DisplayName = "API Access",
                Type = ScopeType.Resource,
                IncludeAllClaimsForUser = true,
                Claims = new List<ScopeClaim>
                {
                    new ScopeClaim(ClaimTypes.Name),
                    new ScopeClaim(ClaimTypes.Role)
                }
            },
           new Scope
           {
               Enabled = true,
               Name  = "role",
               DisplayName = "Role(s)",
               Description = "roles of user",
               Type = ScopeType.Identity,
               Claims = new List<ScopeClaim>
               {
                   new ScopeClaim(ClaimTypes.Role,false)
               }
           },
           StandardScopes.AllClaims
        };
    }

    // clients want to access resources (aka scopes)
    public static IEnumerable<Client> GetClients()
    {
        // client credentials client
        return new List<Client>
        {
            new Client
            {
                ClientId = "client",
                AllowedGrantTypes = GrantTypes.ClientCredentials,

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
                AllowedScopes = { "api1" }
            },

            // resource owner password grant client
            new Client
            {
                ClientId = "ro.client",
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
                AllowedScopes = { "api1" }
            },

            // OpenID Connect implicit flow client (MVC)
            new Client
            {
                ClientId = "mvc",
                ClientName = "MVC Client",
                AllowedGrantTypes = GrantTypes.Implicit,

                //flow

                RedirectUris = { "http://localhost:5002/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5002" },

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    "role"
                }
            },
            //for hybrid flow
            new Client
            {
                ClientId = "mvchybrid",
                ClientName ="mvc hybrid client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris = {"http://localhost:5003/signin-oidc"},
                PostLogoutRedirectUris = {"http://localhost:5003"},

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    StandardScopes.OfflineAccess.Name,
                    "api1"
                }
            },
            new Client
            {
                ClientId = "js",
                ClientName = "javascript client",
                AllowedGrantTypes = GrantTypes.Implicit,
                AllowAccessTokensViaBrowser= true,
                RedirectUris = {"http://localhost:5004/callback.html"},
                PostLogoutRedirectUris = {"http://localhost:5004/index.html"},
                AllowedCorsOrigins = {"http://localhost:5004"},

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    "api1",
                    "role",
                    StandardScopes.AllClaims.Name
                }
            },
            //aspnet identity client
            new Client
            {
                ClientId = "mvcIdentity",
                ClientName = "Mvc Identity Client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                RequireConsent = false,
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
                RedirectUris = {"http://localhost:5005/signin-oidc"},
                PostLogoutRedirectUris = {"http://localhost:5005"},
                AllowedScopes =
                {
                     StandardScopes.OpenId.Name,
                     StandardScopes.Profile.Name,
                     StandardScopes.OfflineAccess.Name,
                     "api1"
                }
            }
        };
    }

    public static List<InMemoryUser> GetUsers()
    {
        return new List<InMemoryUser>
        {
            new InMemoryUser
            {
                Subject = "1",
                Username = "[email protected]",
                Password = "password",

                Claims = new List<Claim>
                {
                    new Claim("name", "Alice"),
                    new Claim("website", "https://alice.com"),
                    new Claim(ClaimTypes.Role,"FreeUser")
                }
            },
            new InMemoryUser
            {
                Subject = "2",
                Username = "[email protected]",
                Password = "password",

                Claims = new List<Claim>
                {
                    new Claim("name", "Bob"),
                    new Claim("website", "https://bob.com"),
                    new Claim(ClaimTypes.Role,"PaidUser")
                }
            }
        };
    }
}

WebApi configuration

 public void ConfigureAuth(IAppBuilder app)
    {

        app.UseIdentityServerBearerTokenAuthentication(new IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = "localhost:5000",
            RequiredScopes = new[] { "api1" },
            ClientId = "ro.client",
            ClientSecret = "secret",
            PreserveAccessToken = true
        });
       }

Upvotes: 1

Views: 1986

Answers (1)

Lutando
Lutando

Reputation: 5010

It looks like your Authority in your UseIdentityServerBearerTokenAuthentication middleware is wrong. I think it should be "http://localhost:5000".

Also enabling logging (with trace) to console you can see why your authorization was challenged sometimes.

Upvotes: 1

Related Questions