-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Implemented token management. (#39)
* Implementing token management. * Implemented token creation & unit tests. * Completed unit tests. * Implementing token validation. * Implemented the token blacklist. * Added a migration. * Implemented unit tests. * Implemented unit tests. * Completed token management.
- Loading branch information
Showing
37 changed files
with
2,277 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Microsoft.IdentityModel.Tokens; | ||
|
||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Represents token creation options. | ||
/// </summary> | ||
public record CreateTokenOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the token type. This defaults to 'JWT'. | ||
/// </summary> | ||
public string Type { get; set; } = "JWT"; | ||
/// <summary> | ||
/// Gets or sets the signing algorithm. This defaults to 'HS256'. | ||
/// </summary> | ||
public string SigningAlgorithm { get; set; } = SecurityAlgorithms.HmacSha256; | ||
|
||
/// <summary> | ||
/// Gets or sets the token audience. | ||
/// </summary> | ||
public string? Audience { get; set; } | ||
/// <summary> | ||
/// Gets or sets the token issuer. | ||
/// </summary> | ||
public string? Issuer { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the token expiration date and time. Unspecified date time kinds will be treated as UTC. | ||
/// </summary> | ||
public DateTime? Expires { get; set; } | ||
/// <summary> | ||
/// Gets or sets the date and time when the token was issued. Unspecified date time kinds will be treated as UTC. | ||
/// </summary> | ||
public DateTime? IssuedAt { get; set; } | ||
/// <summary> | ||
/// Gets or sets the date and time from when the token is valid. Unspecified date time kinds will be treated as UTC. | ||
/// </summary> | ||
public DateTime? NotBefore { get; set; } | ||
} |
54 changes: 54 additions & 0 deletions
54
src/Logitar.Identity.Domain/Tokens/CreateTokenParameters.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Represents token creation parameters. | ||
/// </summary> | ||
public record CreateTokenParameters : CreateTokenOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the token subject. | ||
/// </summary> | ||
public ClaimsIdentity Subject { get; set; } | ||
/// <summary> | ||
/// Gets or sets the signing secret. | ||
/// </summary> | ||
public string Secret { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CreateTokenParameters"/> class. | ||
/// </summary> | ||
public CreateTokenParameters() : this(new(), string.Empty) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CreateTokenParameters"/> class. | ||
/// </summary> | ||
/// <param name="subject">The token subject.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
public CreateTokenParameters(ClaimsIdentity subject, string secret) | ||
{ | ||
Subject = subject; | ||
Secret = secret; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CreateTokenParameters"/> class. | ||
/// </summary> | ||
/// <param name="subject">The token subject.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
/// <param name="options">The token creation options.</param> | ||
public CreateTokenParameters(ClaimsIdentity subject, string secret, CreateTokenOptions? options) : this(subject, secret) | ||
{ | ||
if (options != null) | ||
{ | ||
Type = options.Type; | ||
SigningAlgorithm = options.SigningAlgorithm; | ||
Audience = options.Audience; | ||
Issuer = options.Issuer; | ||
Expires = options.Expires; | ||
IssuedAt = options.IssuedAt; | ||
NotBefore = options.NotBefore; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using Microsoft.IdentityModel.Tokens; | ||
|
||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Represents a created token. | ||
/// </summary> | ||
public record CreatedToken | ||
{ | ||
/// <summary> | ||
/// Gets the created security token. | ||
/// </summary> | ||
public SecurityToken SecurityToken { get; } | ||
/// <summary> | ||
/// Gets a string representation of the created token. | ||
/// </summary> | ||
public string TokenString { get; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CreatedToken"/> class. | ||
/// </summary> | ||
/// <param name="securityToken">The created security token.</param> | ||
/// <param name="tokenString">A string representation of the created token.</param> | ||
public CreatedToken(SecurityToken securityToken, string tokenString) | ||
{ | ||
SecurityToken = securityToken; | ||
TokenString = tokenString; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Defines a token blacklist. | ||
/// </summary> | ||
public interface ITokenBlacklist | ||
{ | ||
/// <summary> | ||
/// Blacklists the specified list of token identifiers. | ||
/// </summary> | ||
/// <param name="tokenIds">The token identifiers to blacklist.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The asynchronous operation.</returns> | ||
Task BlacklistAsync(IEnumerable<string> tokenIds, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Blacklists the specified list of token identifiers. | ||
/// </summary> | ||
/// <param name="tokenIds">The token identifiers to blacklist.</param> | ||
/// <param name="expiresOn">The expiration date and time of the token.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The asynchronous operation.</returns> | ||
Task BlacklistAsync(IEnumerable<string> tokenIds, DateTime? expiresOn, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Returns the blacklisted token identifiers from the specified list of token identifiers. | ||
/// </summary> | ||
/// <param name="tokenIds">The list of token identifiers.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The list of blacklisted token identifiers.</returns> | ||
Task<IEnumerable<string>> GetBlacklistedAsync(IEnumerable<string> tokenIds, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Removes expired token identifiers from the blacklist. | ||
/// </summary> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The asynchronous operation.</returns> | ||
Task PurgeAsync(CancellationToken cancellationToken = default); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Defines methods to manage tokens. | ||
/// </summary> | ||
public interface ITokenManager | ||
{ | ||
/// <summary> | ||
/// Creates a token for the specified subject, using the specified signing secret. | ||
/// </summary> | ||
/// <param name="subject">The subject of the token.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The created token.</returns> | ||
Task<CreatedToken> CreateAsync(ClaimsIdentity subject, string secret, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Creates a token for the specified subject, using the specified signing secret and creation options. | ||
/// </summary> | ||
/// <param name="subject">The subject of the token.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
/// <param name="options">The creation options.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The created token.</returns> | ||
Task<CreatedToken> CreateAsync(ClaimsIdentity subject, string secret, CreateTokenOptions? options, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Creates a token with the specified parameters. | ||
/// </summary> | ||
/// <param name="parameters">The creation parameters.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The created token.</returns> | ||
Task<CreatedToken> CreateAsync(CreateTokenParameters parameters, CancellationToken cancellationToken = default); | ||
|
||
/// <summary> | ||
/// Validates a token using the specified signing secret. | ||
/// </summary> | ||
/// <param name="token">The token to validate.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The validated token.</returns> | ||
Task<ValidatedToken> ValidateAsync(string token, string secret, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Validates a token using the specified signing secret and validation options. | ||
/// </summary> | ||
/// <param name="token">The token to validate.</param> | ||
/// <param name="secret">The signing secret.</param> | ||
/// <param name="options">The validation options.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The validated token.</returns> | ||
Task<ValidatedToken> ValidateAsync(string token, string secret, ValidateTokenOptions? options, CancellationToken cancellationToken = default); | ||
/// <summary> | ||
/// Validates a token with the specified parameters. | ||
/// </summary> | ||
/// <param name="parameters">The validation parameters.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The validated token.</returns> | ||
Task<ValidatedToken> ValidateAsync(ValidateTokenParameters parameters, CancellationToken cancellationToken = default); | ||
} |
46 changes: 46 additions & 0 deletions
46
src/Logitar.Identity.Domain/Tokens/SecurityTokenBlacklistedException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Microsoft.IdentityModel.Tokens; | ||
|
||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// The exception raised when a validated security token is blacklisted. | ||
/// </summary> | ||
public class SecurityTokenBlacklistedException : SecurityTokenValidationException | ||
{ | ||
/// <summary> | ||
/// A generic error message for this exception. | ||
/// </summary> | ||
public const string ErrorMessage = "The security token is blacklisted."; | ||
|
||
/// <summary> | ||
/// Gets or sets the list of blacklisted token identifiers. | ||
/// </summary> | ||
public IEnumerable<string> BlacklistedIds | ||
{ | ||
get => (IEnumerable<string>)Data[nameof(BlacklistedIds)]!; | ||
private set => Data[nameof(BlacklistedIds)] = value; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="SecurityTokenBlacklistedException"/> class. | ||
/// </summary> | ||
/// <param name="blacklistedIds">The list of the blacklisted token identifiers.</param> | ||
public SecurityTokenBlacklistedException(IEnumerable<string> blacklistedIds) : base(BuildMessage(blacklistedIds)) | ||
{ | ||
BlacklistedIds = blacklistedIds; | ||
} | ||
|
||
private static string BuildMessage(IEnumerable<string> blacklistedIds) | ||
{ | ||
StringBuilder message = new(); | ||
|
||
message.AppendLine(ErrorMessage); | ||
message.AppendLine("BlacklistedIds:"); | ||
foreach (string blacklistedId in blacklistedIds) | ||
{ | ||
message.Append(" - ").Append(blacklistedId).AppendLine(); | ||
} | ||
|
||
return message.ToString(); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/Logitar.Identity.Domain/Tokens/ValidateTokenOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace Logitar.Identity.Domain.Tokens; | ||
|
||
/// <summary> | ||
/// Represents token validation options. | ||
/// </summary> | ||
public record ValidateTokenOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the list of valid token types. | ||
/// </summary> | ||
public List<string> ValidTypes { get; set; } = []; | ||
|
||
/// <summary> | ||
/// Gets or sets the list of valid audiences. | ||
/// </summary> | ||
public List<string> ValidAudiences { get; set; } = []; | ||
/// <summary> | ||
/// Gets or sets the list of valid audiences. | ||
/// </summary> | ||
public List<string> ValidIssuers { get; set; } = []; | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether or not to blacklist the token identifiers if the token is valid. This should be set to true for one-time use tokens. | ||
/// </summary> | ||
public bool Consume { get; set; } | ||
} |
Oops, something went wrong.