Initial import into Gitea

This commit is contained in:
Grae Jones
2026-03-14 13:50:09 -07:00
parent 8e7e03702e
commit 34c1f09e01
154 changed files with 17666 additions and 1548 deletions

View File

@@ -248,11 +248,26 @@ public sealed class MultiProviderAuthMiddleware
}
else
{
// Standard Entra ID
// Standard Entra ID — could be CIAM tenant or Staff tenant (Tech, Admin)
// Detect by comparing issuer against configured Staff tenant ID
var staffTenantId = _config["Auth:Microsoft:StaffTenantId"];
var staffClientId = _config["Auth:Microsoft:StaffClientId"];
var isStaff = !string.IsNullOrWhiteSpace(staffTenantId) &&
jwt.Issuer.Contains(staffTenantId, StringComparison.OrdinalIgnoreCase);
if (isStaff)
{
tenantId = staffTenantId!;
clientId = staffClientId ?? clientId;
_logger.LogWarning("[Auth] Staff Entra token detected | tenant={Tenant} | Corr={Corr}", tenantId, corrId);
clientContext.IsStaff = true;
}
authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
metadataAddress = $"{authority}/.well-known/openid-configuration";
validIssuers = new[]
{
validIssuers = new[]
{
$"https://login.microsoftonline.com/{tenantId}/v2.0",
$"https://sts.windows.net/{tenantId}/"
};
@@ -342,9 +357,17 @@ public sealed class MultiProviderAuthMiddleware
/// </summary>
private static void ExtractClaims(ClaimsPrincipal principal, ClientContext clientContext)
{
// Always extract oid explicitly — used for activity logging and identity.
// For standard Entra access tokens oid may be under the full claim URI.
var oid = principal.FindFirstValue("oid")
?? principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
clientContext.EntraOid = oid;
// ClientId: prefer oid, fall back to sub
clientContext.ClientId =
principal.FindFirstValue("oid") ?? // Microsoft object ID
principal.FindFirstValue("sub") ?? // Standard subject
oid ??
principal.FindFirstValue("sub") ??
principal.FindFirstValue(ClaimTypes.NameIdentifier);
clientContext.Email =
@@ -389,7 +412,9 @@ public sealed class MultiProviderAuthMiddleware
try
{
var rqst = JsonSerializer.Serialize(new { sessionToken = token });
var resp = await sql.ExecProcAsync("dbo.spSession", "validate", rqst, ct: context.RequestAborted);
var sessionProc = "dbo.spClientSession"; // Gateway handles CIAM client sessions only
var resp = await sql.ExecProcAsync(sessionProc, "validate", rqst, ct: context.RequestAborted);
if (string.IsNullOrWhiteSpace(resp))
{
@@ -412,8 +437,22 @@ public sealed class MultiProviderAuthMiddleware
clientContext.Role = data.TryGetProperty("role", out var role) ? role.GetString() : null;
clientContext.IsDevBypass = false;
_logger.LogWarning("[Auth] Session validated OK | ClientId={ClientId} Email={Email} | Corr={Corr}",
clientContext.ClientId, clientContext.Email, corrId);
// TenantId: session data first, then X-Tenant-Id header fallback
// (In agency model, this is the client's Google Ads customer ID)
clientContext.TenantId =
data.TryGetProperty("tenantId", out var tenId) ? tenId.GetString() :
data.TryGetProperty("googleCustomerId", out var gcid) ? gcid.GetString() :
null;
// Fall back to X-Tenant-Id header if not in session data
if (string.IsNullOrWhiteSpace(clientContext.TenantId) &&
context.Request.Headers.TryGetValue("X-Tenant-Id", out var tenantHeader))
{
clientContext.TenantId = tenantHeader.FirstOrDefault();
}
_logger.LogWarning("[Auth] Session validated OK | ClientId={ClientId} Email={Email} IsAdmin={IsAdmin} | Corr={Corr}",
clientContext.ClientId, clientContext.Email, clientContext.IsAdmin, corrId);
return clientContext.IsAuthenticated;
}