Compare commits

..

1 Commits

Author SHA1 Message Date
yyh
8685658bcd chore: add Codex local environment setup 2026-05-30 12:52:29 +08:00
4 changed files with 23 additions and 26 deletions

0
.codex
View File

View File

@ -0,0 +1,8 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "dify"
[setup]
script = '''
pnpm install --frozen-lockfile --prefer-offline
'''

View File

@ -68,7 +68,7 @@ describe('auth refresh route', () => {
const fetchHeaders = fetchMock.mock.calls[0]?.[1]?.headers as Headers const fetchHeaders = fetchMock.mock.calls[0]?.[1]?.headers as Headers
expect(fetchHeaders.get('cookie')).toBe('refresh_token=old-refresh') expect(fetchHeaders.get('cookie')).toBe('refresh_token=old-refresh')
expect(response.status).toBe(303) expect(response.status).toBe(303)
expect(response.headers.get('location')).toBe('/apps?category=workflow') expect(response.headers.get('location')).toBe('http://localhost:3000/apps?category=workflow')
expect(getSetCookieHeaders(response.headers)).toEqual([ expect(getSetCookieHeaders(response.headers)).toEqual([
'access_token=new-access; Path=/; HttpOnly', 'access_token=new-access; Path=/; HttpOnly',
'refresh_token=new-refresh; Path=/; HttpOnly', 'refresh_token=new-refresh; Path=/; HttpOnly',
@ -85,7 +85,7 @@ describe('auth refresh route', () => {
)) ))
expect(response.status).toBe(303) expect(response.status).toBe(303)
expect(response.headers.get('location')).toBe('/signin?redirect_url=%2Fapps') expect(response.headers.get('location')).toBe('http://localhost:3000/signin?redirect_url=%2Fapps')
}) })
it('should ignore cross-origin redirect targets', async () => { it('should ignore cross-origin redirect targets', async () => {
@ -99,19 +99,6 @@ describe('auth refresh route', () => {
)) ))
expect(response.status).toBe(303) expect(response.status).toBe(303)
expect(response.headers.get('location')).toBe('/signin?redirect_url=%2Fapps') expect(response.headers.get('location')).toBe('http://localhost:3000/signin?redirect_url=%2Fapps')
})
it('should not leak internal request origin when redirecting to signin', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(null, { status: 401 })))
const { GET } = await import('../route')
const response = await GET(createRequest(
'http://internal-service:3000/auth/refresh?redirect_url=%2F',
'refresh_token=expired',
))
expect(response.status).toBe(303)
expect(response.headers.get('location')).toBe('/signin?redirect_url=%2F')
}) })
}) })

View File

@ -18,10 +18,11 @@ const resolveAbsoluteUrlPrefix = (value: string) => {
} }
} }
const resolveServerConsoleApiUrl = (pathname: string) => { const resolveServerConsoleApiUrl = (pathname: string, requestUrl: URL) => {
const requestPath = withoutLeadingSlash(pathname) const requestPath = withoutLeadingSlash(pathname)
const apiPrefix = SERVER_CONSOLE_API_PREFIX const apiPrefix = SERVER_CONSOLE_API_PREFIX
|| resolveAbsoluteUrlPrefix(API_PREFIX) || resolveAbsoluteUrlPrefix(API_PREFIX)
|| new URL(API_PREFIX, requestUrl.origin).toString()
if (!apiPrefix) if (!apiPrefix)
return null return null
@ -64,10 +65,10 @@ const getSetCookieHeaders = (headers: Headers) => {
return setCookie ? [setCookie] : [] return setCookie ? [setCookie] : []
} }
const createRedirectResponse = (pathname: string, setCookies: string[] = []) => { const createRedirectResponse = (request: Request, pathname: string, setCookies: string[] = []) => {
const headers = new Headers({ const headers = new Headers({
'Cache-Control': 'no-store', 'Cache-Control': 'no-store',
'Location': pathname, 'Location': new URL(pathname, request.url).toString(),
}) })
for (const cookie of setCookies) for (const cookie of setCookies)
@ -79,16 +80,17 @@ const createRedirectResponse = (pathname: string, setCookies: string[] = []) =>
}) })
} }
const createSigninRedirectResponse = (redirectPath: string) => const createSigninRedirectResponse = (request: Request, redirectPath: string) =>
createRedirectResponse(`${basePath}/signin?redirect_url=${encodeURIComponent(redirectPath)}`) createRedirectResponse(request, `${basePath}/signin?redirect_url=${encodeURIComponent(redirectPath)}`)
export async function GET(request: Request) { export async function GET(request: Request) {
const requestUrl = new URL(request.url)
const redirectPath = resolveSafeRedirectPath(request) const redirectPath = resolveSafeRedirectPath(request)
const refreshUrl = resolveServerConsoleApiUrl(REFRESH_TOKEN_PATH) const refreshUrl = resolveServerConsoleApiUrl(REFRESH_TOKEN_PATH, requestUrl)
const cookie = request.headers.get('cookie') const cookie = request.headers.get('cookie')
if (!refreshUrl || !cookie) if (!refreshUrl || !cookie)
return createSigninRedirectResponse(redirectPath) return createSigninRedirectResponse(request, redirectPath)
try { try {
const response = await fetch(refreshUrl, { const response = await fetch(refreshUrl, {
@ -101,11 +103,11 @@ export async function GET(request: Request) {
}) })
if (!response.ok) if (!response.ok)
return createSigninRedirectResponse(redirectPath) return createSigninRedirectResponse(request, redirectPath)
return createRedirectResponse(redirectPath, getSetCookieHeaders(response.headers)) return createRedirectResponse(request, redirectPath, getSetCookieHeaders(response.headers))
} }
catch { catch {
return createSigninRedirectResponse(redirectPath) return createSigninRedirectResponse(request, redirectPath)
} }
} }