@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.
144 lines (124 loc) • 5.22 kB
text/typescript
import debug from 'debug';
import { NextRequest, NextResponse } from 'next/server';
import { OIDCService } from '@/server/services/oidc';
import { getUserAuth } from '@/utils/server/auth';
const log = debug('lobe-oidc:consent');
export async function POST(request: NextRequest) {
log('Received POST request for /oidc/consent, URL: %s', request.url);
try {
const formData = await request.formData();
const consent = formData.get('consent') as string;
const uid = formData.get('uid') as string;
log('POST /oauth/consent - uid=%s, choice=%s', uid, consent);
const oidcService = await OIDCService.initialize();
let details;
try {
details = await oidcService.getInteractionDetails(uid);
log(
'Interaction details found - prompt=%s, client=%s',
details.prompt.name,
details.params.client_id,
);
} catch (error) {
log(
'Error: Interaction details not found - %s',
error instanceof Error ? error.message : 'unknown error',
);
if (error instanceof Error && error.message.includes('interaction session not found')) {
return NextResponse.json(
{
error: 'invalid_request',
error_description:
'Authorization session expired or invalid, please restart the authorization flow',
},
{ status: 400 },
);
}
throw error;
}
const { prompt } = details;
let result;
if (consent === 'accept') {
log(`User accepted the request, Handling 'login' prompt`);
const { userId } = await getUserAuth();
log('Obtained userId: %s', userId);
if (details.prompt.name === 'login') {
result = {
login: { accountId: userId, remember: true },
};
} else {
log(`Handling 'consent' prompt`);
// 1. 获取必要的 ID
const clientId = details.params.client_id as string;
// 2. 查找或创建 Grant 对象
const grant = await oidcService.findOrCreateGrants(userId!, clientId, details.grantId);
// 3. 将用户同意的 scopes 和 claims 添加到 Grant 对象
// 这些信息通常在 details.prompt.details 中
const missingOIDCScope = (prompt.details.missingOIDCScope as string[]) || [];
if (missingOIDCScope) {
grant.addOIDCScope(missingOIDCScope.join(' '));
log('Added OIDC scopes to grant: %s', missingOIDCScope.join(' '));
}
const missingOIDCClaims = (prompt.details.missingOIDCClaims as string[]) || [];
if (missingOIDCClaims) {
grant.addOIDCClaims(missingOIDCClaims);
log('Added OIDC claims to grant: %s', missingOIDCClaims.join(' '));
}
const missingResourceScopes =
(prompt.details.missingResourceScopes as Record<string, string[]>) || {};
if (missingResourceScopes) {
for (const [indicator, scopes] of Object.entries(missingResourceScopes)) {
grant.addResourceScope(indicator, scopes.join(' '));
log('Added resource scopes for %s to grant: %s', indicator, scopes.join(' '));
}
}
// 如果使用了 RAR (Rich Authorization Requests),也需要添加到 grant
// if (prompt.details.rar) {
// prompt.details.rar.forEach(detail => grant.addRar(detail));
// }
// 4. 保存 Grant 对象以获取其 jti (grantId)
const newGrantId = await grant.save();
log('Saved grant with ID: %s', newGrantId);
// 5. 准备包含 grantId 的 result
result = { consent: { grantId: newGrantId } };
log('Consent result prepared with grantId');
}
log('User %s the authorization', consent);
} else {
log('User rejected the request');
result = {
error: 'access_denied',
error_description: 'User denied the authorization request',
};
log('User %s the authorization', consent);
}
log('Interaction Result: %O', result);
const internalRedirectUrlString = await oidcService.getInteractionResult(uid, result);
log('OIDC Provider internal redirect URL string: %s', internalRedirectUrlString);
// // Construct the handoff URL
// const handoffUrl = new URL('/oauth/handoff', request.nextUrl.origin);
// // Set the original redirect URL as the 'target' query parameter (URL encoded)
// handoffUrl.searchParams.set('target', internalRedirectUrlString);
//
// log('Redirecting to handoff page: %s', handoffUrl.toString());
// // Redirect to the handoff page
// return NextResponse.redirect(handoffUrl.toString(), {
// headers: request.headers, // Keep original headers if necessary
// status: 303,
// });
return NextResponse.redirect(internalRedirectUrlString, {
headers: request.headers,
status: 303,
});
} catch (error) {
log('Error processing consent: %s', error instanceof Error ? error.message : 'unknown error');
console.error('Error processing consent:', error);
return NextResponse.json(
{
error: 'server_error',
error_description: 'Error processing consent',
},
{ status: 500 },
);
}
}