using Microsoft.AspNetCore; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using OpenIddict.Abstractions; using OpenIddict.Server.AspNetCore; using PrivaPub.StaticServices; using System.Security.Claims; using static OpenIddict.Abstractions.OpenIddictConstants; namespace PrivaPub.Api.Mastodon.Auth { [ApiController] public class TokenController : ControllerBase { readonly DbEntities _dbEntities; public TokenController(DbEntities dbEntities) { _dbEntities = dbEntities; } [HttpPost("/oauth/token"), IgnoreAntiforgeryToken, Produces("application/json")] public async Task Exchange(CancellationToken token) { var request = HttpContext.GetOpenIddictServerRequest(); if (request == default) return BadRequest(); if (request.IsAuthorizationCodeGrantType()) { var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; if (principal == default || !await Usable(principal.GetClaim(Claims.Subject), token)) return Refuse(Errors.InvalidGrant, "The persona can no longer be used."); return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } if (request.IsClientCredentialsGrantType()) { var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType, Claims.Name, Claims.Role); identity.SetClaim(Claims.Subject, "app:" + request.ClientId); identity.SetScopes(MastodonScopes.Parse(request.Scope)); identity.SetDestinations(_ => new[] { Destinations.AccessToken }); return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } return Refuse(Errors.UnsupportedGrantType, "The grant type is not supported."); } async Task Usable(string avatarId, CancellationToken token) { if (string.IsNullOrEmpty(avatarId)) return false; var link = await _dbEntities.RootToAvatars.Match(r => r.AvatarId == avatarId).ExecuteFirstAsync(token); var root = link == default ? default : await _dbEntities.RootUsers.MatchID(link.RootId).ExecuteFirstAsync(token); var avatar = await _dbEntities.Avatars.MatchID(avatarId).ExecuteFirstAsync(token); return root is { IsBanned: false, DeletedAt: null } && avatar is { DeletionAt: null }; } ForbidResult Refuse(string error, string description) => Forbid(new AuthenticationProperties(new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = error, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = description }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } }