mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
🐛 fix: fix desktop auth redirect url error (#8597)
* try to fix cloudflare issue * fix 0.0.0.0 * fix route * fix desktop callback * update pkg * try to fix again * try to fix route again
This commit is contained in:
parent
f92b86b194
commit
0ed73685dc
4 changed files with 130 additions and 21 deletions
|
|
@ -257,7 +257,7 @@
|
|||
"semver": "^7.7.2",
|
||||
"sharp": "^0.34.3",
|
||||
"shiki": "^3.8.1",
|
||||
"stripe": "^16.12.0",
|
||||
"stripe": "^17.7.0",
|
||||
"superjson": "^2.2.2",
|
||||
"svix": "^1.69.0",
|
||||
"swr": "^2.3.4",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,50 @@ import { NextRequest, NextResponse, after } from 'next/server';
|
|||
|
||||
import { OAuthHandoffModel } from '@/database/models/oauthHandoff';
|
||||
import { serverDB } from '@/database/server';
|
||||
import { correctOIDCUrl } from '@/utils/server/correctOIDCUrl';
|
||||
|
||||
const log = debug('lobe-oidc:callback:desktop');
|
||||
|
||||
const errorPathname = '/oauth/callback/error';
|
||||
|
||||
/**
|
||||
* 安全地构建重定向URL
|
||||
*/
|
||||
const buildRedirectUrl = (req: NextRequest, pathname: string): URL => {
|
||||
const forwardedHost = req.headers.get('x-forwarded-host');
|
||||
const requestHost = req.headers.get('host');
|
||||
const forwardedProto =
|
||||
req.headers.get('x-forwarded-proto') || req.headers.get('x-forwarded-protocol');
|
||||
|
||||
// 确定实际的主机名,提供后备值
|
||||
const actualHost = forwardedHost || requestHost;
|
||||
const actualProto = forwardedProto || 'https';
|
||||
|
||||
log(
|
||||
'Building redirect URL - host: %s, proto: %s, pathname: %s',
|
||||
actualHost,
|
||||
actualProto,
|
||||
pathname,
|
||||
);
|
||||
|
||||
// 如果主机名仍然无效,使用req.nextUrl作为后备
|
||||
if (!actualHost) {
|
||||
log('Warning: Invalid host detected, using req.nextUrl as fallback');
|
||||
const fallbackUrl = req.nextUrl.clone();
|
||||
fallbackUrl.pathname = pathname;
|
||||
return fallbackUrl;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(`${actualProto}://${actualHost}${pathname}`);
|
||||
} catch (error) {
|
||||
log('Error constructing URL, using req.nextUrl as fallback: %O', error);
|
||||
const fallbackUrl = req.nextUrl.clone();
|
||||
fallbackUrl.pathname = pathname;
|
||||
return fallbackUrl;
|
||||
}
|
||||
};
|
||||
|
||||
export const GET = async (req: NextRequest) => {
|
||||
try {
|
||||
const searchParams = req.nextUrl.searchParams;
|
||||
|
|
@ -14,9 +55,11 @@ export const GET = async (req: NextRequest) => {
|
|||
|
||||
if (!code || !state || typeof code !== 'string' || typeof state !== 'string') {
|
||||
log('Missing code or state in form data');
|
||||
const errorUrl = req.nextUrl.clone();
|
||||
errorUrl.pathname = '/oauth/callback/error';
|
||||
|
||||
const errorUrl = buildRedirectUrl(req, errorPathname);
|
||||
errorUrl.searchParams.set('reason', 'invalid_request');
|
||||
|
||||
log('Redirecting to error URL: %s', errorUrl.toString());
|
||||
return NextResponse.redirect(errorUrl);
|
||||
}
|
||||
|
||||
|
|
@ -31,9 +74,16 @@ export const GET = async (req: NextRequest) => {
|
|||
await authHandoffModel.create({ client, id, payload });
|
||||
log('Handoff record created successfully for id: %s', id);
|
||||
|
||||
// Redirect to a generic success page. The desktop app will poll for the result.
|
||||
const successUrl = req.nextUrl.clone();
|
||||
successUrl.pathname = '/oauth/callback/success';
|
||||
const successUrl = buildRedirectUrl(req, '/oauth/callback/success');
|
||||
|
||||
// 添加调试日志
|
||||
log('Request host header: %s', req.headers.get('host'));
|
||||
log('Request x-forwarded-host: %s', req.headers.get('x-forwarded-host'));
|
||||
log('Request x-forwarded-proto: %s', req.headers.get('x-forwarded-proto'));
|
||||
log('Constructed success URL: %s', successUrl.toString());
|
||||
|
||||
const correctedUrl = correctOIDCUrl(req, successUrl);
|
||||
log('Final redirect URL: %s', correctedUrl.toString());
|
||||
|
||||
// cleanup expired
|
||||
after(async () => {
|
||||
|
|
@ -42,17 +92,18 @@ export const GET = async (req: NextRequest) => {
|
|||
log('Cleaned up %d expired handoff records', cleanedCount);
|
||||
});
|
||||
|
||||
return NextResponse.redirect(successUrl);
|
||||
return NextResponse.redirect(correctedUrl);
|
||||
} catch (error) {
|
||||
log('Error in OIDC callback: %O', error);
|
||||
const errorUrl = req.nextUrl.clone();
|
||||
errorUrl.pathname = '/oauth/callback/error';
|
||||
|
||||
const errorUrl = buildRedirectUrl(req, errorPathname);
|
||||
errorUrl.searchParams.set('reason', 'internal_error');
|
||||
|
||||
if (error instanceof Error) {
|
||||
errorUrl.searchParams.set('errorMessage', error.message);
|
||||
}
|
||||
|
||||
log('Redirecting to error URL: %s', errorUrl.toString());
|
||||
return NextResponse.redirect(errorUrl);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||
|
||||
import { OIDCService } from '@/server/services/oidc';
|
||||
import { getUserAuth } from '@/utils/server/auth';
|
||||
import { correctOIDCUrl } from '@/utils/server/correctOIDCUrl';
|
||||
|
||||
const log = debug('lobe-oidc:consent');
|
||||
|
||||
|
|
@ -113,19 +114,15 @@ export async function POST(request: NextRequest) {
|
|||
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,
|
||||
// });
|
||||
let finalRedirectUrl;
|
||||
try {
|
||||
finalRedirectUrl = correctOIDCUrl(request, new URL(internalRedirectUrlString));
|
||||
} catch {
|
||||
finalRedirectUrl = new URL(internalRedirectUrlString);
|
||||
log('Warning: Could not parse redirect URL, using as-is: %s', internalRedirectUrlString);
|
||||
}
|
||||
|
||||
return NextResponse.redirect(internalRedirectUrlString, {
|
||||
return NextResponse.redirect(finalRedirectUrl, {
|
||||
headers: request.headers,
|
||||
status: 303,
|
||||
});
|
||||
|
|
|
|||
61
src/utils/server/correctOIDCUrl.ts
Normal file
61
src/utils/server/correctOIDCUrl.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import debug from 'debug';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
const log = debug('lobe-oidc:correctOIDCUrl');
|
||||
|
||||
/**
|
||||
* 修复 OIDC 重定向 URL 在代理环境下的问题
|
||||
* @param req - Next.js 请求对象
|
||||
* @param url - 要修复的 URL 对象
|
||||
* @returns 修复后的 URL 对象
|
||||
*/
|
||||
export const correctOIDCUrl = (req: NextRequest, url: URL): URL => {
|
||||
const requestHost = req.headers.get('host');
|
||||
const forwardedHost = req.headers.get('x-forwarded-host');
|
||||
const forwardedProto =
|
||||
req.headers.get('x-forwarded-proto') || req.headers.get('x-forwarded-protocol');
|
||||
|
||||
log('Input URL: %s', url.toString());
|
||||
log(
|
||||
'Request headers - host: %s, x-forwarded-host: %s, x-forwarded-proto: %s',
|
||||
requestHost,
|
||||
forwardedHost,
|
||||
forwardedProto,
|
||||
);
|
||||
|
||||
// 确定实际的主机名和协议,提供后备值
|
||||
const actualHost = forwardedHost || requestHost;
|
||||
const actualProto = forwardedProto || (url.protocol === 'https:' ? 'https' : 'http');
|
||||
|
||||
// 如果无法确定有效的主机名,直接返回原URL
|
||||
if (!actualHost || actualHost === 'null') {
|
||||
log('Warning: Cannot determine valid host, returning original URL');
|
||||
return url;
|
||||
}
|
||||
|
||||
// 如果 URL 指向本地地址,或者主机名与实际请求主机不匹配,则修正 URL
|
||||
const needsCorrection =
|
||||
url.hostname === 'localhost' ||
|
||||
url.hostname === '127.0.0.1' ||
|
||||
url.hostname === '0.0.0.0' ||
|
||||
url.hostname !== actualHost;
|
||||
|
||||
if (needsCorrection) {
|
||||
log('URL needs correction. Original hostname: %s, correcting to: %s', url.hostname, actualHost);
|
||||
|
||||
try {
|
||||
const correctedUrl = new URL(url.toString());
|
||||
correctedUrl.protocol = actualProto + ':';
|
||||
correctedUrl.host = actualHost;
|
||||
|
||||
log('Corrected URL: %s', correctedUrl.toString());
|
||||
return correctedUrl;
|
||||
} catch (error) {
|
||||
log('Error creating corrected URL, returning original: %O', error);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
log('URL does not need correction, returning original: %s', url.toString());
|
||||
return url;
|
||||
};
|
||||
Loading…
Reference in a new issue