fix(web): echo OAuth state on authorize redirect (#39459)

Signed-off-by: samzong <samzong.lu@gmail.com>
This commit is contained in:
samzong 2026-07-23 17:30:52 +08:00 committed by GitHub
parent 452dff5c37
commit b8f91d0e61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 7 additions and 4 deletions

View File

@ -63,7 +63,8 @@ describe('OAuthAuthorize', () => {
vi.clearAllMocks()
mocks.searchParams = new URLSearchParams({
client_id: 'client-1',
redirect_uri: 'https://client.example.com/callback?state=state-1',
redirect_uri: 'https://client.example.com/callback',
state: 'state-1',
})
mocks.request.mockImplementation(async (url: string) => {
if (url.endsWith('/oauth/provider/authorize')) return jsonResponse({ code: 'oauth-code' })
@ -86,7 +87,7 @@ describe('OAuthAuthorize', () => {
vi.unstubAllGlobals()
})
it('authorizes the displayed app and redirects with the returned code', async () => {
it('authorizes the displayed app and redirects with the returned code and state', async () => {
const user = userEvent.setup()
renderPage()
@ -95,7 +96,7 @@ describe('OAuthAuthorize', () => {
const providerTransportRequest = providerRequest?.[2]?.request as Request
await expect(providerTransportRequest.clone().json()).resolves.toEqual({
client_id: 'client-1',
redirect_uri: 'https://client.example.com/callback?state=state-1',
redirect_uri: 'https://client.example.com/callback',
})
await user.click(screen.getByRole('button', { name: /continue/i }))
@ -106,7 +107,7 @@ describe('OAuthAuthorize', () => {
await expect(transportRequest.clone().json()).resolves.toEqual({ client_id: 'client-1' })
await waitFor(() =>
expect(globalThis.location.href).toBe(
'https://client.example.com/callback?state=state-1&code=oauth-code',
'https://client.example.com/callback?code=oauth-code&state=state-1',
),
)
})

View File

@ -64,6 +64,7 @@ export default function OAuthAuthorize() {
const searchParams = useSearchParams()
const client_id = decodeURIComponent(searchParams.get('client_id') || '')
const redirect_uri = decodeURIComponent(searchParams.get('redirect_uri') || '')
const state = searchParams.get('state')
const hasOAuthParams = Boolean(client_id && redirect_uri)
// Probe user profile. 401 stays as `error` (legitimate "not logged in" state),
// other errors throw to the nearest error.tsx; jumpTo same-pathname guard in
@ -117,6 +118,7 @@ export default function OAuthAuthorize() {
const { code } = await authorize({ body: { client_id } })
const url = new URL(redirect_uri)
url.searchParams.set('code', code)
if (state) url.searchParams.set('state', state)
globalThis.location.href = url.toString()
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error)