auth-helpers: SvelteKit Auth helper: PKCE flow doesn't work with generateLink (type magiclink)

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

I migrated from v0.9 to v0.10 following this guide, reviewing my auth code overall, not just the migration part. https://supabase.com/docs/guides/auth/auth-helpers/sveltekit

but this:

const genLink = await supabase.auth.admin.generateLink({
      type: 'magiclink',
      email: email,
      options: {
         redirectTo: '.../callback'
      }
    })

doesn’t seem to work anymore. genLink.data.properties.action_link doesn’t contain the code parameter, which I assume should be present for exchangeCodeForSession in the callback server-side code.

What am I doing wrong?

To Reproduce

  1. Generate a magic link with auth.admin.generateLink via Supabase JS client with Service Key
  2. Sent it via custom email service to the user (we use Postmark)
  3. User clicks on the link
  4. User is redirected to the callback route with exchangeCodeForSession
  5. url.searchParams.get('code') returns nothing

I inspected the generated URL as present in the email sent to the user and it has the correct redirect_to parm, but doesn’t have the code parm.

UPDATE: I also made sure to include this, when creating the client, but no luck:

auth: {
    flowType: 'pkce',
  },

Expected behavior

Authenticate user upon clicking on the link directly on the server, as intended by the new PKCE flow. It was working fine previously with generateLink just to be clear.

System information

  • Version of supabase-js: latest
  • Version of SvelteKit Auth Helper: v0.10

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 2
  • Comments: 22 (5 by maintainers)

Most upvoted comments

A temporary workaround is to manually set the user session based on the access token:

const searchParams = new URLSearchParams($page.url.hash.replace('#', '?'))

const { error } = await supabase.auth.setSession({
    access_token: searchParams.get('access_token'),
    refresh_token: searchParams.get('refresh_token')
})

However, I’d much more like this to be fixed so it works the intended way.

For me, using the signInWithOtp works with the callback going to /auth/callback for the supabase.auth.exchangeCodeForSession().

However, as also described above, when using auth.admin.generateLink as following:

await supabaseAdmin.auth.admin.generateLink({
        type: 'magiclink',
        email,
        options: {
            redirectTo: `${url.origin}/auth/callback`
        }
    })

it seems the callback to /auth/callback does not contain any search params. Instead, I get #access_token hash on the url and it still does not trigger onAuthStateChange. - It does not log in the user.

Thanks to the guide mentioned by @silentworks, I figured it out and documented the process here: https://catjam.fi/articles/supabase-generatelink-fix

Thank you @wyozi – were are still using the /pages router and no server components and were able to adapt your example relatively easily!

Closing this one out as I believe it has been resolved by the link to the guide I posted above.

https://github.com/supabase/auth-helpers/issues/610#issuecomment-1704456113

@silentworks thanks for the reply! As for blog post, it was the post above mine by wyozi, that tried to elaborate on what is going on.

As for your code, you are using verifyOtp, without any email or phone value? How is this possible? I get an error? Is it because of my version of the library? EDIT: I just upgraded to the same versions as you and I still get AuthApiError: Only an email address or phone number should be provided on verify

The only different think I do from you is that i generateLink(), type:invite, and take the token hash from there and send as an email through an i18n email function.

But that should not make any difference?

What is the expected outcome? The user loads the link, the server verifys and gives user a session so he can access authroized urls right?

Got it to work

So the versions do have something to say. The types was updated.

Also its important to not send in token, but token_hash, that is where I got confused. I found the log error in the source code and it got me on the right track.

Thanks for the rubberducking 😄

bump!

@peachp @yuvalkarmi @erik-jenkins @nathancahill

gotrue-js 2.44.1 is supposed to address this. Was released some time yesterday. You might need to do an npm/pnpm override to get it installed for testing.

https://github.com/supabase/gotrue-js/pull/722

I’m experiencing this issue as well (though with the react auth helper).

Digging through the code, looks like the request generateLink sends a request to the supabase admin api endpoint (e.g. https://your_database_id.supabase.co/auth/v1/admin/generate_link), which seemingly ignores the param flowType in the request body, no matter what it is set to.

I’m also seeing this - looks like the callback URL is getting called with the implicit auth flow URL params: https://supabase.com/docs/guides/auth/server-side-rendering#understanding-the-authentication-flow

Not sure what’s causing this, since it looks like both the server and client supabase clients are created with the auth flow hardcoded to 'pkce'

nope, both are quite unrelated, but thanks for trying Mr. AI 😛