@payez/next-mvp 3.6.2 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -128,16 +128,20 @@ async function getIDPClientConfig(forceRefresh = false) {
128
128
  }
129
129
  }
130
130
  // Layer 3: Fetch from IDP
131
+ const internalIdpUrl = process.env.INTERNAL_IDP_URL;
131
132
  const idpUrl = process.env.IDP_URL;
132
133
  const clientIdStr = process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID;
133
- if (!idpUrl) {
134
- throw new Error('[IDP_CONFIG] FATAL: IDP_URL must be set');
135
- }
136
134
  if (!clientIdStr) {
137
135
  throw new Error('[IDP_CONFIG] FATAL: CLIENT_ID or NEXT_PUBLIC_CLIENT_ID must be set');
138
136
  }
137
+ if (!internalIdpUrl && !idpUrl) {
138
+ throw new Error('[IDP_CONFIG] FATAL: INTERNAL_IDP_URL or IDP_URL must be set');
139
+ }
139
140
  // Start fetch and store promise so concurrent callers wait for same result
140
- pendingFetch = fetchConfigFromIDP(idpUrl, clientIdStr)
141
+ const fetcher = internalIdpUrl
142
+ ? fetchConfigFromInternalIDP(internalIdpUrl, clientIdStr)
143
+ : fetchConfigFromIDP(idpUrl, clientIdStr);
144
+ pendingFetch = fetcher
141
145
  .then(async (config) => {
142
146
  // Cache with TTL from response (default 5 minutes)
143
147
  cachedConfig = config;
@@ -181,6 +185,74 @@ function getEnabledProviders(config) {
181
185
  // ============================================================================
182
186
  // Internal Functions
183
187
  // ============================================================================
188
+ async function fetchConfigFromInternalIDP(internalIdpUrl, clientIdStr) {
189
+ const containersKey = process.env.CONTAINERS_KEY;
190
+ if (!containersKey) {
191
+ throw new Error('[IDP_CONFIG] FATAL: CONTAINERS_KEY is required when using INTERNAL_IDP_URL');
192
+ }
193
+ const url = `${internalIdpUrl.replace(/\/$/, '')}/InternalClientConfig/${encodeURIComponent(clientIdStr)}`;
194
+ console.log(`[IDP_CONFIG] Fetching config from internal IDP: ${url}`);
195
+ const resp = await fetch(url, {
196
+ method: 'GET',
197
+ headers: {
198
+ 'Accept': 'application/json',
199
+ 'Authorization': `Secret ${containersKey}`,
200
+ },
201
+ cache: 'no-store'
202
+ });
203
+ if (!resp.ok) {
204
+ const txt = await resp.text().catch(() => 'Unknown error');
205
+ throw new Error(`[IDP_CONFIG] FATAL: Internal IDP returned ${resp.status} - ${txt}`);
206
+ }
207
+ const body = await resp.json().catch(() => null);
208
+ if (!body) {
209
+ throw new Error('[IDP_CONFIG] FATAL: Internal IDP returned empty or invalid JSON');
210
+ }
211
+ const configData = body?.data ?? body;
212
+ const rawClientId = configData.clientId ?? configData.client_id;
213
+ if (rawClientId === undefined || rawClientId === null) {
214
+ throw new Error(`[IDP_CONFIG] FATAL: Internal IDP response missing clientId. Got: ${JSON.stringify(Object.keys(configData))}`);
215
+ }
216
+ const config = {
217
+ clientId: String(rawClientId),
218
+ clientSlug: configData.clientSlug ?? configData.client_slug ?? configData.slug ?? '',
219
+ nextAuthSecret: configData.nextAuthSecret ?? configData.next_auth_secret ?? '',
220
+ configCacheTtlSeconds: configData.configCacheTtlSeconds ?? configData.config_cache_ttl_seconds ?? 300,
221
+ oauthProviders: (configData.oauthProviders ?? configData.oauth_providers ?? []).map((p) => ({
222
+ provider: p.provider ?? '',
223
+ enabled: p.enabled ?? false,
224
+ clientId: p.clientId ?? p.client_id ?? '',
225
+ clientSecret: p.clientSecret ?? p.client_secret ?? '',
226
+ scopes: p.scopes,
227
+ additionalParams: p.additionalParams ?? p.additional_params
228
+ })),
229
+ authSettings: {
230
+ require2FA: configData.authSettings?.require2FA ?? configData.auth_settings?.require_2fa ?? true,
231
+ allowed2FAMethods: configData.authSettings?.allowed2FAMethods ?? configData.auth_settings?.allowed_2fa_methods ?? ['email', 'sms'],
232
+ mfaGracePeriodHours: configData.authSettings?.mfaGracePeriodHours ?? configData.auth_settings?.mfa_grace_period_hours ?? 24,
233
+ mfaRememberDeviceDays: configData.authSettings?.mfaRememberDeviceDays ?? configData.auth_settings?.mfa_remember_device_days ?? 30,
234
+ sessionTimeoutMinutes: configData.authSettings?.sessionTimeoutMinutes ?? configData.auth_settings?.session_timeout_minutes ?? 60,
235
+ idleTimeoutMinutes: configData.authSettings?.idleTimeoutMinutes ?? configData.auth_settings?.idle_timeout_minutes ?? 15,
236
+ allowRememberMe: configData.authSettings?.allowRememberMe ?? configData.auth_settings?.allow_remember_me ?? true,
237
+ rememberMeDays: configData.authSettings?.rememberMeDays ?? configData.auth_settings?.remember_me_days ?? 30,
238
+ lockoutThreshold: configData.authSettings?.lockoutThreshold ?? configData.auth_settings?.lockout_threshold ?? 5,
239
+ lockoutDurationMinutes: configData.authSettings?.lockoutDurationMinutes ?? configData.auth_settings?.lockout_duration_minutes ?? 15
240
+ },
241
+ branding: {
242
+ theme: configData.branding?.theme,
243
+ primaryColor: configData.branding?.primaryColor ?? configData.branding?.primary_color,
244
+ secondaryColor: configData.branding?.secondaryColor ?? configData.branding?.secondary_color,
245
+ logoUrl: configData.branding?.logoUrl ?? configData.branding?.logo_url
246
+ },
247
+ baseClientUrl: configData.baseClientUrl ?? configData.base_client_url ?? configData.BaseClientUrl
248
+ };
249
+ if (!config.nextAuthSecret) {
250
+ throw new Error('[IDP_CONFIG] FATAL: Internal IDP did not return nextAuthSecret');
251
+ }
252
+ console.log(`[IDP_CONFIG] Internal IDP config loaded for ${clientIdStr}`);
253
+ consecutiveFailures = 0;
254
+ return config;
255
+ }
184
256
  async function fetchConfigFromIDP(idpUrl, clientIdStr) {
185
257
  // =========================================================================
186
258
  // Circuit Breaker Check
@@ -219,7 +291,7 @@ async function fetchConfigFromIDP(idpUrl, clientIdStr) {
219
291
  issuer: clientIdStr,
220
292
  subject: clientIdStr,
221
293
  audience: 'urn:payez:externalauth:clientconfig',
222
- expires_in: 60
294
+ expires_in: 60,
223
295
  };
224
296
  const signingResp = await fetch(signingUrl, {
225
297
  method: 'POST',
@@ -33,12 +33,11 @@ async function resolveNextAuthSecret() {
33
33
  throw new Error('CLIENT_ID is required (e.g., "ideal_resume_website")');
34
34
  // Step 1: Request IDP to sign a client assertion (IDP has the keys, not us)
35
35
  const signingUrl = new URL(`${base.replace(/\/$/, '')}/api/ExternalAuth/sign-client-assertion`);
36
- // Client ID passed via X-Client-Id header, not query string
37
36
  const signingPayload = {
38
37
  issuer: clientIdStr,
39
38
  subject: clientIdStr,
40
39
  audience: 'urn:payez:externalauth:nextauthsecret',
41
- expires_in: 60
40
+ expires_in: 60,
42
41
  };
43
42
  const signingResp = await fetch(signingUrl.toString(), {
44
43
  method: 'POST',