Files
sim/apps/sim/lib/auth/oauth-token.ts
Waleed 3fa4bb4c12 feat(auth): add OAuth 2.1 provider for MCP connector support (#3274)
* feat(auth): add OAuth 2.1 provider for MCP connector support

* fix(auth): rename redirect_u_r_ls column to redirect_urls

* chore(db): regenerate oauth migration with correct column naming

* fix(auth): reorder CORS headers and handle missing redirectURI

* fix(auth): redirect to login without stale callbackUrl on account switch

* chore: run lint

* fix(auth): override credentials header on OAuth CORS entries

* fix(auth): preserve OAuth flow when switching accounts on consent page

* fix(auth): add session and user-id checks to authorize-params endpoint

* fix(auth): add expiry check, credentials, MCP CORS, and scope in WWW-Authenticate

* feat(mcp): add tool annotations for Connectors Directory compliance
2026-02-20 15:56:15 -08:00

52 lines
1.5 KiB
TypeScript

import { db } from '@sim/db'
import { oauthAccessToken } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, gt } from 'drizzle-orm'
const logger = createLogger('OAuthToken')
interface OAuthTokenValidationResult {
success: boolean
userId?: string
scopes?: string[]
error?: string
}
/**
* Validates an OAuth 2.1 access token by looking it up in the oauthAccessToken table.
* Returns the associated userId and scopes if the token is valid and not expired.
*/
export async function validateOAuthAccessToken(token: string): Promise<OAuthTokenValidationResult> {
try {
const [record] = await db
.select({
userId: oauthAccessToken.userId,
scopes: oauthAccessToken.scopes,
accessTokenExpiresAt: oauthAccessToken.accessTokenExpiresAt,
})
.from(oauthAccessToken)
.where(
and(
eq(oauthAccessToken.accessToken, token),
gt(oauthAccessToken.accessTokenExpiresAt, new Date())
)
)
.limit(1)
if (!record) {
return { success: false, error: 'Invalid or expired OAuth access token' }
}
if (!record.userId) {
return { success: false, error: 'OAuth token has no associated user' }
}
const scopes = record.scopes.split(' ').filter(Boolean)
return { success: true, userId: record.userId, scopes }
} catch (error) {
logger.error('OAuth access token validation failed', { error })
return { success: false, error: 'Token validation error' }
}
}