Supabase & NodeJS: Invalid claim: AuthApiError: invalid claim: missing sub claim with getUser(jwt)

203 Views Asked by At

I have a custom backend that needs to authenticate users before letting them request some Express endpoints.

It works that way: NextJS (front-end) sends a request to NodeJS/Express (backend) with a Bearer token (the access_token of the session).

For that, I use the access_token on the frontend thanks to supabase.auth.getSession(). But, when submitting, I get the following error: AuthApiError: invalid claim: missing sub claim

When using the Supabase auth debugger, I have:

GoTrueClient@4 (0.0.0) #_recoverAndRefresh() session from storage null
GoTrueClient@4 (0.0.0) #_recoverAndRefresh() session is not valid
GoTrueClient@4 (0.0.0) #_recoverAndRefresh() end
GoTrueClient@4 (0.0.0) #_handleVisibilityChange()
GoTrueClient@4 (0.0.0) #_initialize() end
GoTrueClient@4 (0.0.0) #_acquireLock lock released for storage key sb-ypvazegvykknqzcjdtcs-auth-token
GoTrueClient@4 (0.0.0) #_acquireLock end
GoTrueClient@4 (0.0.0) #_acquireLock begin -1
GoTrueClient@4 (0.0.0) #_acquireLock lock acquired for storage key sb-ypvazegvykknqzcjdtcs-auth-token
GoTrueClient@4 (0.0.0) #_useSession begin
GoTrueClient@4 (0.0.0) #__loadSession() begin
GoTrueClient@4 (0.0.0) #_acquireLock begin -1
GoTrueClient@4 (0.0.0) #_acquireLock end
GoTrueClient@4 (0.0.0) #getSession() session from storage null
GoTrueClient@4 (0.0.0) #__loadSession() end
GoTrueClient@4 (0.0.0) #_useSession begin
GoTrueClient@4 (0.0.0) #__loadSession() begin
GoTrueClient@4 (0.0.0) INITIAL_SESSION callback id 0af5b134-694b-42fa-bc6d-f63c289b2488 session null
GoTrueClient@4 (0.0.0) #getSession() session from storage null
GoTrueClient@4 (0.0.0) #__loadSession() end
GoTrueClient@4 (0.0.0) #_useSession end
GoTrueClient@4 (0.0.0) #_useSession end

Here is the complete Middleware code:

const { createClient } = require("@supabase/supabase-js");
const jwtDecode = require("jwt-decode");

async function authenticate(req, res, next) {
  try {
    // Créer une instance de Supabase client
    const supabase = createClient(
      "REMOVED_FOR_PRIVACY",
      "REMOVED_FOR_PRIVACY",
      {
        auth: {
          autoRefreshToken: false,
          detectSessionInUrl: false,
          persistSession: false,
          localStorage: false,
          debug: true
        }
      }
    );
    // Récupérer le jeton d'authentification depuis les en-têtes de la requête
    const token = req.headers.authorization;

    if (!token) {
      return res.status(401).json({ error: "Unauthorized. No token" });
    } else {
      const jwt = token.split("Bearer ").pop();
      // On veut ensuite décrypter le token JWT
      const datadecoded = jwtDecode.jwtDecode(jwt);
      console.log(datadecoded);
      // Vérifier si le jeton est valide en utilisant Supabase
      const response = await supabase.auth.getUser();

      console.log(response);
      if (!response.data.user) {
        // Si l'utilisateur n'est pas authentifié ou s'il y a une erreur, renvoyer une erreur 401
        return res.status(401).json({ error: "Unauthorized. No user" });
      }
      req.user = user;
      next();
    }
  } catch (error) {
    console.error("Error during authentication:", error);
    return res.status(500).json({ error: "Internal Server Error" });
  }
}

module.exports = {
  authenticate
};

I expected to get a User object. But got:

data: { user: null }, error: AuthApiError: invalid claim: missing sub claim

When using jwt.io, or a built-in NodeJS JWT decoder, I get:

{
  "aud": "authenticated",
  "exp": 1709127893,
  "iat": 1709124293,
  "iss": "REMOVED_FOR_PRIVACY",
  "sub": "85441283-ae4e-489d-9a6a-492b6f7748d7",
  "email": "REMOVED_FOR_PRIVACY",
  "phone": "",
  "app_metadata": {
    "provider": "email",
    "providers": [
      "email"
    ]
  },
  "user_metadata": {},
  "role": "authenticated",
  "aal": "aal1",
  "amr": [
    {
      "method": "password",
      "timestamp": 1709111568
    }
  ],
  "session_id": "746acbc5-8d05-4a41-b5a8-fdc21a356ed9"
}

I can see that the error is maybe coming from GoTrue used by Supabase.

1

There are 1 best solutions below

0
dshukertjr On

You can get the auth users like this:

const supabase = createClient('REMOVED_FOR_PRIVACY', 'REMOVED_FOR_PRIVACY', {
  global: { headers: { Authorization: req.headers.get('Authorization')! } },
})

const { data } = await supabaseClient.auth.getUser()