Skip to content

Commit 7563e9a

Browse files
committed
feat: do not send non-JWTs in Authorization header
1 parent ce1e2f0 commit 7563e9a

File tree

3 files changed

+74
-5
lines changed

3 files changed

+74
-5
lines changed

src/SupabaseClient.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ import {
1919
DEFAULT_REALTIME_OPTIONS,
2020
} from './lib/constants'
2121
import { fetchWithAuth } from './lib/fetch'
22-
import { stripTrailingSlash, applySettingDefaults } from './lib/helpers'
22+
import {
23+
stripTrailingSlash,
24+
applySettingDefaults,
25+
isJWT,
26+
checkAuthorizationHeader,
27+
} from './lib/helpers'
2328
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
2429
import { Fetch, GenericSchema, SupabaseClientOptions, SupabaseAuthClientOptions } from './lib/types'
2530

@@ -96,6 +101,8 @@ export default class SupabaseClient<
96101
this.storageKey = settings.auth.storageKey ?? ''
97102
this.headers = settings.global.headers ?? {}
98103

104+
checkAuthorizationHeader(this.headers)
105+
99106
if (!settings.accessToken) {
100107
this.auth = this._initSupabaseAuthClient(
101108
settings.auth ?? {},
@@ -285,10 +292,14 @@ export default class SupabaseClient<
285292
headers?: Record<string, string>,
286293
fetch?: Fetch
287294
) {
288-
const authHeaders = {
289-
Authorization: `Bearer ${this.supabaseKey}`,
295+
const authHeaders: { [header: string]: string } = {
290296
apikey: `${this.supabaseKey}`,
291297
}
298+
299+
if (isJWT(this.supabaseKey)) {
300+
authHeaders.Authorization = `Bearer ${this.supabaseKey}`
301+
}
302+
292303
return new SupabaseAuthClient({
293304
url: this.authUrl,
294305
headers: { ...authHeaders, ...headers },

src/lib/fetch.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-ignore
22
import nodeFetch, { Headers as NodeFetchHeaders } from '@supabase/node-fetch'
3+
import { isJWT } from './helpers'
34

45
type Fetch = typeof fetch
56

@@ -31,15 +32,17 @@ export const fetchWithAuth = (
3132
const fetch = resolveFetch(customFetch)
3233
const HeadersConstructor = resolveHeadersConstructor()
3334

35+
const defaultAccessToken = isJWT(supabaseKey) ? supabaseKey : null
36+
3437
return async (input, init) => {
35-
const accessToken = (await getAccessToken()) ?? supabaseKey
38+
const accessToken = (await getAccessToken()) ?? defaultAccessToken
3639
let headers = new HeadersConstructor(init?.headers)
3740

3841
if (!headers.has('apikey')) {
3942
headers.set('apikey', supabaseKey)
4043
}
4144

42-
if (!headers.has('Authorization')) {
45+
if (!headers.has('Authorization') && accessToken) {
4346
headers.set('Authorization', `Bearer ${accessToken}`)
4447
}
4548

src/lib/helpers.ts

+55
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,58 @@ export function applySettingDefaults<
6666

6767
return result
6868
}
69+
70+
export const BASE64URL_REGEX = /^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}$|[a-z0-9_-]{2}$)$/i
71+
72+
/**
73+
* Checks that the value somewhat looks like a JWT, does not do any additional parsing or verification.
74+
*/
75+
export function isJWT(value: string): boolean {
76+
if (value.startsWith('Bearer ')) {
77+
value = value.substring('Bearer '.length)
78+
}
79+
80+
value = value.trim()
81+
82+
if (!value) {
83+
return false
84+
}
85+
86+
const parts = value.split('.')
87+
88+
if (parts.length !== 3) {
89+
return false
90+
}
91+
92+
for (let i = 0; i < parts.length; i += 1) {
93+
const part = parts[i]
94+
95+
if (part.length < 4 || !parts[i].match(BASE64URL_REGEX)) {
96+
return false
97+
}
98+
}
99+
100+
return true
101+
}
102+
103+
export function checkAuthorizationHeader(headers: { [header: string]: string }) {
104+
if (headers.authorization && headers.Authorization) {
105+
console.warn(
106+
'@supabase-js: Both `authorization` and `Authorization` headers specified in createClient options. `Authorization` will be used.'
107+
)
108+
}
109+
110+
const authorization = headers.Authorization ?? headers.authorization ?? null
111+
112+
if (!authorization) {
113+
return
114+
}
115+
116+
if (authorization.startsWith('Bearer ') && authorization.length > 'Bearer '.length) {
117+
if (!isJWT(authorization)) {
118+
throw new Error(
119+
'@supabase-js: createClient called with global Authorization header that does not contain a JWT'
120+
)
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)