103 lines
3.5 KiB
C#
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
|
|
);
|
|
}
|
|
}
|
|
}
|