Add init link.
This commit is contained in:
@@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ROLAC.API.Authorization;
|
||||
using ROLAC.API.DTOs.Invitations;
|
||||
using ROLAC.API.Services;
|
||||
|
||||
namespace ROLAC.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Admin endpoints for generating and e-mailing first-login invitation links.
|
||||
/// The public consume/validate endpoints live on <see cref="AuthController"/> so they can set the
|
||||
/// refresh-token cookie and stay anonymous.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/invitations")]
|
||||
[Authorize]
|
||||
public class InvitationsController : ControllerBase
|
||||
{
|
||||
private readonly IInvitationService _invitations;
|
||||
public InvitationsController(IInvitationService invitations) => _invitations = invitations;
|
||||
|
||||
/// <summary>POST /api/invitations — generate a link for a member; returns { token, expiresAt }.</summary>
|
||||
[HttpPost]
|
||||
[HasPermission(Modules.Users, PermissionActions.Write)]
|
||||
public async Task<IActionResult> Create([FromBody] CreateInvitationRequest request)
|
||||
{
|
||||
try { return Ok(await _invitations.CreateAsync(request)); }
|
||||
catch (InvalidOperationException ex) { return BadRequest(new { message = ex.Message }); }
|
||||
}
|
||||
|
||||
/// <summary>POST /api/invitations/send — e-mail an already-generated link to the member.</summary>
|
||||
[HttpPost("send")]
|
||||
[HasPermission(Modules.Users, PermissionActions.Write)]
|
||||
public async Task<IActionResult> Send([FromBody] SendInvitationRequest request)
|
||||
{
|
||||
try { await _invitations.SendEmailAsync(request.MemberId, request.Link); return NoContent(); }
|
||||
catch (InvalidOperationException ex) { return BadRequest(new { message = ex.Message }); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user