@supabase/gotrue-js 2.42.0 → 2.43.0
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.
- package/dist/main/GoTrueClient.d.ts +13 -0
- package/dist/main/GoTrueClient.d.ts.map +1 -1
- package/dist/main/GoTrueClient.js +280 -221
- package/dist/main/GoTrueClient.js.map +1 -1
- package/dist/main/lib/helpers.d.ts +23 -0
- package/dist/main/lib/helpers.d.ts.map +1 -1
- package/dist/main/lib/helpers.js +94 -1
- package/dist/main/lib/helpers.js.map +1 -1
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/module/GoTrueClient.d.ts +13 -0
- package/dist/module/GoTrueClient.d.ts.map +1 -1
- package/dist/module/GoTrueClient.js +281 -222
- package/dist/module/GoTrueClient.js.map +1 -1
- package/dist/module/lib/helpers.d.ts +23 -0
- package/dist/module/lib/helpers.d.ts.map +1 -1
- package/dist/module/lib/helpers.js +91 -0
- package/dist/module/lib/helpers.js.map +1 -1
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/GoTrueClient.ts +337 -250
- package/src/lib/helpers.ts +111 -0
- package/src/lib/version.ts +1 -1
package/src/GoTrueClient.ts
CHANGED
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
generatePKCEVerifier,
|
|
30
30
|
generatePKCEChallenge,
|
|
31
31
|
supportsLocalStorage,
|
|
32
|
+
stackGuard,
|
|
33
|
+
isInStackGuard,
|
|
32
34
|
} from './lib/helpers'
|
|
33
35
|
import localStorageAdapter from './lib/local-storage'
|
|
34
36
|
import { polyfillGlobalThis } from './lib/polyfills'
|
|
@@ -235,11 +237,11 @@ export default class GoTrueClient {
|
|
|
235
237
|
* manually when checking for an error from an auth redirect (oauth, magiclink, password recovery, etc).
|
|
236
238
|
*/
|
|
237
239
|
initialize(): Promise<InitializeResult> {
|
|
238
|
-
if (
|
|
239
|
-
this.initializePromise
|
|
240
|
+
if (this.initializePromise) {
|
|
241
|
+
return this.initializePromise
|
|
240
242
|
}
|
|
241
243
|
|
|
242
|
-
return this.
|
|
244
|
+
return this._initialize()
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
/**
|
|
@@ -250,63 +252,66 @@ export default class GoTrueClient {
|
|
|
250
252
|
*/
|
|
251
253
|
private async _initialize(): Promise<InitializeResult> {
|
|
252
254
|
if (this.initializePromise) {
|
|
253
|
-
|
|
255
|
+
throw new Error('Double call of #_initialize()')
|
|
254
256
|
}
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
this.initializePromise = stackGuard('_initialize', async () => {
|
|
259
|
+
try {
|
|
260
|
+
const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false
|
|
261
|
+
this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
|
|
259
262
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
|
|
264
|
+
const { data, error } = await this._getSessionFromUrl(isPKCEFlow)
|
|
265
|
+
if (error) {
|
|
266
|
+
this._debug('#_initialize()', 'error detecting session from URL', error)
|
|
264
267
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
// failed login attempt via url,
|
|
269
|
+
// remove old session as in verifyOtp, signUp and signInWith*
|
|
270
|
+
await this._removeSession()
|
|
268
271
|
|
|
269
|
-
|
|
270
|
-
|
|
272
|
+
return { error }
|
|
273
|
+
}
|
|
271
274
|
|
|
272
|
-
|
|
275
|
+
const { session, redirectType } = data
|
|
273
276
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
this._debug(
|
|
278
|
+
'#_initialize()',
|
|
279
|
+
'detected session in URL',
|
|
280
|
+
session,
|
|
281
|
+
'redirect type',
|
|
282
|
+
redirectType
|
|
283
|
+
)
|
|
281
284
|
|
|
282
|
-
|
|
285
|
+
await this._saveSession(session)
|
|
283
286
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
setTimeout(async () => {
|
|
288
|
+
if (redirectType === 'recovery') {
|
|
289
|
+
await this._notifyAllSubscribers('PASSWORD_RECOVERY', session)
|
|
290
|
+
} else {
|
|
291
|
+
await this._notifyAllSubscribers('SIGNED_IN', session)
|
|
292
|
+
}
|
|
293
|
+
}, 0)
|
|
291
294
|
|
|
295
|
+
return { error: null }
|
|
296
|
+
}
|
|
297
|
+
// no login attempt via callback url try to recover session from storage
|
|
298
|
+
await this._recoverAndRefresh()
|
|
292
299
|
return { error: null }
|
|
293
|
-
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
if (isAuthError(error)) {
|
|
302
|
+
return { error }
|
|
303
|
+
}
|
|
294
304
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
305
|
+
return {
|
|
306
|
+
error: new AuthUnknownError('Unexpected error during initialization', error),
|
|
307
|
+
}
|
|
308
|
+
} finally {
|
|
309
|
+
await this._handleVisibilityChange()
|
|
310
|
+
this._debug('#_initialize()', 'end')
|
|
301
311
|
}
|
|
312
|
+
})
|
|
302
313
|
|
|
303
|
-
|
|
304
|
-
error: new AuthUnknownError('Unexpected error during initialization', error),
|
|
305
|
-
}
|
|
306
|
-
} finally {
|
|
307
|
-
await this._handleVisibilityChange()
|
|
308
|
-
this._debug('#_initialize()', 'end')
|
|
309
|
-
}
|
|
314
|
+
return await this.initializePromise
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
/**
|
|
@@ -699,18 +704,20 @@ export default class GoTrueClient {
|
|
|
699
704
|
*/
|
|
700
705
|
async reauthenticate(): Promise<AuthResponse> {
|
|
701
706
|
try {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
707
|
+
return await this._useSession(async (result) => {
|
|
708
|
+
const {
|
|
709
|
+
data: { session },
|
|
710
|
+
error: sessionError,
|
|
711
|
+
} = result
|
|
712
|
+
if (sessionError) throw sessionError
|
|
713
|
+
if (!session) throw new AuthSessionMissingError()
|
|
708
714
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
715
|
+
const { error } = await _request(this.fetch, 'GET', `${this.url}/reauthenticate`, {
|
|
716
|
+
headers: this.headers,
|
|
717
|
+
jwt: session.access_token,
|
|
718
|
+
})
|
|
719
|
+
return { data: { user: null, session: null }, error }
|
|
712
720
|
})
|
|
713
|
-
return { data: { user: null, session: null }, error }
|
|
714
721
|
} catch (error) {
|
|
715
722
|
if (isAuthError(error)) {
|
|
716
723
|
return { data: { user: null, session: null }, error }
|
|
@@ -768,7 +775,55 @@ export default class GoTrueClient {
|
|
|
768
775
|
* Returns the session, refreshing it if necessary.
|
|
769
776
|
* The session returned can be null if the session is not detected which can happen in the event a user is not signed-in or has logged out.
|
|
770
777
|
*/
|
|
771
|
-
async getSession()
|
|
778
|
+
async getSession() {
|
|
779
|
+
return this._useSession(async (result) => {
|
|
780
|
+
return result
|
|
781
|
+
})
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Use instead of {@link #getSession} inside the library. It is
|
|
786
|
+
* semantically usually what you want, as getting a session involves some
|
|
787
|
+
* processing afterwards that requires only one client operating on the
|
|
788
|
+
* session at once across multiple tabs or processes.
|
|
789
|
+
*/
|
|
790
|
+
private async _useSession<R>(
|
|
791
|
+
fn: (
|
|
792
|
+
result:
|
|
793
|
+
| {
|
|
794
|
+
data: {
|
|
795
|
+
session: Session
|
|
796
|
+
}
|
|
797
|
+
error: null
|
|
798
|
+
}
|
|
799
|
+
| {
|
|
800
|
+
data: {
|
|
801
|
+
session: null
|
|
802
|
+
}
|
|
803
|
+
error: AuthError
|
|
804
|
+
}
|
|
805
|
+
| {
|
|
806
|
+
data: {
|
|
807
|
+
session: null
|
|
808
|
+
}
|
|
809
|
+
error: null
|
|
810
|
+
}
|
|
811
|
+
) => Promise<R>
|
|
812
|
+
): Promise<R> {
|
|
813
|
+
return await stackGuard('_useSession', async () => {
|
|
814
|
+
// the use of __loadSession here is the only correct use of the function!
|
|
815
|
+
const result = await this.__loadSession()
|
|
816
|
+
|
|
817
|
+
return await fn(result)
|
|
818
|
+
})
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* NEVER USE DIRECTLY!
|
|
823
|
+
*
|
|
824
|
+
* Always use {@link #_useSession}.
|
|
825
|
+
*/
|
|
826
|
+
private async __loadSession(): Promise<
|
|
772
827
|
| {
|
|
773
828
|
data: {
|
|
774
829
|
session: Session
|
|
@@ -788,11 +843,21 @@ export default class GoTrueClient {
|
|
|
788
843
|
error: null
|
|
789
844
|
}
|
|
790
845
|
> {
|
|
846
|
+
this._debug('#__loadSession()', 'begin')
|
|
847
|
+
|
|
848
|
+
if (this.logDebugMessages && !isInStackGuard('_useSession')) {
|
|
849
|
+
throw new Error('Please use #_useSession()')
|
|
850
|
+
}
|
|
851
|
+
|
|
791
852
|
// make sure we've read the session from the url if there is one
|
|
792
853
|
// save to just await, as long we make sure _initialize() never throws
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
854
|
+
if (!isInStackGuard('_initialize')) {
|
|
855
|
+
// only wait when not called from within #_initialize() since it's
|
|
856
|
+
// waiting for itself. one such pathway is #_initialize() ->
|
|
857
|
+
// #_handleVisibilityChange() -> #_onVisbilityChanged() ->
|
|
858
|
+
// #_loadSession().
|
|
859
|
+
await this.initializePromise
|
|
860
|
+
}
|
|
796
861
|
|
|
797
862
|
try {
|
|
798
863
|
let currentSession: Session | null = null
|
|
@@ -824,7 +889,7 @@ export default class GoTrueClient {
|
|
|
824
889
|
: false
|
|
825
890
|
|
|
826
891
|
this._debug(
|
|
827
|
-
'#
|
|
892
|
+
'#__loadSession()',
|
|
828
893
|
`session has${hasExpired ? '' : ' not'} expired`,
|
|
829
894
|
'expires_at',
|
|
830
895
|
currentSession.expires_at
|
|
@@ -841,7 +906,7 @@ export default class GoTrueClient {
|
|
|
841
906
|
|
|
842
907
|
return { data: { session }, error: null }
|
|
843
908
|
} finally {
|
|
844
|
-
this._debug('#
|
|
909
|
+
this._debug('#__loadSession()', 'end')
|
|
845
910
|
}
|
|
846
911
|
}
|
|
847
912
|
|
|
@@ -851,20 +916,22 @@ export default class GoTrueClient {
|
|
|
851
916
|
*/
|
|
852
917
|
async getUser(jwt?: string): Promise<UserResponse> {
|
|
853
918
|
try {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
919
|
+
return await this._useSession(async (result) => {
|
|
920
|
+
if (!jwt) {
|
|
921
|
+
const { data, error } = result
|
|
922
|
+
if (error) {
|
|
923
|
+
throw error
|
|
924
|
+
}
|
|
859
925
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
926
|
+
// Default to Authorization header if there is no existing session
|
|
927
|
+
jwt = data.session?.access_token ?? undefined
|
|
928
|
+
}
|
|
863
929
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
930
|
+
return await _request(this.fetch, 'GET', `${this.url}/user`, {
|
|
931
|
+
headers: this.headers,
|
|
932
|
+
jwt: jwt,
|
|
933
|
+
xform: _userResponse,
|
|
934
|
+
})
|
|
868
935
|
})
|
|
869
936
|
} catch (error) {
|
|
870
937
|
if (isAuthError(error)) {
|
|
@@ -885,27 +952,29 @@ export default class GoTrueClient {
|
|
|
885
952
|
} = {}
|
|
886
953
|
): Promise<UserResponse> {
|
|
887
954
|
try {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
955
|
+
return await this._useSession(async (result) => {
|
|
956
|
+
const { data: sessionData, error: sessionError } = result
|
|
957
|
+
if (sessionError) {
|
|
958
|
+
throw sessionError
|
|
959
|
+
}
|
|
960
|
+
if (!sessionData.session) {
|
|
961
|
+
throw new AuthSessionMissingError()
|
|
962
|
+
}
|
|
963
|
+
const session: Session = sessionData.session
|
|
964
|
+
const { data, error: userError } = await _request(this.fetch, 'PUT', `${this.url}/user`, {
|
|
965
|
+
headers: this.headers,
|
|
966
|
+
redirectTo: options?.emailRedirectTo,
|
|
967
|
+
body: attributes,
|
|
968
|
+
jwt: session.access_token,
|
|
969
|
+
xform: _userResponse,
|
|
970
|
+
})
|
|
971
|
+
if (userError) throw userError
|
|
972
|
+
session.user = data.user as User
|
|
973
|
+
await this._saveSession(session)
|
|
974
|
+
await this._notifyAllSubscribers('USER_UPDATED', session)
|
|
907
975
|
|
|
908
|
-
|
|
976
|
+
return { data: { user: session.user }, error: null }
|
|
977
|
+
})
|
|
909
978
|
} catch (error) {
|
|
910
979
|
if (isAuthError(error)) {
|
|
911
980
|
return { data: { user: null }, error }
|
|
@@ -997,29 +1066,31 @@ export default class GoTrueClient {
|
|
|
997
1066
|
*/
|
|
998
1067
|
async refreshSession(currentSession?: { refresh_token: string }): Promise<AuthResponse> {
|
|
999
1068
|
try {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1069
|
+
return await this._useSession(async (result) => {
|
|
1070
|
+
if (!currentSession) {
|
|
1071
|
+
const { data, error } = result
|
|
1072
|
+
if (error) {
|
|
1073
|
+
throw error
|
|
1074
|
+
}
|
|
1005
1075
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1076
|
+
currentSession = data.session ?? undefined
|
|
1077
|
+
}
|
|
1008
1078
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1079
|
+
if (!currentSession?.refresh_token) {
|
|
1080
|
+
throw new AuthSessionMissingError()
|
|
1081
|
+
}
|
|
1012
1082
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1083
|
+
const { session, error } = await this._callRefreshToken(currentSession.refresh_token)
|
|
1084
|
+
if (error) {
|
|
1085
|
+
return { data: { user: null, session: null }, error: error }
|
|
1086
|
+
}
|
|
1017
1087
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1088
|
+
if (!session) {
|
|
1089
|
+
return { data: { user: null, session: null }, error: null }
|
|
1090
|
+
}
|
|
1021
1091
|
|
|
1022
|
-
|
|
1092
|
+
return { data: { user: session.user, session }, error: null }
|
|
1093
|
+
})
|
|
1023
1094
|
} catch (error) {
|
|
1024
1095
|
if (isAuthError(error)) {
|
|
1025
1096
|
return { data: { user: null, session: null }, error }
|
|
@@ -1142,27 +1213,29 @@ export default class GoTrueClient {
|
|
|
1142
1213
|
* If using others scope, no `SIGNED_OUT` event is fired!
|
|
1143
1214
|
*/
|
|
1144
1215
|
async signOut({ scope }: SignOut = { scope: 'global' }): Promise<{ error: AuthError | null }> {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1216
|
+
return await this._useSession(async (result) => {
|
|
1217
|
+
const { data, error: sessionError } = result
|
|
1218
|
+
if (sessionError) {
|
|
1219
|
+
return { error: sessionError }
|
|
1220
|
+
}
|
|
1221
|
+
const accessToken = data.session?.access_token
|
|
1222
|
+
if (accessToken) {
|
|
1223
|
+
const { error } = await this.admin.signOut(accessToken, scope)
|
|
1224
|
+
if (error) {
|
|
1225
|
+
// ignore 404s since user might not exist anymore
|
|
1226
|
+
// ignore 401s since an invalid or expired JWT should sign out the current session
|
|
1227
|
+
if (!(isAuthApiError(error) && (error.status === 404 || error.status === 401))) {
|
|
1228
|
+
return { error }
|
|
1229
|
+
}
|
|
1157
1230
|
}
|
|
1158
1231
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1232
|
+
if (scope !== 'others') {
|
|
1233
|
+
await this._removeSession()
|
|
1234
|
+
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
|
|
1235
|
+
await this._notifyAllSubscribers('SIGNED_OUT', null)
|
|
1236
|
+
}
|
|
1237
|
+
return { error: null }
|
|
1238
|
+
})
|
|
1166
1239
|
}
|
|
1167
1240
|
|
|
1168
1241
|
/**
|
|
@@ -1195,20 +1268,22 @@ export default class GoTrueClient {
|
|
|
1195
1268
|
}
|
|
1196
1269
|
|
|
1197
1270
|
private async _emitInitialSession(id: string): Promise<void> {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1271
|
+
return await this._useSession(async (result) => {
|
|
1272
|
+
try {
|
|
1273
|
+
const {
|
|
1274
|
+
data: { session },
|
|
1275
|
+
error,
|
|
1276
|
+
} = result
|
|
1277
|
+
if (error) throw error
|
|
1204
1278
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1279
|
+
await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', session)
|
|
1280
|
+
this._debug('INITIAL_SESSION', 'callback id', id, 'session', session)
|
|
1281
|
+
} catch (err) {
|
|
1282
|
+
await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', null)
|
|
1283
|
+
this._debug('INITIAL_SESSION', 'callback id', id, 'error', err)
|
|
1284
|
+
console.error(err)
|
|
1285
|
+
}
|
|
1286
|
+
})
|
|
1212
1287
|
}
|
|
1213
1288
|
|
|
1214
1289
|
/**
|
|
@@ -1634,28 +1709,30 @@ export default class GoTrueClient {
|
|
|
1634
1709
|
const now = Date.now()
|
|
1635
1710
|
|
|
1636
1711
|
try {
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1712
|
+
return await this._useSession(async (result) => {
|
|
1713
|
+
const {
|
|
1714
|
+
data: { session },
|
|
1715
|
+
} = result
|
|
1716
|
+
|
|
1717
|
+
if (!session || !session.refresh_token || !session.expires_at) {
|
|
1718
|
+
this._debug('#_autoRefreshTokenTick()', 'no session')
|
|
1719
|
+
return
|
|
1720
|
+
}
|
|
1645
1721
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1722
|
+
// session will expire in this many ticks (or has already expired if <= 0)
|
|
1723
|
+
const expiresInTicks = Math.floor(
|
|
1724
|
+
(session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
|
|
1725
|
+
)
|
|
1650
1726
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1727
|
+
this._debug(
|
|
1728
|
+
'#_autoRefreshTokenTick()',
|
|
1729
|
+
`access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
|
|
1730
|
+
)
|
|
1655
1731
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1732
|
+
if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
|
|
1733
|
+
await this._callRefreshToken(session.refresh_token)
|
|
1734
|
+
}
|
|
1735
|
+
})
|
|
1659
1736
|
} catch (e: any) {
|
|
1660
1737
|
console.error('Auto refresh tick failed with error. This is likely a transient error.', e)
|
|
1661
1738
|
}
|
|
@@ -1777,14 +1854,16 @@ export default class GoTrueClient {
|
|
|
1777
1854
|
|
|
1778
1855
|
private async _unenroll(params: MFAUnenrollParams): Promise<AuthMFAUnenrollResponse> {
|
|
1779
1856
|
try {
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1857
|
+
return await this._useSession(async (result) => {
|
|
1858
|
+
const { data: sessionData, error: sessionError } = result
|
|
1859
|
+
if (sessionError) {
|
|
1860
|
+
return { data: null, error: sessionError }
|
|
1861
|
+
}
|
|
1784
1862
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1863
|
+
return await _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
|
|
1864
|
+
headers: this.headers,
|
|
1865
|
+
jwt: sessionData?.session?.access_token,
|
|
1866
|
+
})
|
|
1788
1867
|
})
|
|
1789
1868
|
} catch (error) {
|
|
1790
1869
|
if (isAuthError(error)) {
|
|
@@ -1799,30 +1878,32 @@ export default class GoTrueClient {
|
|
|
1799
1878
|
*/
|
|
1800
1879
|
private async _enroll(params: MFAEnrollParams): Promise<AuthMFAEnrollResponse> {
|
|
1801
1880
|
try {
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1881
|
+
return await this._useSession(async (result) => {
|
|
1882
|
+
const { data: sessionData, error: sessionError } = result
|
|
1883
|
+
if (sessionError) {
|
|
1884
|
+
return { data: null, error: sessionError }
|
|
1885
|
+
}
|
|
1806
1886
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1887
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors`, {
|
|
1888
|
+
body: {
|
|
1889
|
+
friendly_name: params.friendlyName,
|
|
1890
|
+
factor_type: params.factorType,
|
|
1891
|
+
issuer: params.issuer,
|
|
1892
|
+
},
|
|
1893
|
+
headers: this.headers,
|
|
1894
|
+
jwt: sessionData?.session?.access_token,
|
|
1895
|
+
})
|
|
1816
1896
|
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1897
|
+
if (error) {
|
|
1898
|
+
return { data: null, error }
|
|
1899
|
+
}
|
|
1820
1900
|
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1901
|
+
if (data?.totp?.qr_code) {
|
|
1902
|
+
data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`
|
|
1903
|
+
}
|
|
1824
1904
|
|
|
1825
|
-
|
|
1905
|
+
return { data, error: null }
|
|
1906
|
+
})
|
|
1826
1907
|
} catch (error) {
|
|
1827
1908
|
if (isAuthError(error)) {
|
|
1828
1909
|
return { data: null, error }
|
|
@@ -1836,32 +1917,34 @@ export default class GoTrueClient {
|
|
|
1836
1917
|
*/
|
|
1837
1918
|
private async _verify(params: MFAVerifyParams): Promise<AuthMFAVerifyResponse> {
|
|
1838
1919
|
try {
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1920
|
+
return await this._useSession(async (result) => {
|
|
1921
|
+
const { data: sessionData, error: sessionError } = result
|
|
1922
|
+
if (sessionError) {
|
|
1923
|
+
return { data: null, error: sessionError }
|
|
1924
|
+
}
|
|
1843
1925
|
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1926
|
+
const { data, error } = await _request(
|
|
1927
|
+
this.fetch,
|
|
1928
|
+
'POST',
|
|
1929
|
+
`${this.url}/factors/${params.factorId}/verify`,
|
|
1930
|
+
{
|
|
1931
|
+
body: { code: params.code, challenge_id: params.challengeId },
|
|
1932
|
+
headers: this.headers,
|
|
1933
|
+
jwt: sessionData?.session?.access_token,
|
|
1934
|
+
}
|
|
1935
|
+
)
|
|
1936
|
+
if (error) {
|
|
1937
|
+
return { data: null, error }
|
|
1852
1938
|
}
|
|
1853
|
-
)
|
|
1854
|
-
if (error) {
|
|
1855
|
-
return { data: null, error }
|
|
1856
|
-
}
|
|
1857
1939
|
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1940
|
+
await this._saveSession({
|
|
1941
|
+
expires_at: Math.round(Date.now() / 1000) + data.expires_in,
|
|
1942
|
+
...data,
|
|
1943
|
+
})
|
|
1944
|
+
await this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data)
|
|
1863
1945
|
|
|
1864
|
-
|
|
1946
|
+
return { data, error }
|
|
1947
|
+
})
|
|
1865
1948
|
} catch (error) {
|
|
1866
1949
|
if (isAuthError(error)) {
|
|
1867
1950
|
return { data: null, error }
|
|
@@ -1875,20 +1958,22 @@ export default class GoTrueClient {
|
|
|
1875
1958
|
*/
|
|
1876
1959
|
private async _challenge(params: MFAChallengeParams): Promise<AuthMFAChallengeResponse> {
|
|
1877
1960
|
try {
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
return await _request(
|
|
1884
|
-
this.fetch,
|
|
1885
|
-
'POST',
|
|
1886
|
-
`${this.url}/factors/${params.factorId}/challenge`,
|
|
1887
|
-
{
|
|
1888
|
-
headers: this.headers,
|
|
1889
|
-
jwt: sessionData?.session?.access_token,
|
|
1961
|
+
return await this._useSession(async (result) => {
|
|
1962
|
+
const { data: sessionData, error: sessionError } = result
|
|
1963
|
+
if (sessionError) {
|
|
1964
|
+
return { data: null, error: sessionError }
|
|
1890
1965
|
}
|
|
1891
|
-
|
|
1966
|
+
|
|
1967
|
+
return await _request(
|
|
1968
|
+
this.fetch,
|
|
1969
|
+
'POST',
|
|
1970
|
+
`${this.url}/factors/${params.factorId}/challenge`,
|
|
1971
|
+
{
|
|
1972
|
+
headers: this.headers,
|
|
1973
|
+
jwt: sessionData?.session?.access_token,
|
|
1974
|
+
}
|
|
1975
|
+
)
|
|
1976
|
+
})
|
|
1892
1977
|
} catch (error) {
|
|
1893
1978
|
if (isAuthError(error)) {
|
|
1894
1979
|
return { data: null, error }
|
|
@@ -1946,39 +2031,41 @@ export default class GoTrueClient {
|
|
|
1946
2031
|
* {@see GoTrueMFAApi#getAuthenticatorAssuranceLevel}
|
|
1947
2032
|
*/
|
|
1948
2033
|
private async _getAuthenticatorAssuranceLevel(): Promise<AuthMFAGetAuthenticatorAssuranceLevelResponse> {
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
2034
|
+
return await this._useSession(async (result) => {
|
|
2035
|
+
const {
|
|
2036
|
+
data: { session },
|
|
2037
|
+
error: sessionError,
|
|
2038
|
+
} = result
|
|
2039
|
+
if (sessionError) {
|
|
2040
|
+
return { data: null, error: sessionError }
|
|
2041
|
+
}
|
|
2042
|
+
if (!session) {
|
|
2043
|
+
return {
|
|
2044
|
+
data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
|
|
2045
|
+
error: null,
|
|
2046
|
+
}
|
|
1960
2047
|
}
|
|
1961
|
-
}
|
|
1962
2048
|
|
|
1963
|
-
|
|
2049
|
+
const payload = this._decodeJWT(session.access_token)
|
|
1964
2050
|
|
|
1965
|
-
|
|
2051
|
+
let currentLevel: AuthenticatorAssuranceLevels | null = null
|
|
1966
2052
|
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2053
|
+
if (payload.aal) {
|
|
2054
|
+
currentLevel = payload.aal
|
|
2055
|
+
}
|
|
1970
2056
|
|
|
1971
|
-
|
|
2057
|
+
let nextLevel: AuthenticatorAssuranceLevels | null = currentLevel
|
|
1972
2058
|
|
|
1973
|
-
|
|
1974
|
-
|
|
2059
|
+
const verifiedFactors =
|
|
2060
|
+
session.user.factors?.filter((factor: Factor) => factor.status === 'verified') ?? []
|
|
1975
2061
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2062
|
+
if (verifiedFactors.length > 0) {
|
|
2063
|
+
nextLevel = 'aal2'
|
|
2064
|
+
}
|
|
1979
2065
|
|
|
1980
|
-
|
|
2066
|
+
const currentAuthenticationMethods = payload.amr || []
|
|
1981
2067
|
|
|
1982
|
-
|
|
2068
|
+
return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null }
|
|
2069
|
+
})
|
|
1983
2070
|
}
|
|
1984
2071
|
}
|