Loading src/api/Identity/AuthenticationServiceExtensions.cs +32 −7 Original line number Diff line number Diff line using System.IdentityModel.Tokens.Jwt; using System.Net.Http.Headers; using System.Security.Claims; using System.Security.Principal; using System.Text.Json; using Cals.Authentication; using Cals.Visor.Infrastructure.Contexts; using Cals.Visor.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; Loading Loading @@ -109,7 +113,7 @@ public static class AuthenticationServiceExtensions } } AttachUserInfoClaims(context, userInfo!); await AttachUserInfoClaims(context, userInfo!); } private static async Task<Dictionary<string, object>?> FetchAndCacheUserInfoAsync( Loading Loading @@ -168,15 +172,36 @@ public static class AuthenticationServiceExtensions } } private static void AttachUserInfoClaims( private static async Task AttachUserInfoClaims( TokenValidatedContext context, Dictionary<string, object> userInfo) { var identity = (ClaimsIdentity)context.Principal!.Identity!; foreach (var kvp in userInfo) { identity.AddClaim(new Claim(kvp.Key, kvp.Value?.ToString() ?? "")); } if (context.Principal == null || context.Principal.Identity == null) throw new Exception("No principal in request"); IIdentity? identity = context.Principal.Identity; WiscEduUser oidcIdentity = new WiscEduOidcUser(context.Principal); // Extract the unique ID from the OIDC identity if (oidcIdentity.Pvi == null) throw new Exception($"OIDC unique identifier not found (expected pvi)."); // Resolve your application user service ApplicationDbContext dbContext = context.HttpContext.RequestServices .GetRequiredService<ApplicationDbContext>(); // Look up the internal user AppUser? user = await dbContext.Users.FirstOrDefaultAsync(u => u.UniqueID == oidcIdentity.Pvi); // If user does not exist, create it user ??= oidcIdentity.ToAppUser(); dbContext.Add(user); await dbContext.SaveChangesAsync(); // Add the app-specific internal user id claim if (user.AppUserId.HasValue) ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(VisorClaimTypes.Id, user.AppUserId.Value.ToString())); } private static List<Claim> GetDevClaimsForUser(string? username) Loading src/api/Identity/VisorClaimTypes.cs 0 → 100644 +6 −0 Original line number Diff line number Diff line namespace Cals.Visor.Api.Identity; public class VisorClaimTypes { public const string Id = "app_user_id"; } src/api/Identity/VisorUser.cs +8 −0 Original line number Diff line number Diff line using System.Security.Claims; using Cals.Authentication; using Cals.Visor.Api.Identity; namespace Cals.Visor.Identity; Loading @@ -13,10 +14,17 @@ public class VisorUser : WiscEduOidcUser, IWiscEduUser public VisorUser(ClaimsIdentity? identity) : base(identity) { if (identity == null) return; Claim? idClaim = identity.FindFirst(VisorClaimTypes.Id); if (idClaim != null && long.TryParse(idClaim.Value, out long id)) Id = id; } public VisorUser(ClaimsPrincipal principal) : this((ClaimsIdentity?)principal.Identity) { } public long? Id { get; protected set; } } src/api/Identity/WiscEduUserExtensions.cs 0 → 100644 +20 −0 Original line number Diff line number Diff line using Cals.Authentication; using Cals.Visor.Models; namespace Cals.Visor.Api; public static class WiscEduUserExtensions { public static AppUser ToAppUser(this WiscEduUser wiscEduUser) { return new AppUser() { DisplayName = wiscEduUser.Name, FirstName = wiscEduUser.FirstName, LastName = wiscEduUser.LastName, Email = wiscEduUser.Email, UniqueID = wiscEduUser.Pvi, Username = wiscEduUser.NetId }; } } Loading
src/api/Identity/AuthenticationServiceExtensions.cs +32 −7 Original line number Diff line number Diff line using System.IdentityModel.Tokens.Jwt; using System.Net.Http.Headers; using System.Security.Claims; using System.Security.Principal; using System.Text.Json; using Cals.Authentication; using Cals.Visor.Infrastructure.Contexts; using Cals.Visor.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; Loading Loading @@ -109,7 +113,7 @@ public static class AuthenticationServiceExtensions } } AttachUserInfoClaims(context, userInfo!); await AttachUserInfoClaims(context, userInfo!); } private static async Task<Dictionary<string, object>?> FetchAndCacheUserInfoAsync( Loading Loading @@ -168,15 +172,36 @@ public static class AuthenticationServiceExtensions } } private static void AttachUserInfoClaims( private static async Task AttachUserInfoClaims( TokenValidatedContext context, Dictionary<string, object> userInfo) { var identity = (ClaimsIdentity)context.Principal!.Identity!; foreach (var kvp in userInfo) { identity.AddClaim(new Claim(kvp.Key, kvp.Value?.ToString() ?? "")); } if (context.Principal == null || context.Principal.Identity == null) throw new Exception("No principal in request"); IIdentity? identity = context.Principal.Identity; WiscEduUser oidcIdentity = new WiscEduOidcUser(context.Principal); // Extract the unique ID from the OIDC identity if (oidcIdentity.Pvi == null) throw new Exception($"OIDC unique identifier not found (expected pvi)."); // Resolve your application user service ApplicationDbContext dbContext = context.HttpContext.RequestServices .GetRequiredService<ApplicationDbContext>(); // Look up the internal user AppUser? user = await dbContext.Users.FirstOrDefaultAsync(u => u.UniqueID == oidcIdentity.Pvi); // If user does not exist, create it user ??= oidcIdentity.ToAppUser(); dbContext.Add(user); await dbContext.SaveChangesAsync(); // Add the app-specific internal user id claim if (user.AppUserId.HasValue) ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(VisorClaimTypes.Id, user.AppUserId.Value.ToString())); } private static List<Claim> GetDevClaimsForUser(string? username) Loading
src/api/Identity/VisorClaimTypes.cs 0 → 100644 +6 −0 Original line number Diff line number Diff line namespace Cals.Visor.Api.Identity; public class VisorClaimTypes { public const string Id = "app_user_id"; }
src/api/Identity/VisorUser.cs +8 −0 Original line number Diff line number Diff line using System.Security.Claims; using Cals.Authentication; using Cals.Visor.Api.Identity; namespace Cals.Visor.Identity; Loading @@ -13,10 +14,17 @@ public class VisorUser : WiscEduOidcUser, IWiscEduUser public VisorUser(ClaimsIdentity? identity) : base(identity) { if (identity == null) return; Claim? idClaim = identity.FindFirst(VisorClaimTypes.Id); if (idClaim != null && long.TryParse(idClaim.Value, out long id)) Id = id; } public VisorUser(ClaimsPrincipal principal) : this((ClaimsIdentity?)principal.Identity) { } public long? Id { get; protected set; } }
src/api/Identity/WiscEduUserExtensions.cs 0 → 100644 +20 −0 Original line number Diff line number Diff line using Cals.Authentication; using Cals.Visor.Models; namespace Cals.Visor.Api; public static class WiscEduUserExtensions { public static AppUser ToAppUser(this WiscEduUser wiscEduUser) { return new AppUser() { DisplayName = wiscEduUser.Name, FirstName = wiscEduUser.FirstName, LastName = wiscEduUser.LastName, Email = wiscEduUser.Email, UniqueID = wiscEduUser.Pvi, Username = wiscEduUser.NetId }; } }