From 4734d7ff0df185aa837d72175d7c2500b4ca13c0 Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Mon, 29 Jun 2026 10:59:14 +0200 Subject: [PATCH 1/2] docs(agents): forbid em dashes in public-facing text --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index f9019d7..b5dcdf1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,6 +78,7 @@ Direct provider wiring currently lives in [src/config/directMessaging.ts](/Users - Be careful around token response shapes and bearer auth. Browser-cookie auth mode has been removed. - Preserve existing local worktree changes unless the user explicitly asks you to clean them up. - Keep comments minimal. Comment only when the code genuinely needs explaining (a non-obvious reason or gotcha); do not narrate what the code plainly does. +- Do not use em dashes (—) in public-facing text: commit messages, code comments, PR/issue descriptions, changesets, and docs. Use a comma, parentheses, or a separate sentence instead. ## Before You Finish A Change From 6b6f1e6330b31dbbe80f441f8219efc6413f8adb Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Mon, 29 Jun 2026 10:59:32 +0200 Subject: [PATCH 2/2] fix(oauth): apply provider schema defaults from OAUTH_PROVIDERS env OAUTH_PROVIDERS was parsed with a raw JSON.parse, so per-provider defaults (subjectJsonPath, emailJsonPath, ...) were never applied and OAuth profile extraction failed silently. Validate the env value through the schema, and log the underlying error in the callback catch. Fixes #49. --- .changeset/oauth-provider-config-defaults.md | 8 +++++ src/controllers/oauth.ts | 6 +++- src/utils/parseEnvConfigs.ts | 8 +++++ .../utils/parseSystemConfigEnvValue.spec.ts | 34 +++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .changeset/oauth-provider-config-defaults.md diff --git a/.changeset/oauth-provider-config-defaults.md b/.changeset/oauth-provider-config-defaults.md new file mode 100644 index 0000000..4e77d60 --- /dev/null +++ b/.changeset/oauth-provider-config-defaults.md @@ -0,0 +1,8 @@ +--- +'seamless-auth-api': patch +--- + +Apply OAuthProviderConfigSchema defaults to providers configured via OAUTH_PROVIDERS. The +env value was parsed with a raw JSON.parse, so per-provider fields like subjectJsonPath and +emailJsonPath stayed undefined and OAuth profile extraction failed with a generic +"OAuth login failed". The OAuth callback now also logs the underlying error. Fixes #49. diff --git a/src/controllers/oauth.ts b/src/controllers/oauth.ts index 5883893..cdec4de 100644 --- a/src/controllers/oauth.ts +++ b/src/controllers/oauth.ts @@ -24,6 +24,9 @@ import { verifyOAuthState, } from '../services/oauthService.js'; import { issueSessionAndRespond } from '../services/sessionIssuance.js'; +import getLogger from '../utils/logger.js'; + +const logger = getLogger('oauth'); function allowedReturnTo(value: string | undefined, origins: string[]) { if (!value) return undefined; @@ -152,7 +155,8 @@ export async function finishOAuthLogin(req: Request, res: Response) { req, res, }); - } catch { + } catch (error) { + logger.error(`OAuth callback failed for provider ${provider.id}: ${error}`); await AuthEventService.log({ type: 'oauth_login_failed', req, diff --git a/src/utils/parseEnvConfigs.ts b/src/utils/parseEnvConfigs.ts index 1ddd825..13f4c3c 100644 --- a/src/utils/parseEnvConfigs.ts +++ b/src/utils/parseEnvConfigs.ts @@ -4,7 +4,10 @@ * See LICENSE file in the project root for full license information */ +import { z } from 'zod'; + import { SYSTEM_CONFIG_ENV_MAP } from '../config/systemConfig.envMap.js'; +import { OAuthProviderConfigSchema } from '../schemas/systemConfig.schema.js'; export function parseSystemConfigEnvValue(key: keyof typeof SYSTEM_CONFIG_ENV_MAP, raw: string) { switch (key) { @@ -18,6 +21,11 @@ export function parseSystemConfigEnvValue(key: keyof typeof SYSTEM_CONFIG_ENV_MA .filter(Boolean); case 'oauth_providers': + // Validate through the schema so per-provider defaults (subjectJsonPath, + // emailJsonPath, ...) are applied; raw JSON.parse leaves them undefined and + // OAuth profile extraction then silently fails. + return z.array(OAuthProviderConfigSchema).parse(JSON.parse(raw)); + case 'lockout_policy': return JSON.parse(raw); diff --git a/tests/unit/utils/parseSystemConfigEnvValue.spec.ts b/tests/unit/utils/parseSystemConfigEnvValue.spec.ts index 631238a..dc8c392 100644 --- a/tests/unit/utils/parseSystemConfigEnvValue.spec.ts +++ b/tests/unit/utils/parseSystemConfigEnvValue.spec.ts @@ -63,6 +63,40 @@ describe('parseSystemConfigEnvValue', () => { }); }); + describe('oauth_providers parsing', () => { + it('applies per-provider schema defaults (e.g. subjectJsonPath)', () => { + const raw = JSON.stringify([ + { + id: 'mock', + name: 'Mock', + clientId: 'client', + clientSecretEnv: 'MOCK_SECRET', + authorizationUrl: 'https://idp.test/authorize', + tokenUrl: 'https://idp.test/token', + userInfoUrl: 'https://idp.test/userinfo', + }, + ]); + + const result = parseSystemConfigEnvValue('oauth_providers', raw) as Array< + Record + >; + + expect(result[0]).toMatchObject({ + enabled: true, + subjectJsonPath: 'sub', + emailJsonPath: 'email', + emailVerifiedJsonPath: 'email_verified', + scopes: [], + allowSignup: true, + accountLinking: 'email', + }); + }); + + it('throws on an invalid provider entry', () => { + expect(() => parseSystemConfigEnvValue('oauth_providers', '[{"id":"x"}]')).toThrow(); + }); + }); + describe('invalid key', () => { it('throws for unknown key', () => { expect(() => parseSystemConfigEnvValue('invalid_key' as any, 'value')).toThrow(