132 lines
4.0 KiB
C#
132 lines
4.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace SecureToken.Core
|
|
{
|
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
|
|
|
|
/// <summary>
|
|
/// Fluent builder for constructing token claims before issuing.
|
|
/// </summary>
|
|
public sealed class TokenDescriptor
|
|
{
|
|
private string _subject = string.Empty;
|
|
private string _issuer = string.Empty;
|
|
private readonly List<string> _audiences = new List<string>();
|
|
private readonly List<string> _roles = new List<string>();
|
|
private readonly Dictionary<string, string> _custom = new Dictionary<string, string>();
|
|
private TimeSpan _lifetime = TimeSpan.FromHours(1);
|
|
private TimeSpan _notBeforeDelay = TimeSpan.Zero;
|
|
private string? _bindingContext;
|
|
private string _tokenType = TokenTypeConstants.Access;
|
|
|
|
public static TokenDescriptor Create() => new TokenDescriptor();
|
|
|
|
public TokenDescriptor ForSubject(string subject)
|
|
{
|
|
_subject = subject;
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor IssuedBy(string issuer)
|
|
{
|
|
_issuer = issuer;
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor ForAudience(string audience)
|
|
{
|
|
_audiences.Add(audience);
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor ForAudiences(IEnumerable<string> audiences)
|
|
{
|
|
_audiences.AddRange(audiences);
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor WithRole(string role)
|
|
{
|
|
_roles.Add(role);
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor WithRoles(IEnumerable<string> roles)
|
|
{
|
|
_roles.AddRange(roles);
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor WithClaim(string key, string value)
|
|
{
|
|
_custom[key] = value;
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor WithLifetime(TimeSpan lifetime)
|
|
{
|
|
if (lifetime <= TimeSpan.Zero)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(lifetime), "Lifetime must be positive.");
|
|
}
|
|
|
|
_lifetime = lifetime;
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor ExpiresIn(int minutes) =>
|
|
WithLifetime(TimeSpan.FromMinutes(minutes));
|
|
|
|
/// <summary>
|
|
/// Bind this token to a specific context (IP address, device fingerprint, etc.).
|
|
/// The same binding must be provided during validation.
|
|
/// </summary>
|
|
public TokenDescriptor BoundTo(string context)
|
|
{
|
|
_bindingContext = context;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tag the token type to prevent cross-purpose usage.
|
|
/// Use <see cref="TokenTypeConstants"/> for well-known values.
|
|
/// </summary>
|
|
public TokenDescriptor OfType(string tokenType)
|
|
{
|
|
_tokenType = tokenType;
|
|
return this;
|
|
}
|
|
|
|
public TokenDescriptor AsRefreshToken() => OfType(TokenTypeConstants.Refresh);
|
|
public TokenDescriptor AsServiceAccount() => OfType(TokenTypeConstants.ServiceAccount);
|
|
|
|
public TokenDescriptor NotValidBefore(TimeSpan delay)
|
|
{
|
|
_notBeforeDelay = delay;
|
|
return this;
|
|
}
|
|
|
|
internal TokenClaims Build(int keyGeneration = 0)
|
|
{
|
|
var now = DateTimeOffset.UtcNow;
|
|
return new TokenClaims
|
|
{
|
|
Subject = _subject,
|
|
Issuer = _issuer,
|
|
Audiences = _audiences.AsReadOnly(),
|
|
Roles = _roles.AsReadOnly(),
|
|
Custom = _custom,
|
|
IssuedAt = now,
|
|
NotBefore = now + _notBeforeDelay,
|
|
ExpiresAt = now + _lifetime,
|
|
BindingContext = _bindingContext,
|
|
TokenType = _tokenType,
|
|
KeyGeneration = keyGeneration,
|
|
};
|
|
}
|
|
}
|
|
}
|