supabase-js: AuthApiError: invalid claim: missing sub claim
Bug report
Describe the bug
Credentials are malformed are not being stored properly and therefore the client is not able to lately resolve the session
After calling supabase.auth.signInWithPassword({email, password})
this strangely formatted token is generated
'["eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjY4NjAyNTM1LCJzdWIiOiJlYThiMjkxYS0wZTVkLTQ0YmEtYmViYi0zOGViYTQ1Y2UyOTgiLCJlbWFpbCI6ImFsZXh2Y2FzaWxsYXNAZ21haWwuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIiwic2Vzc2lvbl9pZCI6ImFkN2FmYzRiLTZmYzQtNGQxOC04OWJiLTRiYmM1YmNmMzUxMyJ9.LK3i6Dyh-dZX-yk4NvgEdxYwmxsoOnAk1vfK-0Nmoe4","ktuUPxuJF-PHPhRpWfvUfQ",null,null]'
And later on, doing await supabase.auth.setSession(token);
throws the following error:
{
error: AuthApiError: invalid claim: missing sub claim
at /home/alexvcasillas/otpfy/otpfy-v2/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:41:20
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
__isAuthError: true,
status: 401
}
}
This is the versions that I’m currently using:
"@supabase/auth-helpers-nextjs": "^0.5.2",
"@supabase/auth-helpers-react": "^0.3.1",
"@supabase/supabase-js": "^2.1.0",
This worked before with versions:
"@supabase/auth-helpers-nextjs": "^0.4.0-next.4",
"@supabase/auth-helpers-react": "^0.3.0-next.4",
"@supabase/supabase-js": "^2.0.0",
And I have my product live without issues https://otpfy.com, but after upgrading everything to latest, it stopped working as expected 🤔
To Reproduce
Steps to reproduce the behavior, please provide code snippets or a repository:
I’m using next
for everything related
Sign in a user server-side
import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { Database } from "../../types/database";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { email, password } = req.body;
if (!email || !password) {
res.status(400).send({ data: null });
return;
}
const supabaseServerClient = createServerSupabaseClient<Database>({
req,
res,
});
const { data, error } = await supabaseServerClient.auth.signInWithPassword({
email,
password,
});
if (error) {
res.status(500).send({ data: null, error: error.message });
return;
}
res.status(200).send({ data: data });
}
Try to see if the user is authenticated after server-side:
import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { fetchUser } from "../../services/user";
import { Database } from "../../types/database";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const supabaseServerClient = createServerSupabaseClient<Database>({
req,
res,
});
const supabaseAuthToken = req.headers["supabase-auth-token"];
if (supabaseAuthToken === "undefined") {
res.status(401).send({ data: null });
return;
}
const token = JSON.parse(supabaseAuthToken as string) as SupabaseAuthToken;
await supabaseServerClient.auth.setSession(token);
const {
data: { user },
error,
} = await supabaseServerClient.auth.getUser(token.access_token);
if (error) {
res.status(401).send({ data: null });
return;
}
if (user) {
const userData = await fetchUser(user.id, supabaseServerClient);
res.status(200).send({ data: userData ?? null });
return;
}
res.status(500).send({ data: null });
}
Expected behavior
I should be able to sign up a user and work fine as expected
System information
- OS: Windows
- Browser (if applies): Chrome 107.0.5304.107
- Version of supabase-js: 2.1.0
- Version of Node.js: 16.15.0 on the local environment. 16.x on Vercel
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 8
- Comments: 19 (6 by maintainers)
Too bad to see that this error still exists. Especially when making a POST request inside a Next js /app dir.
How do we get around this? 🤔
Yeah, there’s no good docs on this. For
getUser()
to work server-side,persistSession
needs to befalse
.Not sure if this is a perfect solution, but I made some option-changes to the call to
createClient
, which seems to have fixed the issue for me.It seems that after I added
persistSession: false
thegetUser
call no longer resulted in an error.@Spiralis @j4w8n thanks for bringing this up folks, I’m passing this as a feedback to the Auth team 😃
After following this example from the
auth-helpers
repository I managed to have all these features fully working and working like I charm I have to say.Having upgraded to Next 13 seemed to be the root cause of this issue as now I can see that the
supabase-auth-token
is looking the same way as the one that I pointed out to be “malformed” because it didn’t look like in the previous version.I think it’s safe to close this bug report as it relates to using
supabase-js
the same way as we did fornext<13
, which now is different and I’ve figured it out thanks to the given example. Hope others can find this example helpful too 😃I can’t reproduce the malformed credential. I’m using OAuth instead of email/pass; but I’d assume a JWT is created with the same code, no matter which is used.
As for
setSession()
, we’re now required to pass in a “session”. But not a whole session, just theaccess_token
andrefresh_token
. The docs have not been fully updated to reflect this, but the Parameters section is correct.Any workarounds? Using supabase on both server and client log-outs users from time to time, really annoying 😦 @awalias can we please reopen this issue since the error still occurs?
I ran into this issue tonight, getting
invalid claim: missing sub claim
, and ended up on this issue so I’ll add my solution here for intrepid explorers 😄I’m doing something similar to what was mentioned above with a client that logs in via Supabase, then sends the access/refresh tokens to a separate server.
The problem for me was that
client.auth.setSession
returns aPromise
! I was just missing anawait
…No problem @soedirgo. What I said is only true if you don’t pass an access token into getUser. Because in that case, it calls getSession; which is where things really breakdown.
Gary opened an issue about it https://github.com/supabase/gotrue-js/issues/539
There are also issues with using setSession on the server side without setting persistSession to false. Not because of that method’s direct code, but because it calls
_saveSession
and it’s code causes issues.I’m having that issue using supabase with expo. When I set
persistSession: false
authentication works but I definitely want to persist my session. Any idea if there is another way to fix?I was getting this error because I have an Express backend and want to attach the user object in Express middleware. I am passing the accessToken / refreshToken via the serverSideRendering docs.
In the middlware, I was calling
Adding this flag (as referenced above) seems to have fixed it:
I am having the same issue. But I am not in NextJS-land.
I am working on a server-side Solid Start solution, using cookies. Having said that the reproducing sample is just about identical.
The only difference is that I am getting the access_token and refresh_token from a cookie.
The weird thing is that the
setSession
call completes fine (I am passing an object with the access and header tokens). It even returns the session-data and the error property is indeed null.The error comes from the
getUser()
call. And it is exactly the same as described in this issue.A few days ago when I started working on the code, it was working too (AFAICT). I first then started to see this a few times and now it is there every time.
Is it necessary to even call getUser? The samples all show the setSession call without using the returned data, which includes the user. Can’t I just use the user returned?
In this app I am not exposing the supabase-client at all in the browser. All the code is run server-side. I am creating a secure cookie that contains the access and refresh tokens (encrypted). So, every request from the client to the server will pass that cookie and I will then be able to authenticate the user (via
setSession
).Should I then update the refresh-token in my cookie after the call, if it has changed? As I believe that it may have been rotated - and I guess it should not be reused.