.Net Core Custom Authentication using API Keys with Identity Server 4

manvir_singh

I have a .NET Core 2.2 Web API that authenticates with JWT tokens. Tokens are generated by Identity Server 4 on a separate API.

All the authentication and authorisation works as expected with JWT tokens. But I need to extend this to allow usage of API keys. If an API key is supplied, I want to load up the claims of that particular user, add it to the request and let Authorize attribute deal with the set policies.

Here is what I have done so far following suggestions from here. My error is exactly the same as the linked post and it works for me as well using GenericPrincipal with a set of roles but I am using AuthorisationPolicies and I always get 401 error with my current implementation, giving me errors similar to the link above.

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore(options =>
        {
            options.Filters.Add(new RequireHttpsAttribute());
            options.Filters.Add(new AuthorizeFilter());
            options.Filters.Add(typeof(ValidateModelStateAttribute));
            options.AllowEmptyInputInBodyModelBinding = true;
        })
        .AddAuthorization(options =>
        {
            options.AddPolicies();
        })
        .AddJsonFormatters();

        services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = Configuration["Authentication:Authority"];
                options.RequireHttpsMetadata = true;
                options.ApiName = Configuration["Authentication:ApiName"];
            });
        services.AddCors();
    }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseCors(policy =>
        {
            policy.AllowAnyHeader();
            policy.AllowAnyMethod();
            policy.AllowAnyOrigin();
        });

        app.UseHttpsRedirection();
        app.UseMiddleware<ApiKeyMiddleware>();
        app.UseAuthentication();
        app.UseMvc();
    }

AuthorizationPolicies.cs

public static class AuthorizationPolicies
{
    public const string ReadUsersPolicy = "ReadUsers";
    public const string EditUsersPolicy = "EditUsers";

    public static void AddPolicies(this AuthorizationOptions options)
    {
        options.AddPolicy(ReadUsersPolicy, policy => policy.RequireClaim(Foo.Permission, Foo.CanReadUsers));
        options.AddPolicy(EditUsersPolicy, policy => policy.RequireClaim(Foo.Permission, Foo.CanEditUsers));
    }
}

ApiKeyMiddleware

public class ApiKeyMiddleware
{
    public ApiKeyMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    private readonly RequestDelegate _next;

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Path.StartsWithSegments(new PathString("/api")))
        {
            if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
            {
                var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                await ValidateApiKey(context, _next, headerKey);
            }
            else
            {
                await _next.Invoke(context);
            }
        }
        else
        {
            await _next.Invoke(context);
        }
    }

    private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
    {
        var userClaimsService = context.RequestServices.GetService<IUserClaimsService>();
        List<string> permissions = (await userClaimsService.GetAllPermissionsForApiKey(key))?.ToList();
        if (permissions == null)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            await context.Response.WriteAsync("Invalid Api Key");
            return;
        }

        ICollection<Claim> claims = permissions.Select(x => new Claim(FooClaimTypes.Permission, x)).ToList();
        var identity = new ClaimsIdentity(claims);
        var principal = new ClaimsPrincipal(identity);
        context.User = principal;
        await next.Invoke(context);
    }
}

UsersController.cs

[Authorize(AuthorizationPolicies.EditUsersPolicy)]
    public async Task<IActionResult> Put([FromBody] UserUpdateDto userUpdateDto)
    {
        ...
    }
manvir_singh

Apparently, I had to set AuthenticationType to be Custom on the ClaimsIdentity as explained here.

var identity = new ClaimsIdentity(claims, "Custom");

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

.NET Core Identity Server 4 Authentication VS Identity Authentication

Authentication/Authorization using Asp.net Core 2.1 and Identity Server 4

.net core identity server 4 authentication handler for oidc

Identity Server 4 .Net Core 2.0: Multiple authentication type

API is authorized without the Authorization Headers in Request using Identity Server 4 and .net core Identity

API Integration Test for Login controller using Identity server4 in asp.net core

JWT Authentication using a custom attribute in .NET Core Web API

Blazor WebAssembly Authentication with IdentityServer4, Asp.Net Core Identity and custom provider without Entity Framework

.net identity custom authentication

How do I add authentication to an ASP.NET Core minimal API using Identity, but without using Azure?

How to implement .Net Core Identity with custom API

Identity Server 4 Authentication

client specific claims identity server4 using asp.net core identity

Logging in Identity Server 4 - .Net Core 2.2

User.Identity.IsAuthenticated always false in .net core custom authentication

.Net Core 2.0 Web API using JWT - Adding Identity breaks the JWT authentication

How to refresh CSRF token on login when using cookie authentication without identity in ASP .NET Core Web API

JWT Authentication for .net core 2.2 application not using Identity

Get user profile in ASP.net core API with Identity Server 4 Implicit Flow

Identity Server 4 Swagger Authentication

identity server 4 windows authentication

Asp.net Core 2 enable multi tenancy using Identity Server 4

Silent Authentication using Identity Server

Identity Server 4 with ASP.NET Core 2.2

Redirect from Identity Server 4 not working on .net core 2.2

ASP .NET Core MVC deploy to azure Identity Server 4

Custom authentication asp.net core web api

.Net Core JWT Authentication with custom API Key Middleware

Custom API Key Authentication method in .net core not working on controller