Skip to content

Commit c54fc23

Browse files
author
Vadim Belov
committed
Add logout endpoint and refresh token revocation support
Introduce a Logout endpoint to BaseAuthController supporting GET, POST, and DELETE methods to revoke refresh tokens and remove authentication cookies. Add abstract RevokeRefreshTokenAsync method for token revocation logic. Ensure UserFactory returns null for unauthenticated users.
1 parent 7fbfb35 commit c54fc23

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

Sources/EasyExtensions.AspNetCore.Authorization/Controllers/BaseAuthController.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,31 @@ public abstract class BaseAuthController(
3131
/// </summary>
3232
public IPAddress RequestIP => IPAddress.Parse(Request.GetRemoteAddress());
3333

34+
/// <summary>
35+
/// Logs out the current user by revoking the refresh token and removing the authentication cookie.
36+
/// </summary>
37+
/// <remarks>This endpoint supports GET, POST, and DELETE HTTP methods. If a refresh token cookie
38+
/// is present, it is revoked and deleted. If no refresh token is found, the operation completes without
39+
/// error.</remarks>
40+
/// <returns>An <see cref="IActionResult"/> indicating the result of the logout operation. Returns a success message if
41+
/// the user was logged out.</returns>
42+
[HttpGet("logout")]
43+
[HttpPost("logout")]
44+
[HttpDelete("logout")]
45+
public async Task<IActionResult> Logout([FromQuery] string? token = "")
46+
{
47+
if (Request.Cookies.TryGetValue(CookieRefreshTokenName, out string? refreshToken))
48+
{
49+
await RevokeRefreshTokenAsync(refreshToken);
50+
Response.Cookies.Delete(CookieRefreshTokenName);
51+
}
52+
if (!string.IsNullOrWhiteSpace(token))
53+
{
54+
await RevokeRefreshTokenAsync(token);
55+
}
56+
return Ok("Logged out successfully");
57+
}
58+
3459
/// <summary>
3560
/// Changes the password for the currently authenticated user.
3661
/// </summary>
@@ -248,6 +273,15 @@ public async Task<IActionResult> LoginWithGoogle([FromQuery] string token)
248273
/// <returns>A task that represents the asynchronous operation.</returns>
249274
public abstract Task SaveAndRevokeRefreshTokenAsync(Guid userId, string oldRefreshToken, string newRefreshToken, AuthType authType);
250275

276+
/// <summary>
277+
/// Revokes the specified refresh token, invalidating it for future use.
278+
/// </summary>
279+
/// <remarks>After revocation, the specified refresh token can no longer be used to obtain new
280+
/// access tokens. This method is typically used to log out a user or to respond to a security event.</remarks>
281+
/// <param name="refreshToken">The refresh token to revoke. Cannot be null or empty.</param>
282+
/// <returns>A task that represents the asynchronous revoke operation.</returns>
283+
public abstract Task RevokeRefreshTokenAsync(string refreshToken);
284+
251285
/// <summary>
252286
/// Asynchronously retrieves the unique identifier of a user associated with the specified refresh token.
253287
/// </summary>

Sources/EasyExtensions.AspNetCore.Sentry/Factories/UserFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ public class UserFactory(IHttpContextAccessor httpContextAccessor) : ISentryUser
2323
return null;
2424
}
2525
var context = httpContextAccessor.HttpContext;
26+
if (context.User?.Identity == null || !context.User.Identity.IsAuthenticated)
27+
{
28+
return null;
29+
}
2630
var claims = httpContextAccessor.HttpContext.User.Claims;
2731
int userId = httpContextAccessor.HttpContext.User.TryGetId();
2832
bool hasUserId = httpContextAccessor.HttpContext.User.TryGetUserId(out Guid guidUserId);

0 commit comments

Comments
 (0)