using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.SelfService.Portal.Core.API.Context; using Microsoft.SelfService.Portal.Core.API.Dto.Auth; using Microsoft.SelfService.Portal.Core.API.Models; using Microsoft.SelfService.Portal.Core.API.Services; using System.Text.Json; namespace Microsoft.SelfService.Portal.Core.API.Controllers { [Route("api/auth")] [ApiController] public class AuthController : Controller { private readonly DataContext _context; private readonly ApiTokenService _tokenService; public AuthController(DataContext context, ApiTokenService tokenService) { _context = context; _tokenService = tokenService; } [HttpPost("token")] [AllowAnonymous] [ProducesResponseType(200, Type = typeof(TokenResponseDto))] [ProducesResponseType(400)] [ProducesResponseType(401)] public IActionResult CreateToken([FromBody] TokenRequestDto request) { if (request == null || string.IsNullOrWhiteSpace(request.ClientId) || string.IsNullOrWhiteSpace(request.ClientSecret)) { return BadRequest(new { message = "ClientId and ClientSecret are required." }); } var client = _context.ApiClients .FirstOrDefault(existing => existing.ClientId == request.ClientId); if (client == null || !client.IsEnabled || (client.ExpiresAt.HasValue && client.ExpiresAt.Value <= DateTime.UtcNow) || !ApiClientSecretHasher.VerifySecret(request.ClientSecret, client.SecretHash)) { return Unauthorized(new { message = "Invalid client credentials." }); } IReadOnlyCollection scopes; try { scopes = _tokenService.ResolveRequestedScopes(client, request.Scope); } catch (InvalidOperationException ex) { return Unauthorized(new { message = ex.Message }); } var token = _tokenService.CreateAccessToken(client, scopes); var now = DateTime.UtcNow; client.LastUsedAt = now; client.Modified = now; client.ModifiedBy = client.ClientId; _context.ApiTokens.Add(new ApiTokenModel { Id = Guid.NewGuid(), Jti = token.Jti, Subject = client.ClientId, SubjectType = "ApiClient", ApiClientId = client.Id, Name = $"Client credentials token for {client.Name}", ScopesJson = JsonSerializer.Serialize(scopes), IssuedAt = token.IssuedAt, ExpiresAt = token.ExpiresAt, Created = now, CreatedBy = client.ClientId, Modified = now, ModifiedBy = client.ClientId }); _context.SaveChanges(); return Ok(new TokenResponseDto { AccessToken = token.AccessToken, ExpiresIn = _tokenService.TokenLifetimeSeconds, Scope = string.Join(' ', scopes) }); } } }