Files
social-media/backend/Modules/Identity/Handlers/RefreshToken.cs
Jonathan Bourdon df3e602015
Some checks failed
Backend CI/CD / build_and_deploy (push) Has been cancelled
Frontend CI/CD / build_and_deploy (push) Has been cancelled
feat: pivot to social media workflow app
2026-04-24 12:58:35 -04:00

64 lines
1.9 KiB
C#

using Socialize.Infrastructure.Security;
using Socialize.Modules.Identity.Configuration;
using Socialize.Modules.Identity.Data;
using Socialize.Modules.Identity.Services;
using Microsoft.Extensions.Options;
namespace Socialize.Modules.Identity.Handlers;
[PublicAPI]
public record RefreshTokenRequest(
string RefreshToken);
[PublicAPI]
public record RefreshTokenResponse(
string AccessToken,
string RefreshToken);
[PublicAPI]
public class RefreshTokenHandler(
UserManager userManager,
IOptionsSnapshot<JwtOptions> jwtOptions,
AccessTokenFactory accessTokenFactory)
: Endpoint<RefreshTokenRequest, RefreshTokenResponse>
{
public override void Configure()
{
AllowAnonymous();
Post("/api/users/refresh");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
RefreshTokenRequest request,
CancellationToken ct)
{
// Find the user using the refresh token
User? user = await userManager.Users
.FirstOrDefaultAsync(u => u.RefreshToken == request.RefreshToken, ct);
if (user == null || user.RefreshTokenExpiryTime <= DateTime.UtcNow)
{
await SendUnauthorizedAsync(ct);
return;
}
// Generate a new refresh token if rotation is required
if (jwtOptions.Value.RefreshTokenRequireRotation || user.RefreshToken is null)
{
user.RefreshToken = RefreshTokenGenerator.Next();
}
// Update refresh token expiry time
user.RefreshTokenExpiryTime = DateTime.UtcNow.Add(jwtOptions.Value.RefreshTokenLifetime);
await userManager.UpdateAsync(user);
// Generate a new access token
string accessToken = await accessTokenFactory.CreateAsync(user);
await SendOkAsync(
new RefreshTokenResponse(accessToken, user.RefreshToken),
ct);
}
}