Skip to content

Instantly share code, notes, and snippets.

@TheOtherBrian1
Last active April 6, 2025 19:03
Show Gist options
  • Select an option

  • Save TheOtherBrian1/93b5ba56bc64075f628fc2d9e94c33a3 to your computer and use it in GitHub Desktop.

Select an option

Save TheOtherBrian1/93b5ba56bc64075f628fc2d9e94c33a3 to your computer and use it in GitHub Desktop.

If you used one of the Supabase Clients, such as for JS, Python, or Swift, you may have seen a request that looks something like this:

const { data, error } = await supabase
  .from('countries')
  .select()

These clients are (for the most part) wrappers around REST endpoints. The above query, loosely translates to:

curl 'https://<PROJECT REF>.supabase.co/rest/v1/<TABLE>?select=*' \

-H "apikey: <SERVICE_ROLE KEY | ANON KEY>" \

-H "Authorization: Bearer <SERVICE_ROLE KEY | ANON KEY | AUTH TOKEN>"

The -H stands for headers and when making requests, two headers responsible for authorization are:

  • apikey
  • Authorization: Bearer

The apikey determines the type of user:

  • anon: anonymous
  • service_role: admin

The Authorization: Bearer, contains additional information that can introduce another user type: authenticted. The combination of apikey and auth headers determines the user's access privleges:

Here are all the combinations:

anon user:

  • apikey: ANON KEY
  • Authorization: Bearer ANON KEY

authenticated user:

  • apikey: ANON KEY | SERVICE_ROLE KEY
  • Authorization: Bearer <AUTH_TOKEN>

service_role:

  • apikey: SERVICE_ROLE KEY
  • Authorization: Bearer: SERVICE_ROLE KEY

In general, if you need to use both the service_role and authenticate users, you should create a seperate client exclusively for the service_role.

In some frameworks, especially meta-frameworks (next, sveltekit, nuxt), if this isn't done properly, the service_role client's Authorization: Bearer can be overwritten with a user's JWT session. This will downgrade the service_role's permission to that of an authenticated user.

To test if you're making requests with the service_role as the apikey, but the Authorization Bearer as the anon or authenticated user, you can run this query in the log explorer:

select
  cast(timestamp as datetime) as timestamp,
  cf_connecting_ip AS ip,
  x_client_info as client_library,
  auth_user as user_id,
  path,
  url as full_url,
  payload.role AS client_library_token,
  bearer_payload.role AS session_token
from edge_logs
  cross join unnest(metadata) as m
  cross join unnest(m.request) as r
  cross join unnest(r.headers) as h
  cross join unnest(r.sb) as s
  cross join unnest(s.jwt) as jwt
  cross join unnest(jwt.apikey) as apikey
  cross join unnest(apikey.payload) as payload
  cross join unnest(jwt.authorization) as authorization
  cross join unnest(authorization.payload) as bearer_payload
where 
  (payload.role = 'service_role' OR bearer_payload.role = 'service_role')
    AND
  (payload.role <> bearer_payload.role)
order by
  timestamp desc
limit 100;

Note, logs are collected in batches, so it may take up to 5 minutes before new ones are avaiable

When using the log explorer, please set an appropriate time range: Screenshot 2024-06-30 at 1 47 20 PM

Logs returned by queries may be difficult to read in table format. A row can be double-clicked to expand the results into more readable JSON:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment