import { Configuration } from 'oidc-provider'; import { RedisAdapter } from './redis-adapter'; import { prisma } from '@repo/db'; import { generateKeyPairSync } from 'crypto'; // 自动生成JWKS密钥对(如无环境变量则自动生成,建议持久化到安全存储) function getJWKS() { if (process.env.OIDC_JWKS) { return JSON.parse(process.env.OIDC_JWKS); } // 生成RSA密钥对 const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, }); // 转为JWK格式(这里只做简单转换,生产建议用jose等库) const jwk = require('pem-jwk').pem2jwk(publicKey); jwk.use = 'sig'; jwk.alg = 'RS256'; // 打印公钥,便于配置到前端或备份 console.log('自动生成的OIDC公钥JWK:', JSON.stringify(jwk, null, 2)); return [jwk]; } // 支持从数据库动态加载clients async function getClients() { const dbClients = await prisma.oidcClient.findMany?.(); if (dbClients && dbClients.length > 0) { return dbClients.map(c => ({ client_id: c.clientId, client_secret: c.clientSecret, grant_types: JSON.parse(c.grantTypes), // string -> string[] redirect_uris: JSON.parse(c.redirectUris), // string -> string[] response_types: JSON.parse(c.responseTypes), // string -> string[] scope: c.scope, })); } return [ { client_id: process.env.OIDC_CLIENT_ID || 'example-client', client_secret: process.env.OIDC_CLIENT_SECRET || 'example-secret', grant_types: ['authorization_code', 'refresh_token'], redirect_uris: [process.env.OIDC_REDIRECT_URI || 'http://localhost:3000/callback'], response_types: ['code'], scope: 'openid email profile', }, ]; } const OIDC_COOKIE_KEY = process.env.OIDC_COOKIE_KEY || 'default-cookie-key'; const config: Configuration = { adapter: RedisAdapter, // 注意:clients字段现在是Promise,需在Provider初始化时await clients: await getClients(), pkce: { required: () => true, }, features: { devInteractions: { enabled: false }, resourceIndicators: { enabled: true }, revocation: { enabled: true }, userinfo: { enabled: true }, registration: { enabled: true }, }, cookies: { keys: [OIDC_COOKIE_KEY], }, jwks: { keys: getJWKS(), }, ttl: { AccessToken: 3600, AuthorizationCode: 600, IdToken: 3600, RefreshToken: 1209600, BackchannelAuthenticationRequest: 600, ClientCredentials: 600, DeviceCode: 600, Grant: 1209600, Interaction: 3600, Session: 1209600, RegistrationAccessToken: 3600, DPoPProof: 300, PushedAuthorizationRequest: 600, ReplayDetection: 3600, LogoutToken: 600, }, findAccount: async (ctx, id) => { const user = await prisma.user.findUnique({ where: { id } }); if (!user) return undefined; return { accountId: user.id, async claims() { return { sub: user.id, email: user.email, name: user.name, }; }, }; }, }; export default config;