| | | 1 | | using Elsa.Common; |
| | | 2 | | using Elsa.Extensions; |
| | | 3 | | using Elsa.Identity.Contracts; |
| | | 4 | | using Elsa.Identity.Entities; |
| | | 5 | | using Elsa.Identity.Models; |
| | | 6 | | using Elsa.Identity.Options; |
| | | 7 | | using Microsoft.Extensions.DependencyInjection; |
| | | 8 | | using Microsoft.Extensions.Options; |
| | | 9 | | |
| | | 10 | | namespace Elsa.Identity.Services; |
| | | 11 | | |
| | | 12 | | /// <summary> |
| | | 13 | | /// Default implementation of <see cref="IAccessTokenIssuer"/>. |
| | | 14 | | /// </summary> |
| | | 15 | | public class DefaultAccessTokenIssuer : IAccessTokenIssuer |
| | | 16 | | { |
| | | 17 | | private readonly IRoleProvider _roleProvider; |
| | | 18 | | private readonly IElsaTokenService _tokenService; |
| | | 19 | | private readonly IPermissionStampCalculator? _stampCalculator; |
| | | 20 | | |
| | | 21 | | /// <summary> |
| | | 22 | | /// Initializes a new instance of the <see cref="DefaultAccessTokenIssuer"/> class. |
| | | 23 | | /// </summary> |
| | | 24 | | [ActivatorUtilitiesConstructor] |
| | 5 | 25 | | public DefaultAccessTokenIssuer(IRoleProvider roleProvider, IElsaTokenService tokenService, IPermissionStampCalculat |
| | | 26 | | { |
| | 5 | 27 | | _roleProvider = roleProvider; |
| | 5 | 28 | | _tokenService = tokenService; |
| | 5 | 29 | | _stampCalculator = stampCalculator; |
| | 5 | 30 | | } |
| | | 31 | | |
| | | 32 | | /// <summary> |
| | | 33 | | /// Initializes a new instance using the legacy constructor shape. |
| | | 34 | | /// </summary> |
| | | 35 | | public DefaultAccessTokenIssuer(IRoleProvider roleProvider, ISystemClock systemClock, IOptions<IdentityTokenOptions> |
| | 0 | 36 | | : this(roleProvider, new DefaultElsaTokenService(systemClock, identityTokenOptions)) |
| | | 37 | | { |
| | 0 | 38 | | } |
| | | 39 | | |
| | | 40 | | /// <inheritdoc /> |
| | | 41 | | public async ValueTask<IssuedTokens> IssueTokensAsync(User user, CancellationToken cancellationToken = default) |
| | | 42 | | { |
| | 0 | 43 | | var roles = (await _roleProvider.FindByIdsAsync(user.Roles, cancellationToken)).ToList(); |
| | 0 | 44 | | var permissions = roles.SelectMany(x => x.Permissions).ToList(); |
| | 0 | 45 | | var roleNames = roles.Select(x => x.Name).ToList(); |
| | | 46 | | |
| | | 47 | | // The stamp is derived from the same roles that produced these permissions, so it changes exactly |
| | | 48 | | // when the grants do. Issued unconditionally; only validation is gated by configuration, which |
| | | 49 | | // means turning the feature on does not invalidate tokens already in flight. |
| | 0 | 50 | | var stamp = _stampCalculator is null ? null : await _stampCalculator.ComputeAsync(user, cancellationToken); |
| | 0 | 51 | | var additionalClaims = stamp is null |
| | 0 | 52 | | ? Array.Empty<System.Security.Claims.Claim>() |
| | 0 | 53 | | : [new System.Security.Claims.Claim(PermissionStampCalculator.ClaimType, stamp)]; |
| | | 54 | | |
| | 0 | 55 | | var context = new TokenIssuanceContext(user, roleNames, permissions, additionalClaims); |
| | 0 | 56 | | var accessToken = await _tokenService.IssueAccessTokenAsync(context, cancellationToken); |
| | 0 | 57 | | var refreshToken = await _tokenService.IssueRefreshTokenAsync(context, cancellationToken); |
| | | 58 | | |
| | 0 | 59 | | return new IssuedTokens(accessToken.Token, refreshToken.Token); |
| | 0 | 60 | | } |
| | | 61 | | } |