refactor(auth): extract connector definitions out of auth.ts (#6203)

auth.ts had grown to 3,927 lines, of which ~2,340 were the genericOAuth
connector list — the OAuth apps a workspace connects tools to, as distinct from
the handful of providers used to sign in to Sim. Adding a connector meant
editing the same file that configures sessions, database hooks and Stripe.

Moves that list to lib/auth/connectors/providers.ts behind
buildConnectorProviders(), and relocates getMicrosoftUserInfoFromIdToken to
lib/oauth/microsoft.ts alongside the three Microsoft helpers it already depends
on. auth.ts drops to 1,489 lines and reads as auth configuration again.

Pure move, verified mechanically: the connector array is token-identical after
stripping whitespace, and all 179 template literals emit byte-identical strings
(the one apparent diff was reindentation inside a ${} expression, not text).
Behavior, evaluation order and log scopes are unchanged; the array is still
built once, when betterAuth() runs.

The explicit GenericOAuthConfig[] return type is required, not cosmetic —
inline, the entries were contextually typed by the config property. Without it
prompt: 'consent' widens to string and every getUserInfo parameter becomes
implicitly any.
This commit is contained in:
Waleed
2026-08-03 09:35:17 -07:00
committed by GitHub
parent 14d9542f2a
commit 030c4e2a5e
3 changed files with 2476 additions and 2443 deletions
+6 -2443
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+60
View File
@@ -1,3 +1,13 @@
import { createLogger } from '@sim/logger'
import { generateId } from '@sim/utils/id'
/**
* Scoped `'Auth'` because these lines are emitted from the OAuth callback path
* and were logged under that scope before this helper moved here; renaming the
* scope would break existing log queries and alerts.
*/
const logger = createLogger('Auth')
const MICROSOFT_REFRESH_TOKEN_LIFETIME_DAYS = 90
export const PROACTIVE_REFRESH_THRESHOLD_DAYS = 7
@@ -43,3 +53,53 @@ export function deriveMicrosoftEmailVerified(
(Array.isArray(verifiedSecondary) && verifiedSecondary.includes(email))
)
}
/**
* Extracts user info from a Microsoft ID token JWT instead of calling Graph API /me.
* This avoids 403 errors for external tenant users whose admin hasn't consented to Graph API scopes.
* The ID token is always returned when the openid scope is requested.
*/
export function getMicrosoftUserInfoFromIdToken(
tokens: { accessToken?: string },
providerId: string
) {
const idToken = (tokens as Record<string, unknown>).idToken as string | undefined
if (!idToken) {
logger.error(
`Microsoft ${providerId} OAuth: no ID token received. Ensure openid scope is requested.`
)
throw new Error(`Microsoft ${providerId} OAuth requires an ID token (openid scope)`)
}
const parts = idToken.split('.')
if (parts.length !== 3) {
throw new Error(`Microsoft ${providerId} OAuth: malformed ID token`)
}
let payload: Record<string, unknown>
try {
payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'))
} catch {
throw new Error(`Microsoft ${providerId} OAuth: failed to decode ID token payload`)
}
const email =
(payload.email as string) || (payload.preferred_username as string) || (payload.upn as string)
if (!email) {
throw new Error(
`Microsoft ${providerId} OAuth: ID token contains no email, preferred_username, or upn claim`
)
}
const emailVerified = deriveMicrosoftEmailVerified(payload, email)
const now = new Date()
return {
id: `${payload.oid || payload.sub}-${generateId()}`,
name: (payload.name as string) || 'Microsoft User',
email,
emailVerified,
createdAt: now,
updatedAt: now,
}
}