Skip to main content

Supabase JWT Decoder

Paste a Supabase access token to decode its header and payload, inspect auth claims (role, aal, session_id, app_metadata), and check expiry. Everything runs in your browser - the token is never uploaded, which matters because access tokens grant authenticated access to your database through RLS policies.

Access token

Valid for 56m

Header

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

{
  "iss": "https://abcd1234.supabase.co/auth/v1",
  "sub": "0e4a1b2c-3d4e-5f60-7a89-bcdef0123456",
  "aud": "authenticated",
  "exp": 1793941782,
  "iat": 1793938182,
  "email": "[email protected]",
  "role": "authenticated",
  "aal": "aal1",
  "amr": [
    {
      "method": "password",
      "timestamp": 1793938182
    }
  ],
  "session_id": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
  "app_metadata": {
    "provider": "email",
    "providers": [
      "email"
    ]
  },
  "user_metadata": {
    "full_name": "Dev Example"
  }
}

Claims explained

issIssuer

https://abcd1234.supabase.co/auth/v1

Token issuer - your Supabase project URL

subSubject

0e4a1b2c-3d4e-5f60-7a89-bcdef0123456

The authenticated user UUID

audAudience

authenticated

authenticated for user tokens; anon for anon key

expExpiration11/6/2026, 5:09:42 AM

1793941782

When the token expires (default 1 hour)

iatIssued At11/6/2026, 4:09:42 AM

1793938182

When the token was created

emailEmail

[email protected]

User email at sign-in

roleRole

authenticated

authenticated, anon or service_role - drives RLS policies

aalAuth Assurance Level

aal1

aal1 = first factor, aal2 = MFA verified

amrAuth Methods

[{"method":"password","timestamp":1793938182}]

Which methods were used (password, otp, mfa)

session_idSession ID

a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d

Identifies this login session

app_metadataApp Metadata

{"provider":"email","providers":["email"]}

Provider data, provider_id, roles - writable only server-side

user_metadataUser Metadata

{"full_name":"Dev Example"}

Profile data the user can update

Common Supabase JWT errors

Invalid JWT: expired - access tokens last one hour by default. Your app should refresh via the Supabase client SDK; if you call the API directly, detect 401s and refresh before retrying.

RLS returns empty rows - check the role claim. Only authenticated tokens pass policies written TO authenticated; the anon key has role: anon.

New columns invisible - JWTs are minted at sign-in and cached; user_metadata changes need a session refresh or getSession re-fetch to appear.

Never expose service_role - a service_role JWT bypasses RLS entirely. It belongs in server-side code only; if one ever reaches a browser, rotate it in project settings immediately.

FAQ

How do I decode a Supabase JWT?

Paste it above. The middle segment is Base64URL-encoded JSON - this page decodes it locally and explains each claim. No server is involved, so the token is never exposed.

How long does a Supabase access token last?

One hour by default (configurable via JWT expiry in the dashboard). The refresh token is longer-lived and rotates on use.

Can this tool verify the JWT signature?

No, and be suspicious of any client-side tool that claims to - signature verification requires your project JWT secret, which you should never paste into a website. For verification use your Supabase client or a general JWT debugger with an explicit secret in a local context.

What is the difference between anon key, access token and service_role?

anon key: unauthenticated requests (role: anon). Access token: per-user session JWT (role: authenticated). service_role: bypasses RLS, server-side only.