feat: Implement API client and token models with hashing and JWT authentication
- Added ApiClientModel and ApiTokenModel for managing API clients and tokens. - Introduced ConfigurationDefinitionModel and ConfigurationValueModel for configuration management. - Created CredentialSecretModel for storing credential secrets. - Developed DeploymentArtifactModel and DeploymentBatchModel for deployment management. - Enhanced DeploymentTargetModel and DeploymentTemplateSelectionModel to support template revisions. - Added TemplateRevisionModel and TemplateVersionModel for versioning templates. - Implemented ApiClientSecretHasher for secure secret hashing. - Created ApiTokenService for generating and validating JWT tokens. - Updated QueueJobService to handle deployment requests with artifacts. - Configured authentication settings in appsettings.json for JWT and Negotiate authentication.
This commit is contained in:
94
Controllers/AuthController.cs
Normal file
94
Controllers/AuthController.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
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<string> 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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user