Skip to content

Commit fa97643

Browse files
authored
feat: add third-party auth support (#1004)
1 parent 51cd986 commit fa97643

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

src/SupabaseClient.ts

+28-6
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export default class SupabaseClient<
5151
protected storageKey: string
5252
protected fetch?: Fetch
5353
protected changedAccessToken?: string
54+
protected accessToken?: () => Promise<string>
5455

5556
protected headers: Record<string, string>
5657

@@ -95,11 +96,26 @@ export default class SupabaseClient<
9596
this.storageKey = settings.auth.storageKey ?? ''
9697
this.headers = settings.global.headers ?? {}
9798

98-
this.auth = this._initSupabaseAuthClient(
99-
settings.auth ?? {},
100-
this.headers,
101-
settings.global.fetch
102-
)
99+
if (!settings.accessToken) {
100+
this.auth = this._initSupabaseAuthClient(
101+
settings.auth ?? {},
102+
this.headers,
103+
settings.global.fetch
104+
)
105+
} else {
106+
this.accessToken = settings.accessToken
107+
108+
this.auth = new Proxy<SupabaseAuthClient>({} as any, {
109+
get: (_, prop) => {
110+
throw new Error(
111+
`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(
112+
prop
113+
)} is not possible`
114+
)
115+
},
116+
})
117+
}
118+
103119
this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.global.fetch)
104120

105121
this.realtime = this._initRealtimeClient({ headers: this.headers, ...settings.realtime })
@@ -109,7 +125,9 @@ export default class SupabaseClient<
109125
fetch: this.fetch,
110126
})
111127

112-
this._listenForAuthEvents()
128+
if (!settings.accessToken) {
129+
this._listenForAuthEvents()
130+
}
113131
}
114132

115133
/**
@@ -244,6 +262,10 @@ export default class SupabaseClient<
244262
}
245263

246264
private async _getAccessToken() {
265+
if (this.accessToken) {
266+
return await this.accessToken()
267+
}
268+
247269
const { data } = await this.auth.getSession()
248270

249271
return data.session?.access_token ?? null

src/lib/helpers.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function applySettingDefaults<
3737
global: DEFAULT_GLOBAL_OPTIONS,
3838
} = defaults
3939

40-
return {
40+
const result: Required<SupabaseClientOptions<SchemaName>> = {
4141
db: {
4242
...DEFAULT_DB_OPTIONS,
4343
...dbOptions,
@@ -54,5 +54,15 @@ export function applySettingDefaults<
5454
...DEFAULT_GLOBAL_OPTIONS,
5555
...globalOptions,
5656
},
57+
accessToken: async () => '',
5758
}
59+
60+
if (options.accessToken) {
61+
result.accessToken = options.accessToken
62+
} else {
63+
// hack around Required<>
64+
delete (result as any).accessToken
65+
}
66+
67+
return result
5868
}

src/lib/types.ts

+12
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ export type SupabaseClientOptions<SchemaName> = {
6666
*/
6767
headers?: Record<string, string>
6868
}
69+
/**
70+
* Optional function for using a third-party authentication system with
71+
* Supabase. The function should return an access token or ID token (JWT) by
72+
* obtaining it from the third-party auth client library. Note that this
73+
* function may be called concurrently and many times. Use memoization and
74+
* locking techniques if this is not supported by the client libraries.
75+
*
76+
* When set, the `auth` namespace of the Supabase client cannot be used.
77+
* Create another client if you wish to use Supabase Auth and third-party
78+
* authentications concurrently in the same application.
79+
*/
80+
accessToken?: () => Promise<string>
6981
}
7082

7183
export type GenericTable = {

test/client.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ const KEY = 'some.fake.key'
77

88
const supabase = createClient(URL, KEY)
99

10+
test('it should create a client with third-party auth accessToken', async () => {
11+
const client = createClient(URL, KEY, {
12+
accessToken: async () => {
13+
return 'jwt'
14+
},
15+
})
16+
17+
expect(() => client.auth.getUser()).toThrowError(
18+
'@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.getUser is not possible'
19+
)
20+
})
21+
1022
test('it should create the client connection', async () => {
1123
expect(supabase).toBeDefined()
1224
expect(supabase).toBeInstanceOf(SupabaseClient)

0 commit comments

Comments
 (0)