Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System.Net.Mime;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
Comment thread
micheletolve marked this conversation as resolved.
Outdated
using Newtonsoft.Json.Linq;
using SimpleAuthentication.Auth0;
using SimpleAuthentication.JwtBearer;

namespace SimpleAuthentication.WebApi.Controllers;
Expand Down Expand Up @@ -58,10 +61,46 @@ public ActionResult<LoginResponse> Refresh(string token, bool validateLifetime =
var newToken = jwtBearerService.RefreshToken(token, validateLifetime, expiration);
return new LoginResponse(newToken);
}

[HttpPost]
[Route("auth0")]
public LoginResponseAuth0? LoginAuth0([FromServices] IAuth0Service auth0Service)
Comment thread
micheletolve marked this conversation as resolved.
Outdated
{
// Check for login rights...

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

var token = auth0Service.ObtainTokenAsync(claims);
return JsonConvert.DeserializeObject<LoginResponseAuth0>(token.Result);
}
}

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

public record class LoginResponse(string Token);

public record class ValidationResponse(bool IsValid, User? User);
public record class ValidationResponse(bool IsValid, User? User);

public record class LoginResponseAuth0
Comment thread
micheletolve marked this conversation as resolved.
Outdated
{
[JsonProperty("access_token")]
public string Token { get; set; }

[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }

[JsonProperty("token_type")]
public string Type { get; set; }

public LoginResponseAuth0(string token, int expiresIn, string type)
{
this.Token = token;
this.ExpiresIn = expiresIn;
this.Type = type;
}
}
10 changes: 10 additions & 0 deletions samples/SimpleAuthentication.WebApi/Controllers/MeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ public class MeController : ControllerBase
[ProducesDefaultResponseType]
public ActionResult<User> GetWithBearer()
=> new User(User.Identity!.Name);

[Authorize(AuthenticationSchemes = "ApiKey")]
[HttpGet("authorize-apikey")]
public User GetWithApiKey()
=> new(User.Identity!.Name);

[Authorize(AuthenticationSchemes = "Auth0")]
[HttpGet("authorize-auth0")]
public User GetWithAuth0()
=> new("Auth0 default user");
}

public record class User(string? UserName);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
11 changes: 10 additions & 1 deletion samples/SimpleAuthentication.WebApi/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"Authentication": {
"DefaultAuthenticationScheme": "Bearer", // Optional
"JwtBearer": {
"SchemeName": "Bearer", // Default Bearer
"SchemeName": "Bearer", // Default Bearer
"SecurityKey": "YKgsOiwvDLJe42dyyL3FkhlMAzZZ2Cmr0FTpyLsPE5DA2afd6NbbCV3d5oHDG2rVBaDHH540EUmrzXPPk2LnfanCdERl4apucmu2Ev5oVgN6dGCr8MMxXIIyTaNmmXHSsaONo75UkxQvFtsm9Qsnsz3VxuNzsoqrzqBQdsDvClo1LcrRNNcTdKcvceq1G57PZNxOWFS749wnsqq7r17a9vvinTdYME2umo7DRn8XUiwbdOajCehJfqipIjwbcuoCIrCwwMizKSiidw5KXU7koVvUSV0UH3o4TWHsVBnt5B1os6oPKtCQ63CPqlwHB5Pet4mzA2lhaFROZXbStpigaRJf3J6AOwZurMbo3LhzCpPW6KZwkixMpwCb82ekZvL0tmfQA2LeWDL2esZ9N4N8w8CzxrZt4gyEfywBwsoFohC0ydVznDpwbgCg05ktuczX3FFcsXEErwtY2wu0or0TSrUSnzIrYP26dOOUh4qREPJ7ZnZ5NoQjOMcXkiThdMuy", // Required
"Algorithm": "HS256", // Default HS256
"Issuers": [ "issuer" ], // Optional
Expand All @@ -21,6 +21,15 @@
// to validate the API Key.
//"ApiKeyValue": "f1I7S5GXa4wQDgLQWgz0",
"DefaultUserName": "ApiUser" // Required when ApiKeyValue is used
},
"Auth0":{
// This parameter are getteedtaken from https://auth0.com/ only for test
"SchemeName": "Auth0",
"Algorithm": "RS256",
"Domain": "https://dev-a6-vyksc.us.auth0.com", // The domain created on https://auth0.com/
Comment thread
micheletolve marked this conversation as resolved.
Outdated
"Audience": "https://github.com/micheletolve",
"ClientId": "ipSAr24nCse9QIAlpN6nm2sYdarlaVY5",
"ClientSecret": "dr-qxPyLT2O7eDzCdzal9CHAe-V7t-aouZWBsDNCUsCk6r-rOjrVRQtZ9zGL7wCT"
}
},
"Logging": {
Expand Down
38 changes: 38 additions & 0 deletions src/SimpleAuthentication/Auth0/Auth0Service.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Options;

namespace SimpleAuthentication.Auth0
Comment thread
micheletolve marked this conversation as resolved.
Outdated
{
internal class Auth0Service : IAuth0Service
{
private readonly Auth0Settings auth0Setting;

public Auth0Service(IOptions<Auth0Settings> auth0SettingOptions)
{
auth0Setting = auth0SettingOptions.Value;
}

public async Task<string> ObtainTokenAsync(IList<Claim>? claims = null)
Comment thread
micheletolve marked this conversation as resolved.
Outdated
{
string responseContent;
HttpClient httpClient = new HttpClient();
Comment thread
micheletolve marked this conversation as resolved.
Outdated
httpClient.BaseAddress = new Uri(auth0Setting.Domain);
httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));

var request = new HttpRequestMessage(HttpMethod.Post, "/oauth/token");
request.Content = new StringContent("{\"client_id\":\"ipSAr24nCse9QIAlpN6nm2sYdarlaVY5\",\"client_secret\":\"dr-qxPyLT2O7eDzCdzal9CHAe-V7t-aouZWBsDNCUsCk6r-rOjrVRQtZ9zGL7wCT\",\"audience\":\"https://github.com/micheletolve\",\"grant_type\":\"client_credentials\"}",
Comment thread
micheletolve marked this conversation as resolved.
Outdated
Encoding.UTF8,
"application/json");

using (var responseMessage = await httpClient.SendAsync(request))
Comment thread
micheletolve marked this conversation as resolved.
Outdated
{
responseContent = await responseMessage.Content.ReadAsStringAsync();
Comment thread
micheletolve marked this conversation as resolved.
Outdated
}
return responseContent;
Comment thread
micheletolve marked this conversation as resolved.
Outdated
}
}
}
40 changes: 40 additions & 0 deletions src/SimpleAuthentication/Auth0/Auth0Settings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;

namespace SimpleAuthentication.Auth0
{
/// <summary>
/// Options class provides information needed to control Auth0 Authentication handler behavior.
/// </summary>
public class Auth0Settings
{
/// <summary>
/// Gets or sets The authentication scheme name (Default: Auth0).
/// </summary>
public string SchemeName { get; set; } = JwtBearerDefaults.AuthenticationScheme;
Comment thread
micheletolve marked this conversation as resolved.

/// <summary>
/// Gets or sets the cryptographic algorithm that is used to generate the digital signature (Default: RS256).
/// </summary>
public string Algorithm { get; set; } = "RS256";

/// <summary>
/// Gets or sets the domain.
/// </summary>
public string Domain { get; set; } = null!;

/// <summary>
/// Gets or sets the valid audiences that will be used to check against the token's audience.
/// </summary>
public string Audience { get; set; } = null!;

/// <summary>
/// Gets or sets the client id.
/// </summary>
public string CilentId { get; set; } = null!;

/// <summary>
/// Gets or sets the client secret.
/// </summary>
public string CilentSecret { get; set; } = null!;
}
}
17 changes: 17 additions & 0 deletions src/SimpleAuthentication/Auth0/IAuth0Service.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Security.Claims;

namespace SimpleAuthentication.Auth0
{
/// <summary>
/// Provides methods for Auth0 Bearer generation and validation.
Comment thread
micheletolve marked this conversation as resolved.
Outdated
/// </summary>
public interface IAuth0Service
{
/// <summary>
/// Obtains a bearer token string from Auth0 provider.
/// </summary>
/// <param name="claims">The claims list.</param>
/// <returns></returns>
Comment thread
micheletolve marked this conversation as resolved.
Outdated
Task<string> ObtainTokenAsync(IList<Claim>? claims = null);
}
}
25 changes: 25 additions & 0 deletions src/SimpleAuthentication/SimpleAuthenticationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
using SimpleAuthentication.ApiKey;
using SimpleAuthentication.Auth0;
using SimpleAuthentication.JwtBearer;
using SimpleAuthentication.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
Expand Down Expand Up @@ -64,6 +65,7 @@ public static ISimpleAuthenticationBuilder AddSimpleAuthentication(this Authenti
{
CheckAddJwtBearer(builder, configuration.GetSection($"{sectionName}:JwtBearer"));
CheckAddApiKey(builder, configuration.GetSection($"{sectionName}:ApiKey"));
CheckAddAuth0(builder, configuration.GetSection($"{sectionName}:Auth0"));

return new DefaultSimpleAuthenticationBuilder(configuration, builder);

Expand Down Expand Up @@ -134,6 +136,29 @@ static void CheckAddApiKey(AuthenticationBuilder builder, IConfigurationSection
options.DefaultUserName = settings.DefaultUserName;
});
}

static void CheckAddAuth0(AuthenticationBuilder builder, IConfigurationSection section)
{
var auth0Settings = section.Get<Auth0Settings>();
if (auth0Settings is null)
{
return;
}

ArgumentNullException.ThrowIfNull(auth0Settings.SchemeName, nameof(Auth0Settings.SchemeName));
ArgumentNullException.ThrowIfNull(auth0Settings.Domain, nameof(Auth0Settings.Domain));
ArgumentNullException.ThrowIfNull(auth0Settings.Audience, nameof(Auth0Settings.Audience));

builder.Services.Configure<Auth0Settings>(section);

builder.AddJwtBearer(auth0Settings.SchemeName, options =>
{
options.Authority = auth0Settings.Domain;
options.Audience = auth0Settings.Audience;
});

builder.Services.TryAddSingleton<IAuth0Service, Auth0Service>();
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using SimpleAuthentication.ApiKey;
using SimpleAuthentication.Auth0;
using SimpleAuthentication.JwtBearer;
using Swashbuckle.AspNetCore.SwaggerGen;

Expand All @@ -18,12 +19,14 @@ internal class AuthenticationOperationFilter : IOperationFilter
private readonly IAuthorizationPolicyProvider authorizationPolicyProvider;
private readonly JwtBearerSettings jwtBearerSettings;
private readonly ApiKeySettings apiKeySettings;
private readonly Auth0Settings auth0Settings;

public AuthenticationOperationFilter(IAuthorizationPolicyProvider authorizationPolicyProvider, IOptions<JwtBearerSettings> jwtBearerSettingsOptions, IOptions<ApiKeySettings> apiKeySettingsOptions)
public AuthenticationOperationFilter(IAuthorizationPolicyProvider authorizationPolicyProvider, IOptions<JwtBearerSettings> jwtBearerSettingsOptions, IOptions<ApiKeySettings> apiKeySettingsOptions, IOptions<Auth0Settings> auth0SettingsOptions)
{
this.authorizationPolicyProvider = authorizationPolicyProvider;
jwtBearerSettings = jwtBearerSettingsOptions.Value;
apiKeySettings = apiKeySettingsOptions.Value;
auth0Settings = auth0SettingsOptions.Value;
}

public void Apply(OpenApiOperation operation, OperationFilterContext context)
Expand All @@ -49,6 +52,9 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context)
CheckAddSecurityRequirement(operation, hasApiKeyHeaderAuthentication ? $"{apiKeySettings.SchemeName} in Header" : null);
CheckAddSecurityRequirement(operation, hasApiKeyQueryAuthentication ? $"{apiKeySettings.SchemeName} in Query String" : null);

var hasAuth0Authentication = !string.IsNullOrWhiteSpace(auth0Settings.Domain);
CheckAddSecurityRequirement(operation, hasAuth0Authentication ? $"{auth0Settings.SchemeName} Bearer" : null);

operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(), GetResponse(HttpStatusCode.Unauthorized.ToString()));
operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(), GetResponse(HttpStatusCode.Forbidden.ToString()));
}
Expand Down