Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to setup IdentityServer4 Cookies for login, JWT token for API authorization

We have one application that uses an IdentityServer4 cookies authorization scheme for user login like this:

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = <local IDP server with IdentityServer4>;
            options.ClientId = <ClientId>;
            options.ClientSecret = <secret>
            options.ResponseType = "code id_token";
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("offline_access");
        })

The client on the IDP looks like this:

new Client
{
    ClientId = <ClientID>,
    ClientName = <ClientName>,
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    RequireConsent = true,
    ClientSecrets = { new Secret(<secret>.Sha256()) },
    AllowOfflineAccess = true,
    RedirectUris = { "http://" + ip + "/signin-oidc" },
    PostLogoutRedirectUris = { "http://" + ip + "/signout-callback-oidc" },
    AllowedScopes = {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile
        }
};

We also have a WebAPI located at /api/...

The goal is to add JWT Bearer Token authorization to the API. For that I have added the following code:

        .AddJwtBearer(jwtOptions =>
        {
            jwtOptions.Authority = <local IDP server with IdentityServer4>;
            jwtOptions.Audience = <ClientID>
            jwtOptions.SaveToken = true;
        })

The API is guarded with

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

After login, I get the token with

await HttpContext.GetTokenAsync("access_token");

However, when I try to use that token to access the API, I get the following error:

AuthenticationFailed: IDX10214: Audience validation failed. Audiences: 'http://localhost:50059/resources'. Did not match: validationParameters.ValidAudience: ClientID or validationParameters.ValidAudiences: 'null'.

And indeed, when I decode the token using jwt.io, aud is set to http://localhost:50059/resources, while the ClientID appears as a new field 'client_id': '<ClientID>'.

What I found out so far suggests that aud is always set to <idp>/resources for access tokens and that API access is handled with scope claims inside the token. However, I do not understand how to set that up correctly.

Does anybody know where the problem lies and how I can fix it?

like image 813
xxSwordy Avatar asked May 10 '18 01:05

xxSwordy


People also ask

How do I store JWT tokens in cookie?

How to securely store JWTs in a cookie. A JWT needs to be stored in a safe place inside the user's browser. If you store it inside localStorage, it's accessible by any script inside your page. This is as bad as it sounds; an XSS attack could give an external attacker access to the token.

Can I use JWT with cookies?

Cookies. The server side can send the JWT token to the browser through a cookie, and the browser will automatically bring the JWT token in the cookie header when requesting the server-side interface, and the server side can verify the JWT token in the cookie header to achieve authentication.

How do I authorize API in net core?

Require authorization on a new API By default, the system is configured to easily require authorization for new APIs. To do so, create a new controller and add the [Authorize] attribute to the controller class or to any action within the controller.


1 Answers

Turns out that the solution was quite simple. As suspected, it had to do with scopes. I tried to solve that earlier but it kept saying "invalid scopes"...because I never created the API resource, duh.

  1. Create new API resource in your IDP server new ApiResource("api-name", "API Name")
  2. Add new API resource as AllowedScope to Client client.AllowedScopes.Add("api-name")
  3. Ask for Scope in OpenIdConnect authentication options.Scopes.Add("api-name")
  4. Set Audience (or ApiName if using IdentityServer authentication handler) to API resource options.Audience = "api-name" or options.ApiName = "api-name"

With that I was now able to use the access token to access my API.

like image 136
xxSwordy Avatar answered May 25 '23 12:05

xxSwordy