Issue #947

Sometimes Row Level Security is not enough and we want to do all logic server side, then we need a way for the server to get hold onto current user token.

Send JWT token in Authorization header

From client, we can get session from supabase auth, then send that as Bearer in Authorization header

const session = await supabase.auth.getSession().data.session
const token = session.access_token

const response = await fetch(url, {
    headers: {
        Authorization: `Bearer ${token}`

Decode token

From the nextjs API Route, we can handle all auth logic in middleware. We will use jwt-decode to decode token


import { NextRequest, NextResponse } from "next/server"
import jwt_decode from 'jwt-decode'

export function middleware(request: NextRequest) {
    const res =

    const authorization = request.headers.get('Authorization')
    const token = authorization?.split(' ')[1] || ''
    const decodedToken = jwt_decode(token) as any
    const userId = decodedToken.sub

    return res

export const config = {
    matcher: "/api/:path*",

The decoded object looks like this, where sub, meaning subject, is usually the user id

  "aud": "authenticated",
  "exp": 1615824388,
  "sub": "0334744a-f2a2-4aba-8c8a-6e748f62a172",
  "email": "",
  "app_metadata": {
    "provider": "email"
  "user_metadata": null,
  "role": "authenticated"

With the user id, we can query Supabase Postgres database to check if this user is valid or not

Validate token

To validate json token, we can use the JWT Secret from Supabase to verify, using jsonwebtoken package

import jwt, { type JwtPayload } from 'jsonwebtoken';

const token = authorization.split('Bearer ').pop()
const user = jwt.verify(token, process.env.SUPABASE_JWT_SECRET) as JwtPayload = user.sub

Get user

In the server, we get initiate createClient with service role key, and call auth.getUser(jwt) to get user

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY, {
    auth: {
        autoRefreshToken: false,
        persistSession: false,

const authorization = request.headers.get("Authorization")!
const token = authorization.split("Bearer ").pop()
const user = await supabase.auth.getUser(token)

Read more