Add init link.

This commit is contained in:
Chris Chen
2026-06-24 10:53:13 -07:00
parent e88ea7917f
commit e53cea7a82
20 changed files with 971 additions and 11 deletions
+44 -1
View File
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using ROLAC.API.DTOs.Auth;
using ROLAC.API.DTOs.Invitations;
using ROLAC.API.Entities;
using ROLAC.API.Services;
@@ -16,13 +17,16 @@ public class AuthController : ControllerBase
private const int CookieMaxAge = 30 * 24 * 60 * 60; // 30 days in seconds
private readonly IAuthService _authService;
private readonly IInvitationService _invitations;
private readonly UserManager<AppUser> _userManager;
private readonly IWebHostEnvironment _env;
public AuthController(
IAuthService authService, UserManager<AppUser> userManager, IWebHostEnvironment env)
IAuthService authService, IInvitationService invitations,
UserManager<AppUser> userManager, IWebHostEnvironment env)
{
_authService = authService;
_invitations = invitations;
_userManager = userManager;
_env = env;
}
@@ -186,6 +190,45 @@ public class AuthController : ControllerBase
return NoContent();
}
// -------------------------------------------------------------------------
// GET /api/auth/invitation/validate?token=...
// -------------------------------------------------------------------------
/// <summary>
/// Checks whether an invitation token can still be used. Anonymous so the public
/// "set your password" page can decide what to show before the member types anything.
/// </summary>
[HttpGet("invitation/validate")]
[AllowAnonymous]
[ProducesResponseType(typeof(ValidateInvitationResult), StatusCodes.Status200OK)]
public async Task<IActionResult> ValidateInvitation([FromQuery] string token)
=> Ok(await _invitations.ValidateAsync(token));
// -------------------------------------------------------------------------
// POST /api/auth/accept-invitation
// -------------------------------------------------------------------------
/// <summary>
/// Consumes an invitation: sets the account password and, on success, logs the member in
/// (issues the access token + refresh cookie) so first login lands straight on the portal.
/// </summary>
[HttpPost("accept-invitation")]
[AllowAnonymous]
[ProducesResponseType(typeof(LoginResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> AcceptInvitation([FromBody] AcceptInvitationRequest request)
{
var (user, error) = await _invitations.AcceptAsync(request.Token, request.NewPassword);
if (user is null)
return BadRequest(new { message = error });
var ip = HttpContext.Connection.RemoteIpAddress?.ToString();
var device = Request.Headers.UserAgent.FirstOrDefault();
var (response, raw) = await _authService.IssueSessionAsync(user, ip, device);
SetRefreshCookie(raw);
return Ok(response);
}
// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------