MIT License

项目介绍

marcominerva/SimpleAuthentication 是一个托管在 GitHub 上的开源项目,主要使用 C# 编写,采用 MIT License 许可证,目前获得 251 个 Star、27 次 Fork。

仓库全名
marcominerva/SimpleAuthentication
开源许可证
MIT License
Star 数
251
Fork 数
27
未关闭 Issue
4

README 摘要

Simple Authentication for ASP.NET Core A library to easily integrate Authentication in ASP.NET Core projects. Currently it supports JWT Bearer, API Key and Basic Authentication in both Controller-based and Minimal API projects. [!IMPORTANT] Update from Version 2.x to 3.x Swashbuckle (Swagger) support has been moved out from SimpleAuthentication. If you're using the AddSimpleAuthentication extension method with…

完整内容见 GitHub 原始 README 或本页 。

常见问题

SimpleAuthentication 用什么语言开发?

主要使用 C# 编写,采用 MIT License 许可证,目前有 251 个 Star。

SimpleAuthentication 可以免费商用吗?

仓库采用 MIT License 许可证,可免费使用与修改,商业使用请遵守许可证条款(例如保留版权声明与许可声明)。

如何获取 SimpleAuthentication 的源码?

可以直接克隆仓库:git clone https://github.com/marcominerva/SimpleAuthentication.git,默认分支为 master;安装与运行步骤见仓库 README。

SimpleAuthentication 最近还在维护吗?

仓库最近一次更新为 9月9日,当前有 4 个未关闭 Issue;Star 数为 251,可作为社区活跃度的参考。

关于

GitHub 原始描述(英文)

A library to easily integrate Authentication in ASP.NET Core projects.

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

语言占比

C#

README

Simple Authentication for ASP.NET Core

Lint Code Base CodeQL Nuget NuGet License: MIT

A library to easily integrate Authentication in ASP.NET Core projects. Currently it supports JWT Bearer, API Key and Basic Authentication in both Controller-based and Minimal API projects.

[!IMPORTANT] Update from Version 2.x to 3.x Swashbuckle (Swagger) support has been moved out from SimpleAuthentication. If you’re using the AddSimpleAuthentication extension method with AddSwaggerGen, now you need to install the SimpleAuthentication.Swashbuckle package.

Installation

The library is available on NuGet. Search for SimpleAuthenticationTools in the Package Manager GUI or run the following command in the .NET CLI:

dotnet add package SimpleAuthenticationTools

Usage video

Take a look at a quick demo showing how to integrate the library:

Simple Authentication for ASP.NET Core

Configuration

Authentication can be fully configured adding an Authentication section in the appsettings.json file:

"Authentication": {
    "DefaultScheme": "Bearer", // Optional
    "JwtBearer": {
        "SchemeName": "Bearer" // Default: Bearer
        //"NameClaimType": "user_name", // Default: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
        //"RoleClaimType": "user_role", // Default: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
        "SecurityKey": "supersecretsecuritykey42!", // Required
        "Algorithm": "HS256", // Default: HS256
        "Issuers": [ "issuer" ], // Optional
        "Audiences": [ "audience" ], // Optional
        "ExpirationTime": "01:00:00", // Default: No expiration
        "ClockSkew": "00:02:00", // Default: 5 minutes
    },
    "ApiKey": {
        "SchemeName": "ApiKey", // Default: ApiKey
        // You can specify either HeaderName, QueryStringKey or both
        "HeaderName": "x-api-key",
        "QueryStringKey": "code",
        //"NameClaimType": "user_name", // Default: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
        //"RoleClaimType": "user_role", // Default: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
        // Uncomment this line if you want to validate the API Key against a fixed value.
        // Otherwise, you need to register an IApiKeyValidator implementation that will be used
        // to validate the API Key.
        //"ApiKeyValue": "f1I7S5GXa4wQDgLQWgz0",
        "UserName": "ApiUser" // Required if ApiKeyValue is used
    },
    "Basic": {
        "SchemeName": "Basic", // Default: Basic
        //"NameClaimType": "user_name", // Default: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
        //"RoleClaimType": "user_role", // Default: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
        // Uncomment the following lines if you want to validate user name and password
        // against fixed values.
        // Otherwise, you need to register an IBasicAuthenticationValidator implementation
        // that will be used to validate the credentials.
        //"UserName": "marco",
        //"Password": "P@$$w0rd"
    }
}

You can configure only the kind of authentication you want to use, or you can include all of them.

The DefaultScheme attribute is used to specify what kind of authentication must be configured as default. Allowed values are the values of the SchemeName attributes.

Registering authentication at Startup

using SimpleAuthentication;

var builder = WebApplication.CreateBuilder(args);

// ...
// Registers authentication schemes and services using IConfiguration information (see above).
builder.Services.AddSimpleAuthentication(builder.Configuration);

// ...

var app = builder.Build();

//...
// The following middlewares aren't strictly necessary in .NET 7.0 or higher, because they are automatically
// added when detecting that the corresponding services have been registered. However, you may
// need to call them explicitly if the default middlewares configuration is not correct for your
// app, for example when you need to use CORS.
// Check https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/middleware
// for more information.
//app.UseAuthentication();
//app.UseAuthorization();

//...

app.Run();

Integrating with Swashbuckle

If you’re using Swashbuckle (Swagger) to document your API, you can integrate the authentication configuration with the Swagger documentation. Search for SimpleAuthenticationTools.Swashbuckle in the Package Manager GUI or run the following command in the .NET CLI:

dotnet add package SimpleAuthenticationTools.Swashbuckle

Then, you can use the AddSimpleAuthentication extension method:

builder.Services.AddSwaggerGen(options =>
{
    // ...
    // Add this line to integrate authentication with Swagger.
    options.AddSimpleAuthentication(builder.Configuration);
});

Integrating with Microsoft.AspNetCore.OpenApi (.NET 9 or later)

Starting from version 9, .NET offers built-in support for OpenAPI. If you’re using the AddOpenApi extension method to provide OpenAPI support, you just need to add the corresponding extension method in its declaration (no extra package required):

builder.Services.AddOpenApi(options =>
{
    // ...
    // Add this line to integrate authentication with OpenAPI.
    options.AddSimpleAuthentication(builder.Configuration);
});

Creating a JWT Bearer

When using JWT Bearer authentication, an implementation of the IJwtBearerService interface is automatically registered to create a valid JWT Bearer, according to the settings you have specified in the appsettings.json file:

app.MapPost("api/auth/login", async (LoginRequest loginRequest, IJwtBearerService jwtBearerService) =>
{
    // Check for login rights...

    // Add custom claims (optional).
    var claims = new List<Claim>
    {
        new(ClaimTypes.GivenName, "Marco"),
        new(ClaimTypes.Surname, "Minerva")
    };

    var token = await jwtBearerService.CreateTokenAsync(loginRequest.UserName, claims);
    return TypedResults.Ok(new LoginResponse(token));
});

public record class LoginRequest(string UserName, string Password);

public record class LoginResponse(string Token);

The IJwtBearerService.CreateToken method allows to specify the issuer and the audience of the token. If you don’t specify any value, the first ones defined in appsettings.json will be used.

Supporting multiple API Keys/Basic Authentication credentials

When using API Key or Basic Authentication, you can specify multiple fixed values for authentication:

"Authentication": {
    "ApiKey": {
        "ApiKeys": [
            {
                "Value": "key-1",
                "UserName": "UserName1"
            },
            {
                "Value": "key-2",
                "UserName": "UserName2"
            }
        ]
    },
    "Basic": {
        "Credentials": [
            {
                "UserName": "UserName1",
                "Password": "Password1"
            },
            {
                "UserName": "UserName2",
                "Password": "Password2"
            }
        ]
    }
}

With this configuration, authentication will succeed if any of these credentials are provided.

Assigning roles to API Keys and Basic Authentication credentials

You can optionally specify roles for each API Key or Basic Authentication credential. When authentication succeeds, the specified roles will be automatically added as role claims to the user’s identity.

For single credentials, you can specify roles directly:

"Authentication": {
    "ApiKey": {
        "ApiKeyValue": "f1I7S5GXa4wQDgLQWgz0",
        "UserName": "ApiUser",
        "Roles": ["Administrator"]
    },
    "Basic": {
        "UserName": "marco",
        "Password": "P@$$w0rd",
        "Roles": ["Administrator"]
    }
}

For multiple credentials, you can specify roles for each credential:

"Authentication": {
    "ApiKey": {
        "ApiKeys": [
            {
                "Value": "key-1",
                "UserName": "UserName1",
                "Roles": ["Administrator", "User"]
            },
            {
                "Value": "key-2",
                "UserName": "UserName2",
                "Roles": ["User"]
            }
        ]
    },
    "Basic": {
        "Credentials": [
            {
                "UserName": "UserName1",
                "Password": "Password1",
                "Roles": ["Manager", "User"]
            },
            {
                "UserName": "UserName2",
                "Password": "Password2",
                "Roles": ["User"]
            }
        ]
    }
}

The Roles parameter is optional. If omitted, no role claims will be added to the user’s identity. You can then use the standard ASP.NET Core authorization features to check for roles:

[Authorize(Roles = "Administrator")]
public IActionResult AdminEndpoint()
{
    return Ok("Administrator access granted");
}

// Or with minimal APIs
app.MapGet("/admin", () => "Administrator access granted")
   .RequireAuthorization(policy => policy.RequireRole("Administrator"));

Custom Authentication logic for API Keys and Basic Authentication

If you need to implement custom authentication logic, for example validating credentials with dynamic values and adding claims to identity, you can omit all the credentials in the appsettings.json file and then provide an implementation of IApiKeyValidator.cs or IBasicAuthenticationValidator.cs:

builder.Services.AddTransient<IApiKeyValidator, CustomApiKeyValidator>();
builder.Services.AddTransient<IBasicAuthenticationValidator, CustomBasicAuthenticationValidator>();
//...

public class CustomApiKeyValidator : IApiKeyValidator
{
    public Task<ApiKeyValidationResult> ValidateAsync(string apiKey)
    {
        var result = apiKey switch
        {
            "ArAilHVOoL3upX78Cohq" => ApiKeyValidationResult.Success("User 1"),
            "DiUU5EqImTYkxPDAxBVS" => ApiKeyValidationResult.Success("User 2"),
            _ => ApiKeyValidationResult.Fail("Invalid User")
        };

        return Task.FromResult(result);
    }
}

public class CustomBasicAuthenticationValidator : IBasicAuthenticationValidator
{
    public Task<BasicAuthenticationValidationResult> ValidateAsync(string userName, string password)
    {
        if (userName == password)
        {
            var claims = new List<Claim>() { new(ClaimTypes.Role, "User") };
            return Task.FromResult(BasicAuthenticationValidationResult.Success(userName, claims));
        }

        return Task.FromResult(BasicAuthenticationValidationResult.Fail("Invalid user"));
    }
}

Permission-based authorization

The library provides services for adding permission-based authorization to an ASP.NET Core project. Just use the following registration at startup:

// Enable permission-based authorization.
builder.Services.AddPermissions<T>();

The AddPermissions extension method requires an implementation of the IPermissionHandler interface, which is responsible to check if the user owns the required permissions:

public interface IPermissionHandler
{
    Task<bool> IsGrantedAsync(ClaimsPrincipal user, IEnumerable<string> permissions);
}

The library provides the built-in ScopeClaimPermissionHandler class, that checks for permissions reading the default scope claims of the current user (scp or _http://schemas.microsoft.com/identity/claims/scope_). To use this default handler, we can just write this:

builder.Services.AddScopePermissions(); 
// The line above is equivalent to builder.Services.AddPermissions<ScopeClaimPermissionHandler>();

Based on the scenario, we can provide our own implementation, for example reading different claims or using external services (database, HTTP calls, etc.) to get user permissions.

Then, just use the PermissionAttribute or the RequirePermission extension method:

// In a Controller
[Permission("profile")]
public ActionResult<User> Get() => new User(User.Identity!.Name);

// In a Minimal API
app.MapGet("api/me", (ClaimsPrincipal user) =>
{
    return TypedResults.Ok(new User(user.Identity!.Name));
})
.RequirePermission("profile")

With the ScopeClaimPermissionHandler mentioned above, the invocation succeeds if the user has a scp or _http://schemas.microsoft.com/identity/claims/scope_ claim that contains the profile value, for example:

"scp": "profile email calendar:read"

It is also possible to explicitly create a policy that requires the one or more permissions:

builder.Services.AddAuthorization(options =>
{
    // Define permissions using a policy.
    options.AddPolicy("UserProfile", builder => builder.RequirePermission("profile"));
});

// ...

// In a Controller
[Authorize(Policy = "UserProfile")] 
public ActionResult<User> Get() => new User(User.Identity!.Name);

// In a Minimal API
app.MapGet("api/me", (ClaimsPrincipal user) =>
{
    return TypedResults.Ok(new User(user.Identity!.Name));
})
.RequireAuthorization(policyNames: "UserProfile")

Samples

Contribute

The project is constantly evolving. Contributions are welcome. Feel free to file issues and pull requests in the repository, and we’ll address them as we can.

评论

暂无评论