Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ csharp_style_prefer_local_over_anonymous_function = true:silent
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:silent
csharp_style_prefer_tuple_swap = true:silent
csharp_style_prefer_simple_property_accessors = true:suggestion

# Field preferences
dotnet_style_readonly_field = true:suggestion
Expand Down Expand Up @@ -299,4 +300,7 @@ dotnet_diagnostic.IDE0010.severity = none
dotnet_diagnostic.IDE0072.severity = none

# IDE0305: Simplify collection initialization
dotnet_diagnostic.IDE0305.severity = none
dotnet_diagnostic.IDE0305.severity = none

# CA1873: Avoid potentially expensive logging
dotnet_diagnostic.CA1873.severity = none
10 changes: 2 additions & 8 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
### Implementation Guidelines

- Write code that is secure by default. Avoid exposing potentially private or sensitive data.
- Make code NativeAOT compatible when possible. This means avoiding dynamic code generation, reflection, and other features that are not compatible. with NativeAOT. If not possible, mark the code with an appropriate annotation or throw an exception.
- Make code NativeAOT compatible when possible. This means avoiding dynamic code generation, reflection, and other features that are not compatible with NativeAOT. If not possible, mark the code with an appropriate annotation or throw an exception.

## Documentation

Expand All @@ -81,10 +81,4 @@
- Use NSubstitute for mocking in tests.
- Copy existing style in nearby files for test method names and capitalization.
- When running tests, if possible use filters and check test run counts, or look at test logs, to ensure they actually ran.
- Do not finish work with any tests commented out or disabled that were not previously commented out or disabled.

## Azure

- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools.
- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it.
- Do not finish work with any tests commented out or disabled that were not previously commented out or disabled.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ A library to easily integrate Authentication in ASP.NET Core projects. Currently

## Installation

The library is available on [NuGet](https://www.nuget.org/packages/SimpleAuthenticationTools). Just search for *SimpleAuthenticationTools* in the **Package Manager GUI** or run the following command in the **.NET CLI**:
The library is available on [NuGet](https://www.nuget.org/packages/SimpleAuthenticationTools). Search for *SimpleAuthenticationTools* in the **Package Manager GUI** or run the following command in the **.NET CLI**:

```shell
dotnet add package SimpleAuthenticationTools
```
## Usage video

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

[![Simple Authentication for ASP.NET Core](https://raw.githubusercontent.com/marcominerva/SimpleAuthentication/master/Screenshot.jpg)](https://www.youtube.com/watch?v=SVZuaPE2yNc)

## Configuration

Authentication can be totally configured adding an _Authentication_ section in the _appsettings.json_ file:
Authentication can be fully configured adding an _Authentication_ section in the _appsettings.json_ file:

```
"Authentication": {
Expand All @@ -42,7 +42,6 @@ Authentication can be totally configured adding an _Authentication_ section in t
"Audiences": [ "audience" ], // Optional
"ExpirationTime": "01:00:00", // Default: No expiration
"ClockSkew": "00:02:00", // Default: 5 minutes
"EnableJwtBearerService": true // Default: true
},
"ApiKey": {
"SchemeName": "ApiKey", // Default: ApiKey
Expand Down Expand Up @@ -107,7 +106,7 @@ 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. Just search for *SimpleAuthenticationTools.Swashbuckle* in the **Package Manager GUI** or run the following command in the **.NET CLI**:
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**:

```shell
dotnet add package SimpleAuthenticationTools.Swashbuckle
Expand All @@ -126,7 +125,7 @@ builder.Services.AddSwaggerGen(options =>

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

Starting from version 9, .NET offer a 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):
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):

```csharp
builder.Services.AddOpenApi(options =>
Expand All @@ -142,7 +141,7 @@ builder.Services.AddOpenApi(options =>
When using JWT Bearer authentication, you can set the _EnableJwtBearerService_ setting to _true_ to automatically register an implementation of the [IJwtBearerService](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/JwtBearer/IJwtBearerService.cs) interface to create a valid JWT Bearer, according to the setting you have specified in the _appsettings.json_ file:

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

Expand All @@ -153,7 +152,7 @@ app.MapPost("api/auth/login", (LoginRequest loginRequest, IJwtBearerService jwtB
new(ClaimTypes.Surname, "Minerva")
};

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

Expand Down Expand Up @@ -197,11 +196,11 @@ When using API Key or Basic Authentication, you can specify multiple fixed value
}
```

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

**Custom Authentication logic for API Keys and Basic Authentication**

If you need to implement custom authentication login, 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](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/ApiKey/IApiKeyValidator.cs) or [IBasicAuthenticationValidator.cs](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/BasicAuthentication/IBasicAuthenticationValidator.cs):
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](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/ApiKey/IApiKeyValidator.cs) or [IBasicAuthenticationValidator.cs](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/BasicAuthentication/IBasicAuthenticationValidator.cs):

```csharp
builder.Services.AddTransient<IApiKeyValidator, CustomApiKeyValidator>();
Expand Down Expand Up @@ -247,7 +246,7 @@ The library provides services for adding permission-based authorization to an AS
builder.Services.AddPermissions<T>();
```

The **AddPermissions** extension method requires an implementation of the [IPermissionHandler interface](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/Permissions/IPermissionHandler.cs), that is responsible to check if the user owns the required permissions:
The **AddPermissions** extension method requires an implementation of the [IPermissionHandler interface](https://github.com/marcominerva/SimpleAuthentication/blob/master/src/SimpleAuthentication.Abstractions/Permissions/IPermissionHandler.cs), which is responsible to check if the user owns the required permissions:

```csharp
public interface IPermissionHandler
Expand Down Expand Up @@ -317,4 +316,4 @@ app.MapGet("api/me", (ClaimsPrincipal user) =>

## Contribute

The project is constantly evolving. Contributions are welcome. Feel free to file issues and pull requests on the repo and we'll address them as we can.
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.
4 changes: 2 additions & 2 deletions samples/Controllers/ApiKeySample/ApiKeySample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions samples/Controllers/JwtBearerSample/JwtBearerSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions samples/MinimalApis/ApiKeySample/ApiKeySample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions samples/MinimalApis/JwtBearerSample/JwtBearerSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="[8.0.20,9.0.0)" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="[8.0.21,9.0.0)" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" PrivateAssets="All" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.8.118" PrivateAssets="All" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions src/SimpleAuthentication.Abstractions/ApiKey/ApiKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SimpleAuthentication.ApiKey;

/// <summary>
/// Store API Keys for API Key Authentication
/// </summary>
/// <param name="Value">The API key value</param>
/// <param name="UserName">The user name associated with the current key</param>
public record class ApiKey(string Value, string UserName);
36 changes: 13 additions & 23 deletions src/SimpleAuthentication.Abstractions/ApiKey/ApiKeySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,11 @@ public class ApiKeySettings : AuthenticationSchemeOptions
/// <seealso cref="ApiKeyValue"/>
public string? UserName { get; set; }

private ICollection<ApiKey> apiKeys = [];
/// <summary>
/// The collection of valid API keys.
/// </summary>
/// <seealso cref="ApiKey"/>
public ICollection<ApiKey> ApiKeys
{
get
{
if (!string.IsNullOrWhiteSpace(ApiKeyValue) && !string.IsNullOrWhiteSpace(UserName))
{
// If necessary, add the API Key from the base properties.
apiKeys.Add(new(ApiKeyValue, UserName));
}

return apiKeys;
}

internal set => apiKeys = value ?? [];
}
public IEnumerable<ApiKey> ApiKeys { get; set; } = [];

/// <summary>
/// Gets or sets a <see cref="string"/> that defines the <see cref="ClaimsIdentity.NameClaimType"/>.
Expand All @@ -80,11 +65,16 @@ public ICollection<ApiKey> ApiKeys
/// The default is <see cref="ClaimsIdentity.DefaultRoleClaimType"/>.
/// </remarks>
public string RoleClaimType { get; set; } = ClaimsIdentity.DefaultRoleClaimType;
}

/// <summary>
/// Store API Keys for API Key Authentication
/// </summary>
/// <param name="Value">The API key value</param>
/// <param name="UserName">The user name associated with the current key</param>
public record class ApiKey(string Value, string UserName);
internal IEnumerable<ApiKey> GetAllApiKeys()
{
var apiKeys = (ApiKeys ?? []).ToHashSet();
if (!string.IsNullOrWhiteSpace(ApiKeyValue) && !string.IsNullOrWhiteSpace(UserName))
{
// If necessary, add the API Key from the base properties.
apiKeys.Add(new(ApiKeyValue, UserName));
}

return apiKeys;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,11 @@ public class BasicAuthenticationSettings : AuthenticationSchemeOptions
/// <seealso cref="IBasicAuthenticationValidator"/>
public string? Password { get; set; }

private ICollection<Credential> credentials = [];
/// <summary>
/// The collection of authorization credentials.
/// </summary>
/// <seealso cref="Credential"/>
public ICollection<Credential> Credentials
{
get
{
if (!string.IsNullOrWhiteSpace(UserName) && !string.IsNullOrWhiteSpace(Password))
{
// If necessary, add the credentials from the base properties.
credentials.Add(new Credential(UserName, Password));
}

return credentials;
}

internal set => credentials = value ?? [];
}
public IEnumerable<Credential> Credentials { get; set; } = [];

/// <summary>
/// Gets or sets a <see cref="string"/> that defines the <see cref="ClaimsIdentity.NameClaimType"/>.
Expand All @@ -70,11 +55,15 @@ public ICollection<Credential> Credentials
/// </remarks>
public string RoleClaimType { get; set; } = ClaimsIdentity.DefaultRoleClaimType;

}
internal IEnumerable<Credential> GetAllCredentials()
{
var credentials = (Credentials ?? []).ToHashSet();
if (!string.IsNullOrWhiteSpace(UserName) && !string.IsNullOrWhiteSpace(Password))
{
// If necessary, add the Credentials from the base properties.
credentials.Add(new(UserName, Password));
}

/// <summary>
/// Store credentials used for Basic Authentication.
/// </summary>
/// <param name="UserName">The user name</param>
/// <param name="Password">The password</param>
public record class Credential(string UserName, string Password);
return credentials;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SimpleAuthentication.BasicAuthentication;

/// <summary>
/// Store credentials used for Basic Authentication.
/// </summary>
/// <param name="UserName">The user name</param>
/// <param name="Password">The password</param>
public record class Credential(string UserName, string Password);
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.20" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.21" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.10" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

<ItemGroup>
<PackageReference Include="SimpleAuthenticationTools.Abstractions" Version="3.0.12" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="9.0.6" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading