using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; using Shogi.Api.Models; using Shogi.Api.Repositories; using System.Security.Claims; namespace Shogi.Api; /// /// Standardizes the claims from third party issuers. Also registers new msal users in the database. /// public class ShogiUserClaimsTransformer : IShogiUserClaimsTransformer { private readonly IUserRepository userRepository; public ShogiUserClaimsTransformer(IUserRepository userRepository) { this.userRepository = userRepository; } public async Task TransformAsync(ClaimsPrincipal principal) { var newPrincipal = IsMicrosoft(principal) ? await CreateClaimsFromMicrosoftPrincipal(principal) : await CreateClaimsFromGuestPrincipal(principal); return newPrincipal; } public async Task CreateClaimsFromGuestPrincipal(ClaimsPrincipal principal) { var id = GetGuestUserId(principal); if (string.IsNullOrWhiteSpace(id)) { var newUser = User.CreateGuestUser(Guid.NewGuid().ToString()); await this.userRepository.CreateUser(newUser); return new ClaimsPrincipal(CreateClaimsIdentity(newUser)); } var user = await this.userRepository.ReadUser(id); if (user == null) throw new UnauthorizedAccessException("Guest account does not exist."); return new ClaimsPrincipal(CreateClaimsIdentity(user)); } private async Task CreateClaimsFromMicrosoftPrincipal(ClaimsPrincipal principal) { var id = GetMicrosoftUserId(principal); var displayname = principal.GetDisplayName(); if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(displayname)) { throw new UnauthorizedAccessException("Unknown claim set."); } var user = await this.userRepository.ReadUser(id); if (user == null) { user = User.CreateMsalUser(id, displayname); await this.userRepository.CreateUser(user); } return new ClaimsPrincipal(CreateClaimsIdentity(user)); } private static bool IsMicrosoft(ClaimsPrincipal self) { return self.GetObjectId() != null; } private static string? GetMicrosoftUserId(ClaimsPrincipal self) { return self.GetObjectId(); } private static string? GetGuestUserId(ClaimsPrincipal self) { return self.GetNameIdentifierId(); } private static ClaimsIdentity CreateClaimsIdentity(User user) { var claims = new List(4) { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.DisplayName), }; if (user.LoginPlatform == WhichLoginPlatform.Guest) { claims.Add(new Claim(ClaimTypes.Role, "Guest")); return new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); } else { return new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme); } } } public interface IShogiUserClaimsTransformer : IClaimsTransformation { Task CreateClaimsFromGuestPrincipal(ClaimsPrincipal principal); }