Files
EonaCat.SecureToken/EonaCat.SecureToken/Models/TokenPair.cs
T
2026-06-19 16:34:50 +02:00

103 lines
3.5 KiB
C#

using SecureToken.Core;
using System;
using System.Collections.Generic;
namespace SecureToken
{
// 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>
/// A paired access + refresh token, typically issued at login.
/// </summary>
public sealed class TokenPair
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public DateTimeOffset AccessTokenExpiry { get; set; }
public DateTimeOffset RefreshTokenExpiry { get; set; }
public TokenPair(string accessToken, string refreshToken, DateTimeOffset accessTokenExpiry, DateTimeOffset refreshTokenExpiry)
{
AccessToken = accessToken;
RefreshToken = refreshToken;
AccessTokenExpiry = accessTokenExpiry;
RefreshTokenExpiry = refreshTokenExpiry;
}
}
/// <summary>
/// Extension methods on <see cref="ITokenService"/> for common scenarios.
/// </summary>
public static class TokenServiceExtensions
{
/// <summary>
/// Issues a standard access + refresh token pair.
/// Access token is short-lived; refresh token is long-lived but type-tagged.
/// </summary>
public static TokenPair IssueTokenPair(
this ITokenService service,
string subject,
string issuer,
string audience,
IEnumerable<string>? roles = null,
IEnumerable<KeyValuePair<string, string>>? claims = null,
TimeSpan? accessTokenLifetime = null,
TimeSpan? refreshTokenLifetime = null,
string? bindingContext = null)
{
var accessLifetime = accessTokenLifetime ?? TimeSpan.FromMinutes(15);
var refreshLifetime = refreshTokenLifetime ?? TimeSpan.FromDays(30);
var accessDescriptor = TokenDescriptor.Create()
.ForSubject(subject)
.IssuedBy(issuer)
.ForAudience(audience)
.WithLifetime(accessLifetime)
.OfType(TokenTypeConstants.Access);
var refreshDescriptor = TokenDescriptor.Create()
.ForSubject(subject)
.IssuedBy(issuer)
.WithLifetime(refreshLifetime)
.AsRefreshToken();
if (roles != null)
{
foreach (var role in roles)
{
accessDescriptor.WithRole(role);
refreshDescriptor.WithRole(role);
}
}
if (claims != null)
{
foreach (var claim in claims)
{
accessDescriptor.WithClaim(claim.Key, claim.Value);
refreshDescriptor.WithClaim(claim.Key, claim.Value);
}
}
if (bindingContext != null)
{
accessDescriptor.BoundTo(bindingContext);
refreshDescriptor.BoundTo(bindingContext);
}
var now = DateTimeOffset.UtcNow;
var accessToken = service.Issue(accessDescriptor);
var refreshToken = service.Issue(refreshDescriptor);
return new TokenPair(
accessToken,
refreshToken,
now + accessLifetime,
now + refreshLifetime
);
}
}
}