From c0a22864a957446ab196e61d284fdba1f4b2d8f5 Mon Sep 17 00:00:00 2001 From: Arthur Rosa Date: Thu, 21 May 2026 14:31:19 -0700 Subject: [PATCH] Retiring the last bits of Auth0 --- .env.example | 5 - .github/copilot-instructions.md | 5 +- Jobs/BackfillUsersFromAuth0Job.cs | 191 ---------------------------- LOCAL_DEVELOPMENT.md | 6 +- Program.cs | 51 +------- Services/JobRunnerService.cs | 3 +- Services/MailchimpWebhookService.cs | 156 +---------------------- gatool-api.csproj | 2 - scripts/create-secrets.sh | 4 - scripts/rename-secrets.sh | 4 - 10 files changed, 15 insertions(+), 412 deletions(-) delete mode 100644 Jobs/BackfillUsersFromAuth0Job.cs diff --git a/.env.example b/.env.example index f5a6982..e522dfc 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,5 @@ # This example is for local development without Azure DISABLE_REDIS=true -SECRET_Auth0Issuer= -SECRET_Auth0Audience= -SECRET_Auth0AdminClientId= -SECRET_Auth0AdminClientId= -SECRET_Auth0AdminClientSecret= SECRET_FRCApiKey="Basic KEY" SECRET_FRCCurrentSeason=2025 SECRET_MailchimpAPIKey= diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c4979ab..faff76f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -47,7 +47,7 @@ GATool API is a .NET 10 API service that provides game announcer data for FIRST ``` ### Secrets Management -- Secrets are stored in **AWS Secrets Manager** with plain names (no prefix): `Auth0Issuer`, `FRCApiKey`, `TBAApiKey`, etc. +- Secrets are stored in **AWS Secrets Manager** with plain names (no prefix): `FRCApiKey`, `TBAApiKey`, etc. - `Program.cs` preloads all secrets at startup via `AwsSecretProvider.PreloadSecretsAsync()` - `GetSecret()` and `GetSecretAsync()` calls throughout the codebase use these plain names - `NEW_RELIC_LICENSE_KEY` is injected as an ECS container secret (from Secrets Manager → env var), not loaded by the app @@ -119,7 +119,7 @@ aws ecs describe-services --cluster gatool --services gatool-api --profile gatoo ## Common Patterns ### Authentication/Authorization -- JWT-based auth using Auth0 +- JWT-based auth using the in-house `GatoolJwt` bearer scheme (see `Program.cs`) - Role-based access control via policies: - "user": Basic access - "admin": Administrative functions @@ -151,7 +151,6 @@ public async Task GetExample() - The Blue Alliance API - Statbotics.io - FTC Scout -- Auth0 (authentication) - AWS Secrets Manager (secrets) - AWS S3 (storage for user data, high scores, team updates) - Redis (caching, runs as ECS sidecar) diff --git a/Jobs/BackfillUsersFromAuth0Job.cs b/Jobs/BackfillUsersFromAuth0Job.cs deleted file mode 100644 index d651a66..0000000 --- a/Jobs/BackfillUsersFromAuth0Job.cs +++ /dev/null @@ -1,191 +0,0 @@ -using Auth0.AuthenticationApi; -using Auth0.AuthenticationApi.Models; -using Auth0.Core.Exceptions; -using Auth0.ManagementApi; -using GAToolAPI.Services; -using GAToolAPI.Services.Auth; - -namespace GAToolAPI.Jobs; - -/// -/// One-shot migration job: enumerates every user in Auth0 (email connection only) -/// and upserts them into the new gatool-auth DynamoDB table with the -/// equivalent role set. Designed to be run once when cutting over from Auth0 -/// to the self-hosted auth system, but is idempotent — re-running it just -/// re-syncs roles for any users whose Auth0 roles have changed since the -/// last run. -/// -/// Role mapping (Auth0 → gatool): -/// - Any role whose name contains "admin" (case-insensitive) → grants "admin" + "user" -/// - The "Full User" role (id rol_KRLODHx3eNItUgvI) → grants "user" -/// - Any role whose name contains "user" (case-insensitive) → grants "user" -/// - Read-only / no relevant roles → user is upserted with an empty role array -/// (they exist in DDB but won't satisfy the [user] / [admin] authorization -/// policies, matching their current Auth0 behavior). -/// -/// Run with: dotnet run -- --job BackfillUsersFromAuth0 -/// Dry run: DryRun=true dotnet run -- --job BackfillUsersFromAuth0 -/// -public class BackfillUsersFromAuth0Job( - ILogger logger, - IConfiguration configuration, - ISecretProvider secretProvider, - AuthRepository authRepository) : IJob -{ - private const string Auth0Domain = "gatool.auth0.com"; - private const string FullUserRoleId = "rol_KRLODHx3eNItUgvI"; - private const int PageSize = 50; - // Auth0 free tier limits the Management API to ~2 req/s. We throttle every - // call and back off on 429 to stay well under that. - private static readonly TimeSpan PerCallDelay = TimeSpan.FromMilliseconds(600); - private static readonly TimeSpan RateLimitBackoff = TimeSpan.FromSeconds(10); - private const int MaxRetriesPerCall = 5; - - public async Task ExecuteAsync(CancellationToken cancellationToken = default) - { - var isDryRun = configuration.GetValue("DryRun"); - var prefix = isDryRun ? "[DRY RUN] " : ""; - logger.LogInformation("{Prefix}Starting BackfillUsersFromAuth0 job", prefix); - - // Mint a Management API token using the same client credentials the - // Mailchimp webhook uses for its dual-write. - var clientId = await secretProvider.GetSecretAsync("Auth0AdminClientId", cancellationToken); - var clientSecret = await secretProvider.GetSecretAsync("Auth0AdminClientSecret", cancellationToken); - - var authClient = new AuthenticationApiClient(Auth0Domain); - var tokenResp = await authClient.GetTokenAsync(new ClientCredentialsTokenRequest - { - ClientId = clientId, - ClientSecret = clientSecret, - Audience = $"https://{Auth0Domain}/api/v2/" - }, cancellationToken); - var mgmt = new ManagementApiClient(tokenResp.AccessToken, Auth0Domain); - - // Strategy: instead of N+1 calls (1 list + GetRoles per user), we list - // roles once and then page through each role's *members*. That's - // O(roles × pages-of-members) calls — typically a few dozen — versus - // thousands. Auth0 free tier is 2 req/s so the per-user approach was - // hopelessly slow even with throttling. - var allRoles = await CallWithRetryAsync(() => mgmt.Roles.GetAllAsync( - new Auth0.ManagementApi.Models.GetRolesRequest(), - new Auth0.ManagementApi.Paging.PaginationInfo(0, 50, includeTotals: false), - cancellationToken), cancellationToken); - logger.LogInformation("Found {Count} Auth0 roles: [{Names}]", - allRoles.Count, string.Join(", ", allRoles.Select(r => r.Name))); - - // email → set of gatool roles, accumulated across role lookups. - var emailToRoles = new Dictionary>(StringComparer.OrdinalIgnoreCase); - - foreach (var role in allRoles) - { - var name = role.Name ?? ""; - string? mappedRole = null; - bool grantsUser = false; - if (name.Contains("admin", StringComparison.OrdinalIgnoreCase)) - { - mappedRole = "admin"; - grantsUser = true; - } - else if (role.Id == FullUserRoleId || name.Contains("user", StringComparison.OrdinalIgnoreCase)) - { - mappedRole = "user"; - } - - if (mappedRole == null) - { - logger.LogInformation("Role '{Name}' ({Id}): not mapped; skipping members", name, role.Id); - continue; - } - - int memberPage = 0; - int memberCount = 0; - while (true) - { - var members = await CallWithRetryAsync(() => mgmt.Roles.GetUsersAsync(role.Id, - new Auth0.ManagementApi.Paging.PaginationInfo(memberPage, PageSize, includeTotals: false), - cancellationToken), cancellationToken); - if (members.Count == 0) break; - foreach (var m in members) - { - if (string.IsNullOrWhiteSpace(m.Email)) continue; - if (!emailToRoles.TryGetValue(m.Email, out var set)) - emailToRoles[m.Email] = set = new HashSet(StringComparer.OrdinalIgnoreCase); - set.Add(mappedRole); - if (grantsUser) set.Add("user"); - memberCount++; - } - memberPage++; - } - logger.LogInformation("Role '{Name}' → {Mapped}: {Count} members", name, mappedRole, memberCount); - } - - logger.LogInformation("Collected {Count} unique users with mapped roles; upserting to DDB", - emailToRoles.Count); - - int totalCreated = 0, totalUpdated = 0, totalUnchanged = 0, totalFailed = 0; - foreach (var (email, roleSet) in emailToRoles) - { - cancellationToken.ThrowIfCancellationRequested(); - var roles = roleSet.OrderBy(r => r).ToArray(); - try - { - if (isDryRun) - { - logger.LogInformation("{Prefix}Would upsert {Email} with roles [{Roles}]", - prefix, email, string.Join(",", roles)); - continue; - } - - var existing = await authRepository.GetUserAsync(email, cancellationToken); - if (existing == null) - { - await authRepository.UpsertUserAsync(email, roles, cancellationToken); - totalCreated++; - logger.LogInformation("Created {Email} with roles [{Roles}]", - email, string.Join(",", roles)); - } - else if (!existing.Roles.OrderBy(r => r).SequenceEqual(roles)) - { - await authRepository.SetRolesAsync(email, roles, cancellationToken); - totalUpdated++; - logger.LogInformation("Updated {Email} roles [{Old}] → [{New}]", - email, string.Join(",", existing.Roles), string.Join(",", roles)); - } - else - { - totalUnchanged++; - } - } - catch (Exception ex) - { - totalFailed++; - logger.LogError(ex, "Backfill failed for {Email}", email); - } - } - - logger.LogInformation( - "{Prefix}Backfill complete. users={Users} created={Created} updated={Updated} unchanged={Unchanged} failed={Failed}", - prefix, emailToRoles.Count, totalCreated, totalUpdated, totalUnchanged, totalFailed); - } - - private async Task CallWithRetryAsync(Func> call, CancellationToken ct) - { - // Pre-throttle every call so we stay under 2 req/s on Auth0's free tier. - await Task.Delay(PerCallDelay, ct); - for (int attempt = 1; ; attempt++) - { - try - { - return await call(); - } - catch (RateLimitApiException) when (attempt < MaxRetriesPerCall) - { - var backoff = TimeSpan.FromSeconds(RateLimitBackoff.TotalSeconds * attempt); - logger.LogWarning("Auth0 rate-limited (attempt {Attempt}/{Max}); backing off {Backoff}s", - attempt, MaxRetriesPerCall, backoff.TotalSeconds); - await Task.Delay(backoff, ct); - } - } - } - -} diff --git a/LOCAL_DEVELOPMENT.md b/LOCAL_DEVELOPMENT.md index 302d913..745e905 100644 --- a/LOCAL_DEVELOPMENT.md +++ b/LOCAL_DEVELOPMENT.md @@ -97,8 +97,6 @@ The application retrieves these secrets from AWS Secrets Manager (prefix: `gatoo | Secret Name | Description | Required For | |-------------------------------|-----------------------------|-------------------------| -| `gatool/Auth0Issuer` | Auth0 domain URL | Authentication | -| `gatool/Auth0Audience` | Auth0 API audience | Authentication | | `gatool/FRCApiKey` | FIRST API key | FRC data endpoints | | `gatool/TBAApiKey` | The Blue Alliance API key | TBA/offseason endpoints | | `gatool/FTCApiKey` | FTC API key | FTC data endpoints | @@ -109,8 +107,6 @@ The application retrieves these secrets from AWS Secrets Manager (prefix: `gatoo | `gatool/MailChimpAPIKey` | MailChimp API key | User sync | | `gatool/MailchimpAPIURL` | MailChimp API URL | User sync | | `gatool/MailchimpListID` | MailChimp list ID | User sync | -| `gatool/Auth0AdminClientId` | Auth0 Management client ID | User sync | -| `gatool/Auth0AdminClientSecret` | Auth0 Management secret | User sync | | `gatool/NewRelicLicenseKey` | New Relic license key | Monitoring | To create all secrets at once, use the provided script: @@ -121,7 +117,7 @@ AWS_REGION=us-east-2 ./scripts/create-secrets.sh ## Testing Without Authentication -Some endpoints require authentication. To test without setting up Auth0: +Some endpoints require authentication. To test without a signed-in user: ### Option 1: Test Public Endpoints diff --git a/Program.cs b/Program.cs index f1125f2..125b858 100644 --- a/Program.cs +++ b/Program.cs @@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.StackExchangeRedis; -using Microsoft.IdentityModel.Tokens; using NewRelic.LogEnrichers.Serilog; using NSwag; using NSwag.Generation.Processors.Security; @@ -47,7 +46,6 @@ var smClient = new AmazonSecretsManagerClient(); var secretNames = new[] { - "Auth0Issuer", "Auth0Audience", // kept during migration window for legacy token validation "FRCApiKey", "TBAApiKey", "FTCApiKey", "CasterstoolApiKey", "TOAApiKey", "FRCCurrentSeason", "FTCCurrentSeason", "MailChimpAPIKey", "MailchimpAPIURL", "MailchimpListID", @@ -73,8 +71,7 @@ builder.Services.AddAuthentication(options => { - // Default scheme is our self-issued ES256 JWT. We also keep the Auth0 scheme - // around during the migration window (see AuthenticationSchemes on policies). + // Self-issued ES256 JWT is the only accepted scheme. options.DefaultAuthenticateScheme = "GatoolJwt"; options.DefaultChallengeScheme = "GatoolJwt"; }) @@ -84,39 +81,6 @@ // Configured asynchronously below once the DI container is built. options.RequireHttpsMetadata = false; options.SaveToken = true; - // During the Auth0 migration window we accept tokens from two issuers. - // Without forwarding, every request would be tried against BOTH schemes, - // producing noisy "kid not found" log entries from the scheme that doesn't - // own the token. Forward to the Auth0 scheme when we see an Auth0-issued - // token so each token is validated exactly once by the right handler. - options.ForwardDefaultSelector = ctx => - { - var auth = ctx.Request.Headers.Authorization.ToString(); - if (string.IsNullOrEmpty(auth) || !auth.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return null; - var token = auth["Bearer ".Length..].Trim(); - var dot1 = token.IndexOf('.'); - var dot2 = dot1 < 0 ? -1 : token.IndexOf('.', dot1 + 1); - if (dot2 < 0) return null; - try - { - var payload = token.Substring(dot1 + 1, dot2 - dot1 - 1); - var json = System.Text.Encoding.UTF8.GetString(Base64UrlEncoder.DecodeBytes(payload)); - using var doc = System.Text.Json.JsonDocument.Parse(json); - if (doc.RootElement.TryGetProperty("iss", out var iss)) - { - var issuer = iss.GetString(); - var auth0Issuer = ctx.RequestServices.GetRequiredService().GetSecret("Auth0Issuer"); - if (!string.IsNullOrEmpty(issuer) && !string.IsNullOrEmpty(auth0Issuer) && - issuer.TrimEnd('/').Equals(auth0Issuer.TrimEnd('/'), StringComparison.OrdinalIgnoreCase)) - { - return "Auth0"; - } - } - } - catch { /* malformed token — let the default scheme reject it */ } - return null; - }; options.Events = new JwtBearerEvents { OnMessageReceived = async ctx => @@ -130,23 +94,17 @@ } } }; - }) - .AddJwtBearer("Auth0", options => - { - // Legacy Auth0 tokens — accepted during migration window. - options.Authority = secretProvider.GetSecret("Auth0Issuer"); - options.Audience = secretProvider.GetSecret("Auth0Audience"); }); builder.Services.AddAuthorizationBuilder() .AddPolicy("user", policy => { - policy.AuthenticationSchemes = ["GatoolJwt", "Auth0"]; + policy.AuthenticationSchemes = ["GatoolJwt"]; policy.Requirements.Add(new HasRoleRequirement("user")); }) .AddPolicy("admin", policy => { - policy.AuthenticationSchemes = ["GatoolJwt", "Auth0"]; + policy.AuthenticationSchemes = ["GatoolJwt"]; policy.Requirements.Add(new HasRoleRequirement("admin")); }); builder.Services.AddSingleton(); @@ -157,7 +115,7 @@ builder.Services.AddAWSService(); builder.Services.AddAWSService(); - // Custom auth services (email OTP + WebAuthn passkeys, replaces Auth0) + // Custom auth services (email OTP + WebAuthn passkeys) builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -332,7 +290,6 @@ // Register job services builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); var app = builder.Build(); diff --git a/Services/JobRunnerService.cs b/Services/JobRunnerService.cs index 17acdef..b149a8b 100644 --- a/Services/JobRunnerService.cs +++ b/Services/JobRunnerService.cs @@ -6,8 +6,7 @@ public class JobRunnerService(IServiceProvider serviceProvider, ILogger _availableJobs = new() { - { "UpdateGlobalHighScores", typeof(UpdateGlobalHighScoresJob) }, - { "BackfillUsersFromAuth0", typeof(BackfillUsersFromAuth0Job) } + { "UpdateGlobalHighScores", typeof(UpdateGlobalHighScoresJob) } }; public async Task RunJobAsync(string jobName, CancellationToken cancellationToken = default) diff --git a/Services/MailchimpWebhookService.cs b/Services/MailchimpWebhookService.cs index ceb39dd..859bbe6 100644 --- a/Services/MailchimpWebhookService.cs +++ b/Services/MailchimpWebhookService.cs @@ -1,8 +1,4 @@ using System.Threading.RateLimiting; -using Auth0.AuthenticationApi; -using Auth0.AuthenticationApi.Models; -using Auth0.ManagementApi; -using Auth0.ManagementApi.Models; using GAToolAPI.Services.Auth; using MailChimp.Net; using MailChimp.Net.Core; @@ -10,25 +6,14 @@ namespace GAToolAPI.Services; /// -/// Handles Mailchimp subscribe/unsubscribe/profile webhooks. -/// -/// During the Auth0 -> custom-auth migration we mirror role/user changes -/// to BOTH systems so existing Auth0-issued tokens (still in flight) and -/// the new gatool-issued tokens see consistent role state. Once every -/// client has cut over to the new auth flow, the Auth0 side can be -/// removed (along with the Auth0 packages and admin secrets) by the -/// `cleanup-auth0` task. +/// Handles Mailchimp subscribe/unsubscribe/profile webhooks and mirrors the +/// resulting role state into the gatool auth user store (DynamoDB). /// public class MailchimpWebhookService { private const string OptInText = "I want access to gatool and agree that I will not abuse this access to team data."; - // Auth0 (legacy, kept until cutover) - private const string Auth0Domain = "gatool.auth0.com"; - private const string FullUserRoleId = "rol_KRLODHx3eNItUgvI"; - private const string ReadOnlyRoleId = "rol_EQcREtmOWaGanRYG"; - private const string WelcomeTag = "gatool-welcome"; private readonly ILogger _logger; @@ -46,27 +31,9 @@ public class MailchimpWebhookService QueueLimit = int.MaxValue }); - private readonly SlidingWindowRateLimiter _auth0RateLimiter = new(new SlidingWindowRateLimiterOptions - { - AutoReplenishment = true, - Window = TimeSpan.FromSeconds(1), - PermitLimit = 2, - QueueProcessingOrder = QueueProcessingOrder.OldestFirst, - SegmentsPerWindow = 1, - QueueLimit = int.MaxValue - }); - private MailChimpManager? _mailChimpClient; private string? _mailChimpListId; - // Auth0 management client — lazy-initialized & token-refreshed - private ManagementApiClient? _auth0Client; - private DateTimeOffset _auth0TokenExpiresAt = DateTimeOffset.MinValue; - private string? _auth0ClientId; - private string? _auth0ClientSecret; - private readonly SemaphoreSlim _auth0TokenLock = new(1, 1); - private static readonly TimeSpan Auth0TokenRefreshSkew = TimeSpan.FromMinutes(5); - public MailchimpWebhookService( ILogger logger, ISecretProvider secretProvider, @@ -85,7 +52,6 @@ public async Task HandleEventAsync(string eventType, string email, string? gatoo _logger.LogInformation("Processing Mailchimp webhook: type={EventType}, email={Email}", eventType, email); await EnsureClientsInitializedAsync(cancellationToken); - await EnsureAuth0TokenFreshAsync(cancellationToken); switch (eventType) { @@ -110,10 +76,9 @@ private async Task HandleSubscribeOrProfileAsync(string email, string? gatoolMer { var isOptedIn = gatoolMergeField == OptInText; - // === New auth (DynamoDB) === // "Opted in" gets the full "user" role (read + write). - // "Not opted in" gets no roles (record exists but no API access — equivalent - // to the previous Auth0 read-only role, since all gated endpoints require "user"). + // "Not opted in" gets no roles (record exists but no API access — all + // gated endpoints require "user"). var roles = isOptedIn ? new[] { "user" } : Array.Empty(); var existing = await _authRepository.GetUserAsync(email, cancellationToken); @@ -130,44 +95,9 @@ private async Task HandleSubscribeOrProfileAsync(string email, string? gatoolMer _logger.LogInformation("Updated roles for {Email} to [{Roles}]", email, string.Join(",", roles)); } - // === Legacy auth (Auth0) — mirror until cutover === - bool auth0Created = false; - try - { - var (user, created) = await GetOrCreateAuth0UserAsync(email, cancellationToken); - auth0Created = created; - if (isOptedIn) - { - _logger.LogInformation("Auth0: assigning full user role to {Email}", email); - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - await _auth0Client!.Users.AssignRolesAsync(user.UserId, new AssignRolesRequest - { - Roles = [FullUserRoleId] - }, cancellationToken); - } - else - { - _logger.LogInformation("Auth0: assigning read-only role to {Email}", email); - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - await _auth0Client!.Users.AssignRolesAsync(user.UserId, new AssignRolesRequest - { - Roles = [ReadOnlyRoleId] - }, cancellationToken); - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - await _auth0Client!.Users.RemoveRolesAsync(user.UserId, new AssignRolesRequest - { - Roles = [FullUserRoleId] - }, cancellationToken); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Auth0 mirror failed for {Email} during subscribe/profile — continuing", email); - } - - // Tag for welcome only the first time we see this user in EITHER system, - // so resubscribers don't get re-welcomed. - if (newAuthCreated || auth0Created) + // Tag for welcome only the first time we see this user, so resubscribers + // don't get re-welcomed. + if (newAuthCreated) { await TagSubscriberForWelcomeAsync(email, cancellationToken); } @@ -175,49 +105,8 @@ private async Task HandleSubscribeOrProfileAsync(string email, string? gatoolMer private async Task HandleUnsubscribeAsync(string email, CancellationToken cancellationToken) { - // New auth await _authRepository.DeleteUserAsync(email, cancellationToken); _logger.LogInformation("Deleted auth account for {Email}", email); - - // Legacy auth (Auth0) — mirror until cutover - try - { - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - var users = (await _auth0Client!.Users.GetUsersByEmailAsync(email, - cancellationToken: cancellationToken)) - .Where(u => u.Identities.Any(i => i.Provider == "email")).ToList(); - - foreach (var user in users) - { - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - await _auth0Client.Users.DeleteAsync(user.UserId); - _logger.LogInformation("Deleted Auth0 account for {Email}", email); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Auth0 mirror failed for {Email} during unsubscribe — continuing", email); - } - } - - private async Task<(User user, bool created)> GetOrCreateAuth0UserAsync(string email, - CancellationToken cancellationToken) - { - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - var existing = (await _auth0Client!.Users.GetUsersByEmailAsync(email.ToLowerInvariant(), - cancellationToken: cancellationToken)) - .Where(u => u.Identities.Any(i => i.Provider == "email")).ToList(); - - if (existing.Count >= 1) - return (existing.First(), false); - - await _auth0RateLimiter.AcquireAsync(cancellationToken: cancellationToken); - var newUser = await _auth0Client.Users.CreateAsync(new UserCreateRequest - { - Connection = "email", Email = email, EmailVerified = true - }, cancellationToken); - - return (newUser, true); } private async Task TagSubscriberForWelcomeAsync(string email, CancellationToken cancellationToken) @@ -244,8 +133,6 @@ private async Task EnsureClientsInitializedAsync(CancellationToken cancellationT { if (_mailChimpClient != null) return; - _auth0ClientId = await _secretProvider.GetSecretAsync("Auth0AdminClientId", cancellationToken); - _auth0ClientSecret = await _secretProvider.GetSecretAsync("Auth0AdminClientSecret", cancellationToken); var mailChimpApiKey = await _secretProvider.GetSecretAsync("MailChimpAPIKey", cancellationToken); var mailChimpApiUrl = await _secretProvider.GetSecretAsync("MailchimpAPIURL", cancellationToken); _mailChimpListId = await _secretProvider.GetSecretAsync("MailchimpListID", cancellationToken); @@ -256,33 +143,4 @@ private async Task EnsureClientsInitializedAsync(CancellationToken cancellationT DataCenter = mailChimpApiUrl }); } - - private async Task EnsureAuth0TokenFreshAsync(CancellationToken cancellationToken) - { - if (_auth0Client != null && DateTimeOffset.UtcNow < _auth0TokenExpiresAt - Auth0TokenRefreshSkew) - return; - - await _auth0TokenLock.WaitAsync(cancellationToken); - try - { - if (_auth0Client != null && DateTimeOffset.UtcNow < _auth0TokenExpiresAt - Auth0TokenRefreshSkew) - return; - - _logger.LogInformation("Refreshing Auth0 management API token"); - var authClient = new AuthenticationApiClient(Auth0Domain); - var token = await authClient.GetTokenAsync(new ClientCredentialsTokenRequest - { - Audience = $"https://{Auth0Domain}/api/v2/", - ClientId = _auth0ClientId!, - ClientSecret = _auth0ClientSecret! - }, cancellationToken); - - _auth0Client = new ManagementApiClient(token.AccessToken, Auth0Domain); - _auth0TokenExpiresAt = DateTimeOffset.UtcNow.AddSeconds(token.ExpiresIn); - } - finally - { - _auth0TokenLock.Release(); - } - } } diff --git a/gatool-api.csproj b/gatool-api.csproj index 629e00d..b9294d7 100644 --- a/gatool-api.csproj +++ b/gatool-api.csproj @@ -14,8 +14,6 @@ - - diff --git a/scripts/create-secrets.sh b/scripts/create-secrets.sh index acfdc2c..59d106a 100755 --- a/scripts/create-secrets.sh +++ b/scripts/create-secrets.sh @@ -14,8 +14,6 @@ VAULT_NAME="GAToolApiKeys" REGION="${AWS_REGION:-us-east-2}" SECRETS=( - "Auth0Issuer" - "Auth0Audience" "FRCApiKey" "TBAApiKey" "FTCApiKey" @@ -26,8 +24,6 @@ SECRETS=( "MailChimpAPIKey" "MailchimpAPIURL" "MailchimpListID" - "Auth0AdminClientId" - "Auth0AdminClientSecret" "NewRelicLicenseKey" ) diff --git a/scripts/rename-secrets.sh b/scripts/rename-secrets.sh index 262e156..9cc45ec 100755 --- a/scripts/rename-secrets.sh +++ b/scripts/rename-secrets.sh @@ -10,8 +10,6 @@ REGION="${AWS_REGION:-us-east-2}" PROFILE="${AWS_PROFILE:-gatool}" SECRETS=( - "Auth0Issuer" - "Auth0Audience" "FRCApiKey" "TBAApiKey" "FTCApiKey" @@ -22,8 +20,6 @@ SECRETS=( "MailChimpAPIKey" "MailchimpAPIURL" "MailchimpListID" - "Auth0AdminClientId" - "Auth0AdminClientSecret" "NewRelicLicenseKey" )