(userId: string, providerId: string)
| 442 | } |
| 443 | |
| 444 | export async function getOAuthToken(userId: string, providerId: string): Promise<string | null> { |
| 445 | const connections = await db |
| 446 | .select({ |
| 447 | id: account.id, |
| 448 | accessToken: account.accessToken, |
| 449 | refreshToken: account.refreshToken, |
| 450 | accessTokenExpiresAt: account.accessTokenExpiresAt, |
| 451 | idToken: account.idToken, |
| 452 | scope: account.scope, |
| 453 | }) |
| 454 | .from(account) |
| 455 | .where(and(eq(account.userId, userId), eq(account.providerId, providerId))) |
| 456 | .orderBy(desc(account.updatedAt)) |
| 457 | .limit(1) |
| 458 | |
| 459 | if (connections.length === 0) { |
| 460 | logger.warn(`No OAuth token found for user ${userId}, provider ${providerId}`) |
| 461 | return null |
| 462 | } |
| 463 | |
| 464 | const credential = connections[0] |
| 465 | |
| 466 | // Determine whether we should refresh: missing token OR expired token |
| 467 | const now = new Date() |
| 468 | const tokenExpiry = credential.accessTokenExpiresAt |
| 469 | const shouldAttemptRefresh = |
| 470 | !!credential.refreshToken && (!credential.accessToken || (tokenExpiry && tokenExpiry < now)) |
| 471 | |
| 472 | if (shouldAttemptRefresh) { |
| 473 | return performCoalescedRefresh({ |
| 474 | accountId: credential.id, |
| 475 | providerId, |
| 476 | refreshToken: credential.refreshToken!, |
| 477 | userId, |
| 478 | }) |
| 479 | } |
| 480 | |
| 481 | if (!credential.accessToken) { |
| 482 | logger.warn( |
| 483 | `Access token is null and no refresh attempted or available for user ${userId}, provider ${providerId}` |
| 484 | ) |
| 485 | return null |
| 486 | } |
| 487 | |
| 488 | logger.info(`Found valid OAuth token for user ${userId}, provider ${providerId}`) |
| 489 | return credential.accessToken |
| 490 | } |
| 491 | |
| 492 | /** |
| 493 | * Refreshes an OAuth token if needed based on credential information. |
no test coverage detected