|
|
|
@ -6,6 +6,7 @@ import { OAuthStateStore, oauthStateStore } from './oauth-state-store'; |
|
|
|
import { OAuthError, OAuthErrorCodes } from './oauth-error'; |
|
|
|
import { registerUser, createSession } from '#server/service/auth'; |
|
|
|
import type { MinimalUser } from '#server/service/auth'; |
|
|
|
import { FRONTEND_LOGIN_PATH } from "common/config" |
|
|
|
|
|
|
|
const logger = log4js.getLogger("OAUTH"); |
|
|
|
|
|
|
|
@ -28,6 +29,14 @@ export type OAuthCallbackResult = { |
|
|
|
expiresAt?: Date; |
|
|
|
}; |
|
|
|
|
|
|
|
export type FieldMapping = { |
|
|
|
provider?: string; |
|
|
|
providerUserId: string; |
|
|
|
username?: string; |
|
|
|
email?: string; |
|
|
|
avatar?: string; |
|
|
|
}; |
|
|
|
|
|
|
|
export type OAuthProviderConfig = { |
|
|
|
name: string; |
|
|
|
icon: string; |
|
|
|
@ -38,13 +47,7 @@ export type OAuthProviderConfig = { |
|
|
|
userInfoUrl: string; |
|
|
|
scopes: string[]; |
|
|
|
redirectUri: string; |
|
|
|
mapUserInfo: (raw: Record<string, unknown>) => { |
|
|
|
provider: string; |
|
|
|
providerUserId: string; |
|
|
|
username?: string; |
|
|
|
email?: string; |
|
|
|
avatar?: string; |
|
|
|
}; |
|
|
|
userInfoMapping: FieldMapping; |
|
|
|
}; |
|
|
|
|
|
|
|
const providers: Record<string, OAuthProviderConfig> = { |
|
|
|
@ -58,14 +61,11 @@ const providers: Record<string, OAuthProviderConfig> = { |
|
|
|
userInfoUrl: 'https://api.github.com/user', |
|
|
|
scopes: ['read:user', 'user:email'], |
|
|
|
redirectUri: `${process.env.APP_URL}/api/auth/oauth/github/callback`, |
|
|
|
mapUserInfo(raw: Record<string, unknown>) { |
|
|
|
return { |
|
|
|
provider: 'github', |
|
|
|
providerUserId: String(raw.id ?? ''), |
|
|
|
username: String(raw.login ?? ''), |
|
|
|
email: String(raw.email ?? ''), |
|
|
|
avatar: String(raw.avatar_url ?? ''), |
|
|
|
}; |
|
|
|
userInfoMapping: { |
|
|
|
providerUserId: 'id', |
|
|
|
username: 'login', |
|
|
|
email: 'email', |
|
|
|
avatar: 'avatar_url', |
|
|
|
}, |
|
|
|
}, |
|
|
|
}; |
|
|
|
@ -89,7 +89,7 @@ export class OAuthManager { |
|
|
|
|
|
|
|
const redirectUri = userId |
|
|
|
? `/profile?bind_success=1` |
|
|
|
: `/auth/login?oauth_success=1`; |
|
|
|
: `${FRONTEND_LOGIN_PATH}?oauth_success=1`; |
|
|
|
|
|
|
|
const { state } = this.stateStore.generate(providerName, redirectUri, userId); |
|
|
|
|
|
|
|
@ -283,6 +283,21 @@ export class OAuthManager { |
|
|
|
}>; |
|
|
|
} |
|
|
|
|
|
|
|
private mapUserInfo(raw: Record<string, unknown>, mapping: FieldMapping) { |
|
|
|
const getValue = (path: string) => { |
|
|
|
const value = (raw as Record<string, unknown>)[path]; |
|
|
|
return value != null ? String(value) : undefined; |
|
|
|
}; |
|
|
|
|
|
|
|
return { |
|
|
|
provider: mapping.provider || '', |
|
|
|
providerUserId: getValue(mapping.providerUserId) || '', |
|
|
|
username: mapping.username ? getValue(mapping.username) : undefined, |
|
|
|
email: mapping.email ? getValue(mapping.email) : undefined, |
|
|
|
avatar: mapping.avatar ? getValue(mapping.avatar) : undefined, |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
private async getUserInfo( |
|
|
|
provider: OAuthProviderConfig, |
|
|
|
accessToken: string |
|
|
|
@ -299,7 +314,7 @@ export class OAuthManager { |
|
|
|
} |
|
|
|
|
|
|
|
const raw = await response.json() as Record<string, unknown>; |
|
|
|
return provider.mapUserInfo(raw); |
|
|
|
return this.mapUserInfo(raw, provider.userInfoMapping); |
|
|
|
} |
|
|
|
|
|
|
|
private async createOAuthAccount(data: { |
|
|
|
|