0

So, I'm building a new security token service that is replacing an old solution based on IdentityServer3. I wish to authenticate with PKCE flow, and so far I've configured the new service with a certificate, identity resources, a test user, an API resource, and a client.

Identity resources

IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource>
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResource
    {
        Name = JwtClaimTypes.Role,
        UserClaims = new List<string> { JwtClaimTypes.Role }
    }
};

User

List<TestUser> Users => new List<TestUser>
{
    new TestUser
    {
        SubjectId = "1234",
        Username = "asd",
        Password = "asd",
        Claims = new [] { new Claim(JwtClaimTypes.Role, "super") }
    }
};

API resource

IEnumerable<ApiResource> Apis => new List<ApiResource>
{
    new ApiResource
    {
        Name = "api",
        UserClaims = new List<string> { JwtClaimTypes.Role },
        Scopes = new List<Scope> { new Scope("api") }
    }
};

Client

IEnumerable<Client> Clients => new List<Client>
{
    new Client
    {
        ClientId = "client",
        AllowedGrantTypes = GrantTypes.Code,
        AllowAccessTokensViaBrowser = true,
        RequirePkce = true,
        RequireClientSecret = false,
        RequireConsent = false,

        AllowedScopes =
        {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            JwtClaimTypes.Role,
            "api"
        },

        AllowedCorsOrigins = { "http://localhost:3005/" },
        RedirectUris = { "http://localhost:3005/callback.html" },
        PostLogoutRedirectUris = { "http://localhost:3005/index.html" }
    }
};

The AngularJS client is using oidc-client.js. The communication between this and my token service seems to be working fine. I get valid JWT tokens returned from IdentityServer4, containing the data I expect. My goal is to pass the user's ID contained in an access token to the API, so it can make sure the user is authenticated through an external library built specifically for checking if the user has "super" role, and make calls to the DB with the user ID.

The client configures an OIDC UserManager like this:

return new Oidc.UserManager({
    client_id: 'client',
    response_type: 'code',
    filterProtocolClaims: true,
    loadUserInfo: true,
    revokeAccessTokenOnSignout: true,
    scope: 'openid profile role api',
    authority: 'https://localhost:44300/',
    redirect_uri: 'http://localhost:3005/callback.html',
    post_logout_redirect_uri: 'http://localhost:3005/index.html'
});

Now, this is an old codebase - but it seems to me that when calls are made from the client to the API - access tokens are put in an Authorization header in a push() call using $httpProvider.interceptors, like this..

$httpProvider.interceptors.push(function (apiUrl, oidcManager, currentLanguage) {
    return {
        'request': function (config) {
            if (isApiCall(config, apiUrl, oidcManager)) {        
                var userManager = oidcManager.getUserManager();
                userManager.getUser().then(function (user) {
                    if (!user) {
                        userManager.signinRedirect();
                    }
                    config.headers.Authorization = 'Bearer ' + user.access_token;
                });
            }            
            return config;
        }
    };
});

When logging the config object, I can verify that the Authorization header indeed contains the access token that I need. However, when calling the API with a simple GET request with no required input parameters - protected by an [Authorize] attribute on the action method, I get a 401 Not Authorized response. The API is configured like this, in the Startup class..

JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
    app.UseIdentityServerBearerTokenAuthentication(
        new IdentityServerBearerTokenAuthenticationOptions {
            Authority = "https://localhost:44300/",
            RequiredScopes = new[] { "api" }
        }
);

Now, setting a breakpoint in the controller constructor for the endpoint I'm trying to hit, I can see that no user data, claims, etc. are being picked up. I'm guessing this has to do with how the old AngularJS client is constructing and passing these requests, but so far I'm stuck!

Any insight and guidance on this problem would be much and greatly appreciated - and thanks for reading my long mess!

haugan
  • 385
  • 4
  • 12
  • 1
    as long as u see the token in the header there is nothing wrong in the angular side. Did you verify the token that is being sent to the server using chrome dev tools and inspected the token in jwt.io? all the information intact? – hashbytes Nov 25 '19 at 23:24
  • @hashbytes Thanks for your reply. It seems like my problem was that the APIs was configured in such a way that the routing got defined before my authorization logic. I had to refactor the code structure so IdentityServer was enabled in the correct order! (I think, lol.) – haugan Nov 26 '19 at 08:15

0 Answers0