Supabase Auth调用exchangeCodeForSession时提示"invalid request: both auth code and code verifier should be non-empty"问题求助
Supabase Auth调用exchangeCodeForSession时提示"invalid request: both auth code and code verifier should be non-empty"问题求助
大家好,我现在在做一个跨平台项目,遇到了Supabase OAuth2登录的问题,折腾了好久都没解决,来求助各位!
先简单说下我的项目架构:
这是一个跨平台Web项目(侧重SEO),因为用SSR搭配Capacitor框架时性能太差,所以最终采用了CSR模式开发。
客户端:Svelte + Vite + Capacitor
因为用的是原生Svelte,所以导航用的是*"svelte-routing"*;跨Web、iOS和Android端打包用的是Capacitor。
服务端:Fastify + Supabase
服务端框架选的是Fastify,搭配Supabase使用。因为要用到Supabase的Auth方案,所以没法用Capacitor的Generic OAuth2这类工具。
问题详情
我按照Supabase的Google OAuth2集成指南来实现第三方登录,但是在调用exchangeCodeForSession存储用户会话的时候,一直报AuthApiError:"invalid request: both auth code and code verifier should be non-empty"。
使用的依赖包
// package.json { ..., "dependencies": { "@fastify/compress": "^8.0.1", "@fastify/cookie": "^11.0.2", "@fastify/cors": "^10.0.2", "@fastify/env": "^5.0.2", "@fastify/formbody": "^8.0.2", "@fastify/multipart": "^9.0.2", "@fastify/static": "^8.0.4", "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.47.16", "dotenv": "^16.4.7", "fastify": "^5.2.1", ... }, "packageManager": "yarn@4.5.1" }
相关代码片段
前端代码
Google登录按钮组件(AuthFormFooter.svelte)
<script> // -- IMPORTS ... // -- FUNCTIONS async function signInWithOAuth( provider ) { ... try { let redirectionToUrl; switch ( platform ) { case 'android': redirectionToUrl = 'com.myapp://auth'; break; case 'ios': redirectionToUrl = 'com.myapp://auth'; break; default: redirectionToUrl = 'http://localhost:5173/auth'; } let data = await fetchData( '/api/auth/open-auth', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( { provider, redirectionToUrl } ) } ); if ( data.error ) { console.error( 'Server sign-in error:', data.error ); } else { if ( data.url ) { window.location.href = data.url; } else { console.error( 'Server sign-in error:', data ); } } } catch ( error ) { console.error( errorText, error ); } } </script> <div class="auth-modal-socials"> <div class="auth-modal-socials-item" on:click={() => signInWithOAuth( 'google' )}> <span class="google-logo-icon size-150"></span> </div> </div>
授权回调页面(AuthPage.svelte)
<script> // -- IMPORTS import { onMount } from 'svelte'; import { fetchData } from '$lib/base'; import { navigate } from 'svelte-routing'; // -- FUNCTIONS async function authCallback( code, next ) { try { let response = await fetchData( '/api/auth/callback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( { code } ) } ); if ( response.success ) { navigate( `/${ next.slice( 1 ) }`, { replace: true } ); } else { console.error( 'Authentication failed' ); } } catch ( error ) { console.error( 'Error during authentication', error ); } } onMount( async () => { let params = new URLSearchParams( window.location.search ); let code = params.get( 'code' ); let next = params.get( 'next' ) || '/'; if ( code ) { await authCallback( code, next ); } else { console.error( 'No code found in query params' ); } } ); </script>
服务端代码
Supabase客户端配置(supabase_service.js)
// supabase_service.js class SupabaseService { // -- CONSTRUCTORS constructor( ) { this.client = null; } // -- OPERATIONS initalizeDatabaseClient( request, reply ) { this.client = createServerClient( process.env.SUPABASE_DATABASE_URL, process.env.SUPABASE_DATABASE_KEY, { cookies: { getAll() { return parseCookieHeader( request.headers.cookie ?? '' ); }, setAll( cookiesToSet ) { cookiesToSet.forEach( ( { name, value, options } ) => { let serializedCookie = serializeCookieHeader( name, value, options ); reply.header( 'Set-Cookie', serializedCookie ); } ); } }, auth: { flowType: 'pkce' } } ); } // ~~ getClient( request, reply ) { return this.client; } } // -- VARIABLES export let supabaseService = new SupabaseService();
授权控制器(authentication_controller.js)
// authentication_controller.js ... // -- FUNCTIONS ... // ~~ async function openAuth( request, reply ) { reply.header( 'Access-Control-Allow-Credentials', true ); reply.header( 'Access-Control-Allow-Origin', request.headers.origin ); let { redirectionToUrl, provider } = request.body; try { let { data, error } = await supabaseService.getClient().auth.signInWithOAuth( { provider, options: { redirectTo: redirectionToUrl } } ); if ( data.url ) { let url = data.url; return reply.code( 200 ).send( { url } ); } else { return reply.code( 400 ).send( { error: 'auth-sign-in-failed' } ); } } catch ( error ) { return reply.code( 500 ).send( { error: 'Server error', details: error } ); } } // ~~ async function authCallback( request, reply ) { reply.header( 'Access-Control-Allow-Credentials', true ); reply.header( 'Access-Control-Allow-Origin', request.headers.origin ); let code = request.body.code; let route = request.body.route ?? '/'; try { if ( code ) { let { data, error } = await supabaseService.getClient().auth.exchangeCodeForSession( code ); if ( error ) { return reply.code( 400 ).send( { success: false, error: error.message } ); } return reply.code( 200 ).send( { success: true, route } ); } else { return reply.code( 400 ).send( { success: false, error: 'No code provided' } ); } } catch ( error ) { return reply.code( 500 ).send( { success: false, error: 'Server error', details: error } ); } } // ~~ ... // -- EXPORT export { ..., openAuth, authCallback, ... }
我已经尝试了以下操作,但都没解决问题:
- 更新了所有依赖包到最新版本
- 在Supabase客户端配置中开启了
flowType: 'pkce' - 把cookies选项从原来的get/set/remove改成了getAll/setAll
但还是一直报同样的错误,有没有大佬知道这是哪里出问题了?麻烦帮忙看看,谢谢了!
备注:内容来源于stack exchange,提问作者luisfelipeluis49
相关产品推荐
相关产品推荐

