Skip to content

Commit 2c3df35

Browse files
committed
feat: use getSession from gotrue-js
1 parent c43cf1c commit 2c3df35

File tree

5 files changed

+90
-48
lines changed

5 files changed

+90
-48
lines changed

package-lock.json

Lines changed: 9 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@
3838
},
3939
"dependencies": {
4040
"@supabase/functions-js": "^1.3.3",
41-
"@supabase/gotrue-js": "^1.22.14",
41+
"@supabase/gotrue-js": "^1.23.0-next.1",
4242
"@supabase/postgrest-js": "^0.37.2",
4343
"@supabase/realtime-js": "^1.7.2",
44-
"@supabase/storage-js": "^1.7.0"
44+
"@supabase/storage-js": "^1.7.0",
45+
"cross-fetch": "^3.1.5"
4546
},
4647
"devDependencies": {
4748
"@types/jest": "^26.0.13",

src/SupabaseClient.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
import { DEFAULT_HEADERS, STORAGE_KEY } from './lib/constants'
2-
import { stripTrailingSlash, isBrowser } from './lib/helpers'
3-
import { Fetch, GenericObject, SupabaseClientOptions } from './lib/types'
4-
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
5-
import { SupabaseQueryBuilder } from './lib/SupabaseQueryBuilder'
6-
import { SupabaseStorageClient } from '@supabase/storage-js'
71
import { FunctionsClient } from '@supabase/functions-js'
8-
import { PostgrestClient } from '@supabase/postgrest-js'
92
import { AuthChangeEvent } from '@supabase/gotrue-js'
3+
import { PostgrestClient } from '@supabase/postgrest-js'
104
import {
5+
RealtimeChannel,
116
RealtimeClient,
12-
RealtimeSubscription,
137
RealtimeClientOptions,
14-
RealtimeChannel,
8+
RealtimeSubscription,
159
} from '@supabase/realtime-js'
10+
import { SupabaseStorageClient } from '@supabase/storage-js'
11+
import { DEFAULT_HEADERS, STORAGE_KEY } from './lib/constants'
12+
import { fetchWithAuth } from './lib/fetch'
13+
import { isBrowser, stripTrailingSlash } from './lib/helpers'
14+
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
15+
import { SupabaseQueryBuilder } from './lib/SupabaseQueryBuilder'
16+
import { Fetch, SupabaseClientOptions } from './lib/types'
1617

1718
const DEFAULT_OPTIONS = {
1819
schema: 'public',
@@ -89,13 +90,14 @@ export default class SupabaseClient {
8990

9091
this.schema = settings.schema
9192
this.multiTab = settings.multiTab
92-
this.fetch = settings.fetch
9393
this.headers = { ...DEFAULT_HEADERS, ...options?.headers }
9494
this.shouldThrowOnError = settings.shouldThrowOnError || false
9595

9696
this.auth = this._initSupabaseAuthClient(settings)
9797
this.realtime = this._initRealtimeClient({ headers: this.headers, ...settings.realtime })
9898

99+
this.fetch = fetchWithAuth(this._getAccessToken.bind(this), settings.fetch)
100+
99101
this._listenForAuthEvents()
100102
this._listenForMultiTabEvents()
101103

@@ -110,7 +112,7 @@ export default class SupabaseClient {
110112
*/
111113
get functions() {
112114
return new FunctionsClient(this.functionsUrl, {
113-
headers: this._getAuthHeaders(),
115+
headers: this.headers,
114116
customFetch: this.fetch,
115117
})
116118
}
@@ -119,7 +121,7 @@ export default class SupabaseClient {
119121
* Supabase Storage allows you to manage user-generated content, such as photos or videos.
120122
*/
121123
get storage() {
122-
return new SupabaseStorageClient(this.storageUrl, this._getAuthHeaders(), this.fetch)
124+
return new SupabaseStorageClient(this.storageUrl, this.headers, this.fetch)
123125
}
124126

125127
/**
@@ -130,7 +132,7 @@ export default class SupabaseClient {
130132
from<T = any>(table: string): SupabaseQueryBuilder<T> {
131133
const url = `${this.restUrl}/${table}`
132134
return new SupabaseQueryBuilder<T>(url, {
133-
headers: this._getAuthHeaders(),
135+
headers: this.headers,
134136
schema: this.schema,
135137
realtime: this.realtime,
136138
table,
@@ -227,6 +229,12 @@ export default class SupabaseClient {
227229
return { data: { openSubscriptions: openSubCount }, error }
228230
}
229231

232+
private async _getAccessToken() {
233+
const { session } = await this.auth.getSession()
234+
235+
return session?.access_token ?? null
236+
}
237+
230238
private async _closeSubscription(
231239
subscription: RealtimeSubscription | RealtimeChannel
232240
): Promise<{ error: Error | null }> {
@@ -297,21 +305,13 @@ export default class SupabaseClient {
297305

298306
private _initPostgRESTClient() {
299307
return new PostgrestClient(this.restUrl, {
300-
headers: this._getAuthHeaders(),
308+
headers: this.headers,
301309
schema: this.schema,
302310
fetch: this.fetch,
303311
throwOnError: this.shouldThrowOnError,
304312
})
305313
}
306314

307-
private _getAuthHeaders(): GenericObject {
308-
const headers: GenericObject = { ...this.headers }
309-
const authBearer = this.auth.session()?.access_token ?? this.supabaseKey
310-
headers['apikey'] = this.supabaseKey
311-
headers['Authorization'] = headers['Authorization'] || `Bearer ${authBearer}`
312-
return headers
313-
}
314-
315315
private _listenForMultiTabEvents() {
316316
if (!this.multiTab || !isBrowser() || !window?.addEventListener) {
317317
return null

src/lib/fetch.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import crossFetch, { Headers as CrossFetchHeaders } from 'cross-fetch'
2+
3+
type Fetch = typeof fetch
4+
5+
export const resolveFetch = (customFetch?: Fetch): Fetch => {
6+
let _fetch: Fetch
7+
if (customFetch) {
8+
_fetch = customFetch
9+
} else if (typeof fetch === 'undefined') {
10+
_fetch = crossFetch as unknown as Fetch
11+
} else {
12+
_fetch = fetch
13+
}
14+
return (...args) => _fetch(...args)
15+
}
16+
17+
export const resolveHeadersConstructor = () => {
18+
if (typeof Headers === 'undefined') {
19+
return CrossFetchHeaders
20+
}
21+
22+
return Headers
23+
}
24+
25+
export const fetchWithAuth = (
26+
getAccessToken: () => Promise<string | null>,
27+
customFetch?: Fetch
28+
): Fetch => {
29+
const fetch = resolveFetch(customFetch)
30+
const HeadersConstructor = resolveHeadersConstructor()
31+
32+
return async (input, init) => {
33+
const accessToken = await getAccessToken()
34+
let headers = new HeadersConstructor(init?.headers)
35+
36+
if (!headers.has('Authorization') && accessToken) {
37+
headers.set('Authorization', `Bearer ${accessToken}`)
38+
}
39+
40+
return fetch(input, { ...init, headers })
41+
}
42+
}

test/client.test.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createClient, SupabaseClient } from '../src/index'
2-
import { DEFAULT_HEADERS } from '../src/lib/constants'
32

43
const URL = 'http://localhost:3000'
54
const KEY = 'some.fake.key'
@@ -17,37 +16,36 @@ test('it should throw an error if no valid params are provided', async () => {
1716
})
1817

1918
test('it should not cache Authorization header', async () => {
20-
const checkHeadersSpy = jest.spyOn(SupabaseClient.prototype as any, '_getAuthHeaders')
21-
2219
supabase.auth.setAuth('token1')
23-
supabase.rpc('') // Calling public method `rpc` calls private method _getAuthHeaders which result we want to test
24-
supabase.auth.setAuth('token2')
25-
supabase.rpc('') // Calling public method `rpc` calls private method _getAuthHeaders which result we want to test
20+
supabase.rpc('')
21+
expect(supabase.auth.session()?.access_token).toBe('token1')
2622

27-
expect(checkHeadersSpy.mock.results[0].value).toHaveProperty('Authorization', 'Bearer token1')
28-
expect(checkHeadersSpy.mock.results[1].value).toHaveProperty('Authorization', 'Bearer token2')
23+
supabase.auth.setAuth('token2')
24+
supabase.rpc('')
25+
expect(supabase.auth.session()?.access_token).toBe('token2')
2926
})
3027

3128
describe('Custom Headers', () => {
3229
test('should have custom header set', () => {
3330
const customHeader = { 'X-Test-Header': 'value' }
3431

35-
const checkHeadersSpy = jest.spyOn(SupabaseClient.prototype as any, '_getAuthHeaders')
36-
createClient(URL, KEY, { headers: customHeader }).rpc('') // Calling public method `rpc` calls private method _getAuthHeaders which result we want to test
37-
const getHeaders = checkHeadersSpy.mock.results[0].value
32+
const request = createClient(URL, KEY, { headers: customHeader }).rpc('')
33+
34+
// @ts-ignore
35+
const getHeaders = request.headers
3836

39-
expect(checkHeadersSpy).toBeCalled()
4037
expect(getHeaders).toHaveProperty('X-Test-Header', 'value')
4138
})
4239

4340
test('should allow custom Authorization header', () => {
4441
const customHeader = { Authorization: 'Bearer custom_token' }
4542
supabase.auth.setAuth('override_me')
46-
const checkHeadersSpy = jest.spyOn(SupabaseClient.prototype as any, '_getAuthHeaders')
47-
createClient(URL, KEY, { headers: customHeader }).rpc('') // Calling public method `rpc` calls private method _getAuthHeaders which result we want to test
48-
const getHeaders = checkHeadersSpy.mock.results[0].value
4943

50-
expect(checkHeadersSpy).toBeCalled()
44+
const request = createClient(URL, KEY, { headers: customHeader }).rpc('')
45+
46+
// @ts-ignore
47+
const getHeaders = request.headers
48+
5149
expect(getHeaders).toHaveProperty('Authorization', 'Bearer custom_token')
5250
})
5351
})

0 commit comments

Comments
 (0)