@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
136 lines (113 loc) • 4.41 kB
text/typescript
import { TRPCError } from '@trpc/server';
import debug from 'debug';
import { createContextForInteractionDetails } from '@/libs/oidc-provider/http-adapter';
import { OIDCProvider } from '@/libs/oidc-provider/provider';
import { getOIDCProvider } from './oidcProvider';
const log = debug('lobe-oidc:service');
export class OIDCService {
private provider: OIDCProvider;
constructor(provider: OIDCProvider) {
this.provider = provider;
}
static async initialize() {
const provider = await getOIDCProvider();
return new OIDCService(provider);
}
/**
* 验证 OIDC Bearer Token 并返回用户信息
* 使用 oidc-provider 实例的 AccessToken.find 方法验证 token
*
* @param token - Bearer Token
* @returns 包含用户ID和Token数据的对象
* @throws 如果token无效或OIDC未启用则抛出 TRPCError
*/
async validateToken(token: string) {
try {
log('Validating access token using AccessToken.find');
// 使用 oidc-provider 的 AccessToken 查找和验证方法
const accessToken = await this.provider.AccessToken.find(token);
if (!accessToken) {
log('Access token not found, expired, or consumed');
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'Access token 无效、已过期或已被使用',
});
}
// 从 accessToken 实例中获取必要的数据
// 注意:accessToken 没有 payload() 方法,而是直接访问其属性
const userId = accessToken.accountId; // 用户 ID 通常存储在 accountId 属性中
const clientId = accessToken.clientId;
// 如果需要更多的声明信息,可以从 accessToken 的其他属性中获取
// 例如,scopes、claims、exp 等
const tokenData = {
client_id: clientId,
exp: accessToken.exp,
iat: accessToken.iat,
jti: accessToken.jti,
scope: accessToken.scope,
// OIDC 标准中,sub 字段表示用户 ID
sub: userId,
};
if (!userId) {
log('Access token does not contain user ID (accountId)');
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'Access token 中未包含用户 ID',
});
}
log('Access token validated successfully for user: %s', userId);
return {
// 包含 token 原始数据,可用于获取更多信息
accessToken,
// 构建的 token 数据对象
tokenData,
// 用户 ID
userId,
};
} catch (error) {
if (error instanceof TRPCError) throw error;
// AccessToken.find 可能抛出特定错误
log('Error validating access token with AccessToken.find: %O', error);
console.error('OIDC 令牌验证错误:', error);
throw new TRPCError({
code: 'UNAUTHORIZED',
message: `OIDC 认证失败: ${(error as Error).message}`,
});
}
}
async getInteractionDetails(uid: string) {
const { req, res } = await createContextForInteractionDetails(uid);
return this.provider.interactionDetails(req, res);
}
async getInteractionResult(uid: string, result: any) {
const { req, res } = await createContextForInteractionDetails(uid);
return this.provider.interactionResult(req, res, result);
}
async finishInteraction(uid: string, result: any) {
const { req, res } = await createContextForInteractionDetails(uid);
return this.provider.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
}
async findOrCreateGrants(accountId: string, clientId: string, existingGrantId?: string) {
// 2. 查找或创建 Grant 对象
let grant;
if (existingGrantId) {
// 如果之前的交互步骤已经关联了 Grant
grant = await this.provider.Grant.find(existingGrantId);
log('Found existing grantId: %s', existingGrantId);
}
if (!grant) {
// 如果没有找到或没有 existingGrantId,则创建新的
grant = new this.provider.Grant({
accountId: accountId,
clientId: clientId,
});
log('Created new Grant for account %s and client %s', accountId, clientId);
}
return grant;
}
async getClientMetadata(clientId: string) {
const client = await this.provider.Client.find(clientId);
return client?.metadata();
}
}
export { getOIDCProvider } from './oidcProvider';