@supabase/gotrue-js 2.20.1 → 2.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { DEFAULT_HEADERS, EXPIRY_MARGIN, GOTRUE_URL, STORAGE_KEY } from './lib/c
3
3
  import {
4
4
  AuthError,
5
5
  AuthImplicitGrantRedirectError,
6
+ AuthPKCEGrantCodeExchangeError,
6
7
  AuthInvalidCredentialsError,
7
8
  AuthRetryableFetchError,
8
9
  AuthSessionMissingError,
@@ -66,7 +67,7 @@ import type {
66
67
  AuthenticatorAssuranceLevels,
67
68
  Factor,
68
69
  MFAChallengeAndVerifyParams,
69
- OAuthFlowType,
70
+ AuthFlowType,
70
71
  } from './lib/types'
71
72
 
72
73
  polyfillGlobalThis() // Make "globalThis" available
@@ -78,6 +79,7 @@ const DEFAULT_OPTIONS: Omit<Required<GoTrueClientOptions>, 'fetch' | 'storage'>
78
79
  persistSession: true,
79
80
  detectSessionInUrl: true,
80
81
  headers: DEFAULT_HEADERS,
82
+ flowType: 'implicit',
81
83
  }
82
84
 
83
85
  /** Current session will be checked for refresh at this interval. */
@@ -108,6 +110,8 @@ export default class GoTrueClient {
108
110
  */
109
111
  protected inMemorySession: Session | null
110
112
 
113
+ protected flowType: AuthFlowType
114
+
111
115
  protected autoRefreshToken: boolean
112
116
  protected persistSession: boolean
113
117
  protected storage: SupportedStorage
@@ -154,6 +158,7 @@ export default class GoTrueClient {
154
158
  this.headers = settings.headers
155
159
  this.fetch = resolveFetch(settings.fetch)
156
160
  this.detectSessionInUrl = settings.detectSessionInUrl
161
+ this.flowType = settings.flowType
157
162
 
158
163
  this.mfa = {
159
164
  verify: this._verify.bind(this),
@@ -208,8 +213,9 @@ export default class GoTrueClient {
208
213
  }
209
214
 
210
215
  try {
211
- if (this.detectSessionInUrl && this._isImplicitGrantFlow()) {
212
- const { data, error } = await this._getSessionFromUrl()
216
+ const isPKCEFlow = await this._isPKCEFlow()
217
+ if ((this.detectSessionInUrl && this._isImplicitGrantFlow()) || isPKCEFlow) {
218
+ const { data, error } = await this._getSessionFromUrl(isPKCEFlow)
213
219
 
214
220
  if (error) {
215
221
  // failed login attempt via url,
@@ -323,7 +329,7 @@ export default class GoTrueClient {
323
329
  /**
324
330
  * Log in an existing user with an email and password or phone and password.
325
331
  *
326
- * Be aware that you may get back an error message that will not distingish
332
+ * Be aware that you may get back an error message that will not distinguish
327
333
  * between the cases where the account does not exist or that the
328
334
  * email/phone and password combination is wrong or that the account can only
329
335
  * be accessed via social login.
@@ -386,7 +392,7 @@ export default class GoTrueClient {
386
392
  scopes: credentials.options?.scopes,
387
393
  queryParams: credentials.options?.queryParams,
388
394
  skipBrowserRedirect: credentials.options?.skipBrowserRedirect,
389
- flowType: credentials.options?.flowType ?? 'implicit',
395
+ flowType: this.flowType ?? 'implicit',
390
396
  })
391
397
  }
392
398
 
@@ -394,7 +400,7 @@ export default class GoTrueClient {
394
400
  * Log in an existing user via a third-party provider.
395
401
  */
396
402
  async exchangeCodeForSession(authCode: string): Promise<AuthResponse> {
397
- const codeVerifier = await getItemAsync(this.storage, `${this.storageKey}-oauth-code-verifier`)
403
+ const codeVerifier = await getItemAsync(this.storage, `${this.storageKey}-code-verifier`)
398
404
  const { data, error } = await _request(
399
405
  this.fetch,
400
406
  'POST',
@@ -408,7 +414,7 @@ export default class GoTrueClient {
408
414
  xform: _sessionResponse,
409
415
  }
410
416
  )
411
- await removeItemAsync(this.storage, `${this.storageKey}-oauth-code-verifier`)
417
+ await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
412
418
  if (error || !data) return { data: { user: null, session: null }, error }
413
419
  if (data.session) {
414
420
  await this._saveSession(data.session)
@@ -845,7 +851,7 @@ export default class GoTrueClient {
845
851
  /**
846
852
  * Gets the session data from a URL string
847
853
  */
848
- private async _getSessionFromUrl(): Promise<
854
+ private async _getSessionFromUrl(isPKCEFlow: boolean): Promise<
849
855
  | {
850
856
  data: { session: Session; redirectType: string | null }
851
857
  error: null
@@ -854,8 +860,18 @@ export default class GoTrueClient {
854
860
  > {
855
861
  try {
856
862
  if (!isBrowser()) throw new AuthImplicitGrantRedirectError('No browser detected.')
857
- if (!this._isImplicitGrantFlow()) {
863
+ if (this.flowType == 'implicit' && !this._isImplicitGrantFlow()) {
858
864
  throw new AuthImplicitGrantRedirectError('Not a valid implicit grant flow url.')
865
+ } else if (this.flowType == 'pkce' && !isPKCEFlow) {
866
+ throw new AuthPKCEGrantCodeExchangeError('Not a valid PKCE flow url.')
867
+ }
868
+ if (isPKCEFlow) {
869
+ const authCode = getParameterByName('code')
870
+ if (!authCode) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
871
+ const { data, error } = await this.exchangeCodeForSession(authCode)
872
+ if (error) throw error
873
+ if (!data.session) throw new AuthPKCEGrantCodeExchangeError('No session detected.')
874
+ return { data: { session: data.session, redirectType: null }, error: null }
859
875
  }
860
876
 
861
877
  const error_description = getParameterByName('error_description')
@@ -920,6 +936,16 @@ export default class GoTrueClient {
920
936
  Boolean(getParameterByName('error_description')))
921
937
  )
922
938
  }
939
+ /**
940
+ * Checks if the current URL and backing storage contain parameters given by a PKCE flow
941
+ */
942
+ private async _isPKCEFlow(): Promise<boolean> {
943
+ const currentStorageContent = await getItemAsync(
944
+ this.storage,
945
+ `${this.storageKey}-code-verifier`
946
+ )
947
+ return isBrowser() && Boolean(getParameterByName('code')) && Boolean(currentStorageContent)
948
+ }
923
949
 
924
950
  /**
925
951
  * Inside a browser context, `signOut()` will remove the logged in user from the browser session
@@ -1073,7 +1099,7 @@ export default class GoTrueClient {
1073
1099
  scopes?: string
1074
1100
  queryParams?: { [key: string]: string }
1075
1101
  skipBrowserRedirect?: boolean
1076
- flowType: OAuthFlowType
1102
+ flowType: AuthFlowType
1077
1103
  }
1078
1104
  ) {
1079
1105
  const url: string = await this._getUrlForProvider(provider, {
@@ -1392,7 +1418,7 @@ export default class GoTrueClient {
1392
1418
  redirectTo?: string
1393
1419
  scopes?: string
1394
1420
  queryParams?: { [key: string]: string }
1395
- flowType: OAuthFlowType
1421
+ flowType: AuthFlowType
1396
1422
  }
1397
1423
  ) {
1398
1424
  const urlParams: string[] = [`provider=${encodeURIComponent(provider)}`]
@@ -1404,7 +1430,7 @@ export default class GoTrueClient {
1404
1430
  }
1405
1431
  if (options?.flowType === 'pkce') {
1406
1432
  const codeVerifier = generatePKCEVerifier()
1407
- await setItemAsync(this.storage, `${this.storageKey}-oauth-code-verifier`, codeVerifier)
1433
+ await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier)
1408
1434
  const codeChallenge = await generatePKCEChallenge(codeVerifier)
1409
1435
  const flowParams = new URLSearchParams({
1410
1436
  flow_type: `${encodeURIComponent(options.flowType)}`,
package/src/lib/errors.ts CHANGED
@@ -92,6 +92,23 @@ export class AuthImplicitGrantRedirectError extends CustomAuthError {
92
92
  }
93
93
  }
94
94
 
95
+ export class AuthPKCEGrantCodeExchangeError extends CustomAuthError {
96
+ details: { error: string; code: string } | null = null
97
+ constructor(message: string, details: { error: string; code: string } | null = null) {
98
+ super(message, 'AuthPKCEGrantCodeExchangeError', 500)
99
+ this.details = details
100
+ }
101
+
102
+ toJSON() {
103
+ return {
104
+ name: this.name,
105
+ message: this.message,
106
+ status: this.status,
107
+ details: this.details,
108
+ }
109
+ }
110
+ }
111
+
95
112
  export class AuthRetryableFetchError extends CustomAuthError {
96
113
  constructor(message: string, status: number) {
97
114
  super(message, 'AuthRetryableFetchError', status)
package/src/lib/types.ts CHANGED
@@ -29,7 +29,6 @@ export type AuthChangeEvent =
29
29
  | 'SIGNED_OUT'
30
30
  | 'TOKEN_REFRESHED'
31
31
  | 'USER_UPDATED'
32
- | 'USER_DELETED'
33
32
  | AuthChangeEventMFA
34
33
 
35
34
  export type GoTrueClientOptions = {
@@ -49,6 +48,8 @@ export type GoTrueClientOptions = {
49
48
  storage?: SupportedStorage
50
49
  /* A custom fetch implementation. */
51
50
  fetch?: Fetch
51
+ /* If set to 'pkce' PKCE flow. Defaults to the 'implicit' flow otherwise */
52
+ flowType?: AuthFlowType
52
53
  }
53
54
 
54
55
  export type AuthResponse =
@@ -435,7 +436,7 @@ export type SignInWithPasswordlessCredentials =
435
436
  }
436
437
  }
437
438
 
438
- export type OAuthFlowType = 'implicit' | 'pkce'
439
+ export type AuthFlowType = 'implicit' | 'pkce'
439
440
  export type SignInWithOAuthCredentials = {
440
441
  /** One of the providers supported by GoTrue. */
441
442
  provider: Provider
@@ -448,8 +449,6 @@ export type SignInWithOAuthCredentials = {
448
449
  queryParams?: { [key: string]: string }
449
450
  /** If set to true does not immediately redirect the current browser context to visit the OAuth authorization page for the provider. */
450
451
  skipBrowserRedirect?: boolean
451
- /** If set to 'pkce' PKCE flow. Defaults to the 'implicit' flow otherwise */
452
- flowType?: OAuthFlowType
453
452
  }
454
453
  }
455
454
 
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '2.20.1'
2
+ export const version = '2.20.2'