Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

106 lines
3.4 KiB
TypeScript

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
export const MICROSOFT_PROVIDERS = new Set([
'microsoft-ad',
'microsoft-dataverse',
'microsoft-excel',
'microsoft-planner',
'microsoft-teams',
'outlook',
'onedrive',
'sharepoint',
])
export function isMicrosoftProvider(providerId: string): boolean {
return MICROSOFT_PROVIDERS.has(providerId)
}
export function getMicrosoftRefreshTokenExpiry(): Date {
return new Date(Date.now() + MICROSOFT_REFRESH_TOKEN_LIFETIME_DAYS * 24 * 60 * 60 * 1000)
}
/**
* Derives whether a Microsoft ID token proves ownership of `email`. Azure AD's
* `email`/`upn` claims are unverified and mutable on multi-tenant (`/common/`)
* endpoints, so the email is trusted only when the token explicitly proves it via
* the `email_verified` claim or the verified-email claims, mirroring Better
* Auth's built-in Microsoft provider. Defaults to `false` when no claim asserts
* verification, so an attacker-controlled tenant can never assert a verified
* email it does not own.
*/
export function deriveMicrosoftEmailVerified(
claims: Record<string, unknown>,
email: string
): boolean {
if (claims.email_verified !== undefined) {
return Boolean(claims.email_verified)
}
const { verified_primary_email: verifiedPrimary, verified_secondary_email: verifiedSecondary } =
claims
return (
(Array.isArray(verifiedPrimary) && verifiedPrimary.includes(email)) ||
(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,
}
}