Accessing protected API on IdentityServer4 with Bearer Token
Asked Answered
I

4

15

I have attempted to search for a solution to this problem, but have not found the right search text.

My question is, how can I configure my IdentityServer so that it will also accept/authorize Api Requests with BearerTokens?

I have an IdentityServer4 configured and running. I also have configured a Test API on my IdentityServer like below:

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

In my startup.cs ConfigureServices() is as follows:

public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        ...
        // configure identity server with stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddCertificateFromStore(Configuration.GetSection("AuthorizationSettings"), loggerFactory.CreateLogger("Startup.ConfigureServices.AddCertificateFromStore"))

            // this adds the config data from DB (clients, resources)
            .AddConfigurationStore(options =>
            {
                options.DefaultSchema = "auth";
                options.ConfigureDbContext = builder =>
                {
                    builder.UseSqlServer(databaseSettings.MsSqlConnString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
                };
            })

            // this adds the operational data from DB (codes, tokens, consents)
            .AddOperationalStore(options =>
            {
                options.DefaultSchema = "auth";
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(databaseSettings.MsSqlConnString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));

                // this enables automatic token cleanup. this is optional.
                options.EnableTokenCleanup = true;
                options.TokenCleanupInterval = 30;
            })

            // this uses Asp Net Identity for user stores
            .AddAspNetIdentity<ApplicationUser>()
            .AddProfileService<AppProfileService>()
            ;

        services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = authSettings.AuthorityUrl;
                    options.RequireHttpsMetadata = authSettings.RequireHttpsMetadata;
                    options.ApiName = authSettings.ResourceName;
                })

and Configure() is as follows:

        // NOTE: 'UseAuthentication' is not needed, since 'UseIdentityServer' adds the authentication middleware
        // app.UseAuthentication();
        app.UseIdentityServer();

I have a client configured to allow Implicit grant types and have included the configured ApiName as one of the AllowedScopes:

 new Client
            {
                ClientId = "47DBAA4D-FADD-4FAD-AC76-B2267ECB7850",
                ClientName = "MyTest.Web",
                AllowedGrantTypes = GrantTypes.Implicit,

                RequireConsent = false,

                RedirectUris           = { "http://localhost:6200/assets/oidc-login-redirect.html", "http://localhost:6200/assets/silent-redirect.html" },
                PostLogoutRedirectUris = { "http://localhost:6200/?postLogout=true" },
                AllowedCorsOrigins     = { "http://localhost:6200" },

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    "dev.api",
                    "dev.auth" // <- ApiName for IdentityServer authorization
                },
                AllowAccessTokensViaBrowser = true,
                AllowOfflineAccess = true,
                AccessTokenLifetime = 18000,
            },

When I use Postman to access the protected API but it always redirects to the Login page even though a valid Bearer Token has been added to the Request header.

Commenting out the [Authorize] attribute will correctly return a response, but of course the User.Claims are empty.

When logging into the IdentityServer (via a browser) and then accessing the API (via the browser) it will also return a response. This time, the User.Claims are available.

Integer answered 11/10, 2018 at 17:10 Comment(0)
H
19

There is an example co-hosting a protected API inside IdentityServer: IdentityServerAndApi

I quick comparison between their startup and yours is that they are calling AddJwtBearer instead of AddIdentityServerAuthentication:

services.AddAuthentication()
 .AddJwtBearer(jwt => {
    jwt.Authority = "http://localhost:5000";
    jwt.RequireHttpsMetadata = false;
    jwt.Audience = "api1";
});

TheAuthorize attribute also sets the authentication scheme:

[Authorize(AuthenticationSchemes = "Bearer")]
Hedgcock answered 11/10, 2018 at 19:59 Comment(1)
turns out that the only change required was the Authorize attribute. Thanks!!Integer
P
3

If you want to set a default authentication scheme to be one level above the policies (it is most relevant when you have multiple policies or no policies at all):

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;                
}).AddJwtBearer(o =>
{
    o.Authority = "http://localhost:5000";
    o.RequireHttpsMetadata = false;
    o.Audience = "api1";             
});

Then you can simple use the [Authorize] tag attribute above the controller's method without polluting each authorization attribute with the sceme:

[Authorize]
public IActionResult GetFoo()
{
}
Pocketknife answered 22/8, 2020 at 21:23 Comment(0)
C
0

Found a better solution, configure in the Startup.cs:

services.AddAuthentication()
    .AddLocalApi();

services.AddAuthorization(options =>
{
    options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy =>
    {
        policy.AddAuthenticationSchemes(IdentityServerConstants.LocalApi.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
    });
});

And use in controllers:

[Authorize(IdentityServerConstants.LocalApi.PolicyName)]
public class UserInfoController : Controller
{
    ...
}
Casie answered 28/4, 2020 at 14:24 Comment(1)
services.AddLocalApiAuthentication() worked for me, then just put a simple [Authorize] on whichever controller route you want protectedBookcase
L
0

Or even simpler:

services.AddLocalApiAuthentication();

Again, you still need

[Authorize(IdentityServerConstants.LocalApi.PolicyName)]

on your controller/method. And don't forget to add

IdentityServerConstants.LocalApi.ScopeName

to the allowed scopes/requested ones in the token. See docs for more details.

Langer answered 2/2, 2021 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.