fix(auth): resolve IAM client ID from two-axis matrix (iam env × agos env)
All checks were successful
Stuffle/agos-client/pipeline/head This commit looks good
All checks were successful
Stuffle/agos-client/pipeline/head This commit looks good
The flat _IAM_CONFIGS map conflated "which IAM server" with "which agOS deployment", making it impossible to represent all 3 valid scenarios: - local agOS + local IAM - local agOS + prod IAM - prod agOS + prod IAM Split into _IAM_ISSUERS (issuer by IAM env) and _IAM_CLIENT_IDS (2D matrix keyed by [iamEnv][agosEnv]). agOS env is auto-detected from import.meta.env.MODE so no new env vars are needed. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
18
.env.example
18
.env.example
@@ -25,14 +25,18 @@ VITE_API_URL=http://localhost:2000 # local | prod: https://agos.armco.dev
|
||||
# IAM is deployed at https://iam.armco.dev
|
||||
# Issuer claim in JWTs: stuffle-iam | Audience for AGOS API: stuffle-api
|
||||
#
|
||||
# SPA clients registered (frontend only — no secret, PKCE):
|
||||
# local IAM (localhost:5000): client_53ab7b22d1d845d5a435b21e9e0b15e1 → http://localhost:2001/callback
|
||||
# remote dev IAM: client_5bb63e4ae1644c9d97ffc84a5d228c7c → http://localhost:2001/callback
|
||||
# prod: client_2dbeb454ddf14203b092eb4636a8e3d7 → https://agos.stuffle.ai/callback
|
||||
# Client ID selection uses TWO axes (resolved automatically in iam.ts):
|
||||
# 1. VITE_IAM_PROVIDER_ENV — which IAM server ("local" | "prod", default "prod")
|
||||
# 2. import.meta.env.MODE — "development" (vite dev) or "production" (vite build)
|
||||
#
|
||||
# Set VITE_IAM_PROVIDER_ENV to select which IAM to use:
|
||||
# "local" → http://localhost:5000 + local client
|
||||
# "prod" → https://iam.armco.dev + prod/dev client (default when unset)
|
||||
# Registered SPA clients (frontend only — no secret, PKCE):
|
||||
# IAM local + agOS local → client_53ab7b22d1d845d5a435b21e9e0b15e1 (localhost:2001/callback)
|
||||
# IAM prod + agOS local → client_5bb63e4ae1644c9d97ffc84a5d228c7c (localhost:2001/callback)
|
||||
# IAM prod + agOS prod → client_2dbeb454ddf14203b092eb4636a8e3d7 (agos.stuffle.ai/callback)
|
||||
#
|
||||
# Set VITE_IAM_PROVIDER_ENV to select which IAM server to talk to:
|
||||
# "local" → http://localhost:5000 (for local IAM dev)
|
||||
# "prod" → https://iam.armco.dev (default when unset)
|
||||
VITE_IAM_PROVIDER_ENV=prod
|
||||
VITE_IAM_AUDIENCE=stuffle-api
|
||||
# Optional when IAM expects OAuth resource indicators in addition to audience.
|
||||
|
||||
@@ -20,24 +20,48 @@ import { createStuffleIAMClient } from '@armco/iam-client'
|
||||
import { DEV_BYPASS_ACTIVE as _devBypassActive } from './dev-bypass'
|
||||
import { recordAuthTelemetry } from './authTelemetry'
|
||||
|
||||
const _IAM_CONFIGS = {
|
||||
local: {
|
||||
issuer: 'http://localhost:5000',
|
||||
clientId: 'client_53ab7b22d1d845d5a435b21e9e0b15e1',
|
||||
},
|
||||
prod: {
|
||||
issuer: 'https://iam.armco.dev',
|
||||
clientId: 'client_2dbeb454ddf14203b092eb4636a8e3d7',
|
||||
},
|
||||
// ── IAM issuer lookup (which IAM server) ────────────────────────────────────
|
||||
const _IAM_ISSUERS = {
|
||||
local: 'http://localhost:5000',
|
||||
prod: 'https://iam.armco.dev',
|
||||
} as const
|
||||
|
||||
// ── Client ID matrix: [iamEnv][agosEnv] ─────────────────────────────────────
|
||||
// Each IAM server has separate registered clients for "agOS running locally"
|
||||
// vs "agOS running in production" (different redirect URIs).
|
||||
//
|
||||
// iamEnv × agosEnv → registered client
|
||||
// local × local → "agOS local" in local IAM (redirect: localhost:2001)
|
||||
// prod × local → "agOS local" in prod IAM (redirect: localhost:2001)
|
||||
// prod × prod → "agOS" in prod IAM (redirect: agos.stuffle.ai)
|
||||
// local × prod → not a real scenario (prod deploy never talks to local IAM)
|
||||
const _IAM_CLIENT_IDS: Record<string, Record<string, string>> = {
|
||||
local: {
|
||||
local: 'client_53ab7b22d1d845d5a435b21e9e0b15e1',
|
||||
},
|
||||
prod: {
|
||||
local: 'client_5bb63e4ae1644c9d97ffc84a5d228c7c',
|
||||
prod: 'client_2dbeb454ddf14203b092eb4636a8e3d7',
|
||||
},
|
||||
}
|
||||
|
||||
// VITE_IAM_PROVIDER_ENV selects which IAM server to talk to
|
||||
const _iamProviderEnv = (import.meta.env.VITE_IAM_PROVIDER_ENV as string | undefined) ?? 'prod'
|
||||
if (_iamProviderEnv !== 'local' && _iamProviderEnv !== 'prod') {
|
||||
console.warn(`[iam] Unrecognized VITE_IAM_PROVIDER_ENV="${_iamProviderEnv}". Falling back to "prod".`)
|
||||
}
|
||||
const _iamConfig = _iamProviderEnv === 'local' ? _IAM_CONFIGS.local : _IAM_CONFIGS.prod
|
||||
const _issuer = _iamConfig.issuer
|
||||
const _clientId = _iamConfig.clientId
|
||||
// import.meta.env.MODE is "development" during `vite dev`, "production" during `vite build`
|
||||
const _agosEnv = import.meta.env.MODE === 'development' ? 'local' : 'prod'
|
||||
const _resolvedIamEnv = _iamProviderEnv === 'local' ? 'local' : 'prod'
|
||||
const _issuer = _IAM_ISSUERS[_resolvedIamEnv]
|
||||
const _clientId = _IAM_CLIENT_IDS[_resolvedIamEnv]?.[_agosEnv] ?? ''
|
||||
|
||||
if (_clientId === '') {
|
||||
console.warn(
|
||||
`[iam] No client ID for iamEnv="${_resolvedIamEnv}" + agosEnv="${_agosEnv}". ` +
|
||||
'This combination may not be registered. Check _IAM_CLIENT_IDS in iam.ts.',
|
||||
)
|
||||
}
|
||||
|
||||
/** Resolved IAM issuer URL for the active provider env. Use this instead of reading env vars directly. */
|
||||
export const iamIssuer = _issuer
|
||||
@@ -77,7 +101,8 @@ const _createClient = () => {
|
||||
const redirectUri = _getEffectiveRedirectUri()
|
||||
const clientId = _getEffectiveClientId()
|
||||
console.info('[agos:auth] creating IAM client', {
|
||||
providerEnv: _iamProviderEnv,
|
||||
iamProviderEnv: _resolvedIamEnv,
|
||||
agosEnv: _agosEnv,
|
||||
issuer: _issuer ?? '',
|
||||
clientId,
|
||||
redirectUri,
|
||||
|
||||
Reference in New Issue
Block a user