@supabase/gotrue-js 2.39.1 → 2.40.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/GoTrueAdminApi.js +164 -193
- package/dist/main/GoTrueAdminApi.js.map +1 -1
- package/dist/main/GoTrueClient.d.ts.map +1 -1
- package/dist/main/GoTrueClient.js +1039 -1132
- package/dist/main/GoTrueClient.js.map +1 -1
- package/dist/main/lib/fetch.js +51 -66
- package/dist/main/lib/fetch.js.map +1 -1
- package/dist/main/lib/helpers.js +28 -41
- 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/GoTrueAdminApi.js +164 -193
- package/dist/module/GoTrueAdminApi.js.map +1 -1
- package/dist/module/GoTrueClient.d.ts.map +1 -1
- package/dist/module/GoTrueClient.js +1039 -1132
- package/dist/module/GoTrueClient.js.map +1 -1
- package/dist/module/lib/fetch.js +51 -66
- package/dist/module/lib/fetch.js.map +1 -1
- package/dist/module/lib/helpers.js +28 -41
- 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 +4 -1
- package/src/lib/version.ts +1 -1
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import GoTrueAdminApi from './GoTrueAdminApi';
|
|
11
2
|
import { DEFAULT_HEADERS, EXPIRY_MARGIN, GOTRUE_URL, STORAGE_KEY } from './lib/constants';
|
|
12
3
|
import { AuthImplicitGrantRedirectError, AuthPKCEGrantCodeExchangeError, AuthInvalidCredentialsError, AuthSessionMissingError, AuthInvalidTokenResponseError, AuthUnknownError, isAuthApiError, isAuthError, isAuthRetryableFetchError, } from './lib/errors';
|
|
@@ -94,10 +85,10 @@ export default class GoTrueClient {
|
|
|
94
85
|
catch (e) {
|
|
95
86
|
console.error('Failed to create a new BroadcastChannel, multi-tab state changes will not be available', e);
|
|
96
87
|
}
|
|
97
|
-
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.addEventListener('message', (event) =>
|
|
88
|
+
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.addEventListener('message', async (event) => {
|
|
98
89
|
this._debug('received broadcast notification from other tab or client', event);
|
|
99
|
-
|
|
100
|
-
})
|
|
90
|
+
await this._notifyAllSubscribers(event.data.event, event.data.session, false); // broadcast = false so we don't get an endless loop of messages
|
|
91
|
+
});
|
|
101
92
|
}
|
|
102
93
|
this.initialize();
|
|
103
94
|
}
|
|
@@ -124,53 +115,51 @@ export default class GoTrueClient {
|
|
|
124
115
|
* 2. Never return a session from this method as it would be cached over
|
|
125
116
|
* the whole lifetime of the client
|
|
126
117
|
*/
|
|
127
|
-
_initialize() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
yield this._removeSession();
|
|
142
|
-
return { error };
|
|
143
|
-
}
|
|
144
|
-
const { session, redirectType } = data;
|
|
145
|
-
this._debug('#_initialize()', 'detected session in URL', session, 'redirect type', redirectType);
|
|
146
|
-
yield this._saveSession(session);
|
|
147
|
-
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
148
|
-
if (redirectType === 'recovery') {
|
|
149
|
-
yield this._notifyAllSubscribers('PASSWORD_RECOVERY', session);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
yield this._notifyAllSubscribers('SIGNED_IN', session);
|
|
153
|
-
}
|
|
154
|
-
}), 0);
|
|
155
|
-
return { error: null };
|
|
156
|
-
}
|
|
157
|
-
// no login attempt via callback url try to recover session from storage
|
|
158
|
-
yield this._recoverAndRefresh();
|
|
159
|
-
return { error: null };
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
if (isAuthError(error)) {
|
|
118
|
+
async _initialize() {
|
|
119
|
+
if (this.initializePromise) {
|
|
120
|
+
return this.initializePromise;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false;
|
|
124
|
+
this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow);
|
|
125
|
+
if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
|
|
126
|
+
const { data, error } = await this._getSessionFromUrl(isPKCEFlow);
|
|
127
|
+
if (error) {
|
|
128
|
+
this._debug('#_initialize()', 'error detecting session from URL', error);
|
|
129
|
+
// failed login attempt via url,
|
|
130
|
+
// remove old session as in verifyOtp, signUp and signInWith*
|
|
131
|
+
await this._removeSession();
|
|
163
132
|
return { error };
|
|
164
133
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
134
|
+
const { session, redirectType } = data;
|
|
135
|
+
this._debug('#_initialize()', 'detected session in URL', session, 'redirect type', redirectType);
|
|
136
|
+
await this._saveSession(session);
|
|
137
|
+
setTimeout(async () => {
|
|
138
|
+
if (redirectType === 'recovery') {
|
|
139
|
+
await this._notifyAllSubscribers('PASSWORD_RECOVERY', session);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
await this._notifyAllSubscribers('SIGNED_IN', session);
|
|
143
|
+
}
|
|
144
|
+
}, 0);
|
|
145
|
+
return { error: null };
|
|
168
146
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
// no login attempt via callback url try to recover session from storage
|
|
148
|
+
await this._recoverAndRefresh();
|
|
149
|
+
return { error: null };
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (isAuthError(error)) {
|
|
153
|
+
return { error };
|
|
172
154
|
}
|
|
173
|
-
|
|
155
|
+
return {
|
|
156
|
+
error: new AuthUnknownError('Unexpected error during initialization', error),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
await this._handleVisibilityChange();
|
|
161
|
+
this._debug('#_initialize()', 'end');
|
|
162
|
+
}
|
|
174
163
|
}
|
|
175
164
|
/**
|
|
176
165
|
* Creates a new user.
|
|
@@ -182,72 +171,70 @@ export default class GoTrueClient {
|
|
|
182
171
|
* @returns A logged-in session if the server has "autoconfirm" ON
|
|
183
172
|
* @returns A user if the server has "autoconfirm" OFF
|
|
184
173
|
*/
|
|
185
|
-
signUp(credentials) {
|
|
174
|
+
async signUp(credentials) {
|
|
186
175
|
var _a, _b, _c;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
else if ('phone' in credentials) {
|
|
216
|
-
const { phone, password, options } = credentials;
|
|
217
|
-
res = yield _request(this.fetch, 'POST', `${this.url}/signup`, {
|
|
218
|
-
headers: this.headers,
|
|
219
|
-
body: {
|
|
220
|
-
phone,
|
|
221
|
-
password,
|
|
222
|
-
data: (_b = options === null || options === void 0 ? void 0 : options.data) !== null && _b !== void 0 ? _b : {},
|
|
223
|
-
channel: (_c = options === null || options === void 0 ? void 0 : options.channel) !== null && _c !== void 0 ? _c : 'sms',
|
|
224
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
225
|
-
},
|
|
226
|
-
xform: _sessionResponse,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a password');
|
|
231
|
-
}
|
|
232
|
-
const { data, error } = res;
|
|
233
|
-
if (error || !data) {
|
|
234
|
-
return { data: { user: null, session: null }, error: error };
|
|
235
|
-
}
|
|
236
|
-
const session = data.session;
|
|
237
|
-
const user = data.user;
|
|
238
|
-
if (data.session) {
|
|
239
|
-
yield this._saveSession(data.session);
|
|
240
|
-
yield this._notifyAllSubscribers('SIGNED_IN', session);
|
|
241
|
-
}
|
|
242
|
-
return { data: { user, session }, error: null };
|
|
176
|
+
try {
|
|
177
|
+
await this._removeSession();
|
|
178
|
+
let res;
|
|
179
|
+
if ('email' in credentials) {
|
|
180
|
+
const { email, password, options } = credentials;
|
|
181
|
+
let codeChallenge = null;
|
|
182
|
+
let codeChallengeMethod = null;
|
|
183
|
+
if (this.flowType === 'pkce') {
|
|
184
|
+
const codeVerifier = generatePKCEVerifier();
|
|
185
|
+
await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier);
|
|
186
|
+
codeChallenge = await generatePKCEChallenge(codeVerifier);
|
|
187
|
+
codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256';
|
|
188
|
+
}
|
|
189
|
+
res = await _request(this.fetch, 'POST', `${this.url}/signup`, {
|
|
190
|
+
headers: this.headers,
|
|
191
|
+
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
192
|
+
body: {
|
|
193
|
+
email,
|
|
194
|
+
password,
|
|
195
|
+
data: (_a = options === null || options === void 0 ? void 0 : options.data) !== null && _a !== void 0 ? _a : {},
|
|
196
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
197
|
+
code_challenge: codeChallenge,
|
|
198
|
+
code_challenge_method: codeChallengeMethod,
|
|
199
|
+
},
|
|
200
|
+
xform: _sessionResponse,
|
|
201
|
+
});
|
|
243
202
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
203
|
+
else if ('phone' in credentials) {
|
|
204
|
+
const { phone, password, options } = credentials;
|
|
205
|
+
res = await _request(this.fetch, 'POST', `${this.url}/signup`, {
|
|
206
|
+
headers: this.headers,
|
|
207
|
+
body: {
|
|
208
|
+
phone,
|
|
209
|
+
password,
|
|
210
|
+
data: (_b = options === null || options === void 0 ? void 0 : options.data) !== null && _b !== void 0 ? _b : {},
|
|
211
|
+
channel: (_c = options === null || options === void 0 ? void 0 : options.channel) !== null && _c !== void 0 ? _c : 'sms',
|
|
212
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
213
|
+
},
|
|
214
|
+
xform: _sessionResponse,
|
|
215
|
+
});
|
|
249
216
|
}
|
|
250
|
-
|
|
217
|
+
else {
|
|
218
|
+
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a password');
|
|
219
|
+
}
|
|
220
|
+
const { data, error } = res;
|
|
221
|
+
if (error || !data) {
|
|
222
|
+
return { data: { user: null, session: null }, error: error };
|
|
223
|
+
}
|
|
224
|
+
const session = data.session;
|
|
225
|
+
const user = data.user;
|
|
226
|
+
if (data.session) {
|
|
227
|
+
await this._saveSession(data.session);
|
|
228
|
+
await this._notifyAllSubscribers('SIGNED_IN', session);
|
|
229
|
+
}
|
|
230
|
+
return { data: { user, session }, error: null };
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (isAuthError(error)) {
|
|
234
|
+
return { data: { user: null, session: null }, error };
|
|
235
|
+
}
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
251
238
|
}
|
|
252
239
|
/**
|
|
253
240
|
* Log in an existing user with an email and password or phone and password.
|
|
@@ -257,146 +244,138 @@ export default class GoTrueClient {
|
|
|
257
244
|
* email/phone and password combination is wrong or that the account can only
|
|
258
245
|
* be accessed via social login.
|
|
259
246
|
*/
|
|
260
|
-
signInWithPassword(credentials) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
else if ('phone' in credentials) {
|
|
278
|
-
const { phone, password, options } = credentials;
|
|
279
|
-
res = yield _request(this.fetch, 'POST', `${this.url}/token?grant_type=password`, {
|
|
280
|
-
headers: this.headers,
|
|
281
|
-
body: {
|
|
282
|
-
phone,
|
|
283
|
-
password,
|
|
284
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
285
|
-
},
|
|
286
|
-
xform: _sessionResponse,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a password');
|
|
291
|
-
}
|
|
292
|
-
const { data, error } = res;
|
|
293
|
-
if (error) {
|
|
294
|
-
return { data: { user: null, session: null }, error };
|
|
295
|
-
}
|
|
296
|
-
else if (!data || !data.session || !data.user) {
|
|
297
|
-
return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() };
|
|
298
|
-
}
|
|
299
|
-
if (data.session) {
|
|
300
|
-
yield this._saveSession(data.session);
|
|
301
|
-
yield this._notifyAllSubscribers('SIGNED_IN', data.session);
|
|
302
|
-
}
|
|
303
|
-
return { data: { user: data.user, session: data.session }, error };
|
|
247
|
+
async signInWithPassword(credentials) {
|
|
248
|
+
try {
|
|
249
|
+
await this._removeSession();
|
|
250
|
+
let res;
|
|
251
|
+
if ('email' in credentials) {
|
|
252
|
+
const { email, password, options } = credentials;
|
|
253
|
+
res = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=password`, {
|
|
254
|
+
headers: this.headers,
|
|
255
|
+
body: {
|
|
256
|
+
email,
|
|
257
|
+
password,
|
|
258
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
259
|
+
},
|
|
260
|
+
xform: _sessionResponse,
|
|
261
|
+
});
|
|
304
262
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
263
|
+
else if ('phone' in credentials) {
|
|
264
|
+
const { phone, password, options } = credentials;
|
|
265
|
+
res = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=password`, {
|
|
266
|
+
headers: this.headers,
|
|
267
|
+
body: {
|
|
268
|
+
phone,
|
|
269
|
+
password,
|
|
270
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
271
|
+
},
|
|
272
|
+
xform: _sessionResponse,
|
|
273
|
+
});
|
|
310
274
|
}
|
|
311
|
-
|
|
275
|
+
else {
|
|
276
|
+
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a password');
|
|
277
|
+
}
|
|
278
|
+
const { data, error } = res;
|
|
279
|
+
if (error) {
|
|
280
|
+
return { data: { user: null, session: null }, error };
|
|
281
|
+
}
|
|
282
|
+
else if (!data || !data.session || !data.user) {
|
|
283
|
+
return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() };
|
|
284
|
+
}
|
|
285
|
+
if (data.session) {
|
|
286
|
+
await this._saveSession(data.session);
|
|
287
|
+
await this._notifyAllSubscribers('SIGNED_IN', data.session);
|
|
288
|
+
}
|
|
289
|
+
return { data: { user: data.user, session: data.session }, error };
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
if (isAuthError(error)) {
|
|
293
|
+
return { data: { user: null, session: null }, error };
|
|
294
|
+
}
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
312
297
|
}
|
|
313
298
|
/**
|
|
314
299
|
* Log in an existing user via a third-party provider.
|
|
315
300
|
* This method supports the PKCE flow.
|
|
316
301
|
*/
|
|
317
|
-
signInWithOAuth(credentials) {
|
|
302
|
+
async signInWithOAuth(credentials) {
|
|
318
303
|
var _a, _b, _c, _d;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
skipBrowserRedirect: (_d = credentials.options) === null || _d === void 0 ? void 0 : _d.skipBrowserRedirect,
|
|
326
|
-
});
|
|
304
|
+
await this._removeSession();
|
|
305
|
+
return await this._handleProviderSignIn(credentials.provider, {
|
|
306
|
+
redirectTo: (_a = credentials.options) === null || _a === void 0 ? void 0 : _a.redirectTo,
|
|
307
|
+
scopes: (_b = credentials.options) === null || _b === void 0 ? void 0 : _b.scopes,
|
|
308
|
+
queryParams: (_c = credentials.options) === null || _c === void 0 ? void 0 : _c.queryParams,
|
|
309
|
+
skipBrowserRedirect: (_d = credentials.options) === null || _d === void 0 ? void 0 : _d.skipBrowserRedirect,
|
|
327
310
|
});
|
|
328
311
|
}
|
|
329
312
|
/**
|
|
330
313
|
* Log in an existing user by exchanging an Auth Code issued during the PKCE flow.
|
|
331
314
|
*/
|
|
332
|
-
exchangeCodeForSession(authCode) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
315
|
+
async exchangeCodeForSession(authCode) {
|
|
316
|
+
const codeVerifier = await getItemAsync(this.storage, `${this.storageKey}-code-verifier`);
|
|
317
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=pkce`, {
|
|
318
|
+
headers: this.headers,
|
|
319
|
+
body: {
|
|
320
|
+
auth_code: authCode,
|
|
321
|
+
code_verifier: codeVerifier,
|
|
322
|
+
},
|
|
323
|
+
xform: _sessionResponse,
|
|
324
|
+
});
|
|
325
|
+
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`);
|
|
326
|
+
if (error) {
|
|
327
|
+
return { data: { user: null, session: null }, error };
|
|
328
|
+
}
|
|
329
|
+
else if (!data || !data.session || !data.user) {
|
|
330
|
+
return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() };
|
|
331
|
+
}
|
|
332
|
+
if (data.session) {
|
|
333
|
+
await this._saveSession(data.session);
|
|
334
|
+
await this._notifyAllSubscribers('SIGNED_IN', data.session);
|
|
335
|
+
}
|
|
336
|
+
return { data, error };
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Allows signing in with an OIDC ID token. The authentication provider used
|
|
340
|
+
* should be enabled and configured.
|
|
341
|
+
*/
|
|
342
|
+
async signInWithIdToken(credentials) {
|
|
343
|
+
await this._removeSession();
|
|
344
|
+
try {
|
|
345
|
+
const { options, provider, token, access_token, nonce } = credentials;
|
|
346
|
+
const res = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=id_token`, {
|
|
336
347
|
headers: this.headers,
|
|
337
348
|
body: {
|
|
338
|
-
|
|
339
|
-
|
|
349
|
+
provider,
|
|
350
|
+
id_token: token,
|
|
351
|
+
access_token,
|
|
352
|
+
nonce,
|
|
353
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
340
354
|
},
|
|
341
355
|
xform: _sessionResponse,
|
|
342
356
|
});
|
|
343
|
-
|
|
357
|
+
const { data, error } = res;
|
|
344
358
|
if (error) {
|
|
345
359
|
return { data: { user: null, session: null }, error };
|
|
346
360
|
}
|
|
347
361
|
else if (!data || !data.session || !data.user) {
|
|
348
|
-
return {
|
|
362
|
+
return {
|
|
363
|
+
data: { user: null, session: null },
|
|
364
|
+
error: new AuthInvalidTokenResponseError(),
|
|
365
|
+
};
|
|
349
366
|
}
|
|
350
367
|
if (data.session) {
|
|
351
|
-
|
|
352
|
-
|
|
368
|
+
await this._saveSession(data.session);
|
|
369
|
+
await this._notifyAllSubscribers('SIGNED_IN', data.session);
|
|
353
370
|
}
|
|
354
371
|
return { data, error };
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
* should be enabled and configured.
|
|
360
|
-
*/
|
|
361
|
-
signInWithIdToken(credentials) {
|
|
362
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
363
|
-
yield this._removeSession();
|
|
364
|
-
try {
|
|
365
|
-
const { options, provider, token, access_token, nonce } = credentials;
|
|
366
|
-
const res = yield _request(this.fetch, 'POST', `${this.url}/token?grant_type=id_token`, {
|
|
367
|
-
headers: this.headers,
|
|
368
|
-
body: {
|
|
369
|
-
provider,
|
|
370
|
-
id_token: token,
|
|
371
|
-
access_token,
|
|
372
|
-
nonce,
|
|
373
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
374
|
-
},
|
|
375
|
-
xform: _sessionResponse,
|
|
376
|
-
});
|
|
377
|
-
const { data, error } = res;
|
|
378
|
-
if (error) {
|
|
379
|
-
return { data: { user: null, session: null }, error };
|
|
380
|
-
}
|
|
381
|
-
else if (!data || !data.session || !data.user) {
|
|
382
|
-
return {
|
|
383
|
-
data: { user: null, session: null },
|
|
384
|
-
error: new AuthInvalidTokenResponseError(),
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
if (data.session) {
|
|
388
|
-
yield this._saveSession(data.session);
|
|
389
|
-
yield this._notifyAllSubscribers('SIGNED_IN', data.session);
|
|
390
|
-
}
|
|
391
|
-
return { data, error };
|
|
392
|
-
}
|
|
393
|
-
catch (error) {
|
|
394
|
-
if (isAuthError(error)) {
|
|
395
|
-
return { data: { user: null, session: null }, error };
|
|
396
|
-
}
|
|
397
|
-
throw error;
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
if (isAuthError(error)) {
|
|
375
|
+
return { data: { user: null, session: null }, error };
|
|
398
376
|
}
|
|
399
|
-
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
400
379
|
}
|
|
401
380
|
/**
|
|
402
381
|
* Log in a user using magiclink or a one-time password (OTP).
|
|
@@ -415,97 +394,93 @@ export default class GoTrueClient {
|
|
|
415
394
|
* at this time.
|
|
416
395
|
* This method supports PKCE when an email is passed.
|
|
417
396
|
*/
|
|
418
|
-
signInWithOtp(credentials) {
|
|
397
|
+
async signInWithOtp(credentials) {
|
|
419
398
|
var _a, _b, _c, _d, _e;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return { data: { user: null, session: null }, error };
|
|
446
|
-
}
|
|
447
|
-
if ('phone' in credentials) {
|
|
448
|
-
const { phone, options } = credentials;
|
|
449
|
-
const { data, error } = yield _request(this.fetch, 'POST', `${this.url}/otp`, {
|
|
450
|
-
headers: this.headers,
|
|
451
|
-
body: {
|
|
452
|
-
phone,
|
|
453
|
-
data: (_c = options === null || options === void 0 ? void 0 : options.data) !== null && _c !== void 0 ? _c : {},
|
|
454
|
-
create_user: (_d = options === null || options === void 0 ? void 0 : options.shouldCreateUser) !== null && _d !== void 0 ? _d : true,
|
|
455
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
456
|
-
channel: (_e = options === null || options === void 0 ? void 0 : options.channel) !== null && _e !== void 0 ? _e : 'sms',
|
|
457
|
-
},
|
|
458
|
-
});
|
|
459
|
-
return { data: { user: null, session: null, messageId: data === null || data === void 0 ? void 0 : data.message_id }, error };
|
|
460
|
-
}
|
|
461
|
-
throw new AuthInvalidCredentialsError('You must provide either an email or phone number.');
|
|
399
|
+
try {
|
|
400
|
+
await this._removeSession();
|
|
401
|
+
if ('email' in credentials) {
|
|
402
|
+
const { email, options } = credentials;
|
|
403
|
+
let codeChallenge = null;
|
|
404
|
+
let codeChallengeMethod = null;
|
|
405
|
+
if (this.flowType === 'pkce') {
|
|
406
|
+
const codeVerifier = generatePKCEVerifier();
|
|
407
|
+
await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier);
|
|
408
|
+
codeChallenge = await generatePKCEChallenge(codeVerifier);
|
|
409
|
+
codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256';
|
|
410
|
+
}
|
|
411
|
+
const { error } = await _request(this.fetch, 'POST', `${this.url}/otp`, {
|
|
412
|
+
headers: this.headers,
|
|
413
|
+
body: {
|
|
414
|
+
email,
|
|
415
|
+
data: (_a = options === null || options === void 0 ? void 0 : options.data) !== null && _a !== void 0 ? _a : {},
|
|
416
|
+
create_user: (_b = options === null || options === void 0 ? void 0 : options.shouldCreateUser) !== null && _b !== void 0 ? _b : true,
|
|
417
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
418
|
+
code_challenge: codeChallenge,
|
|
419
|
+
code_challenge_method: codeChallengeMethod,
|
|
420
|
+
},
|
|
421
|
+
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
422
|
+
});
|
|
423
|
+
return { data: { user: null, session: null }, error };
|
|
462
424
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
425
|
+
if ('phone' in credentials) {
|
|
426
|
+
const { phone, options } = credentials;
|
|
427
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/otp`, {
|
|
428
|
+
headers: this.headers,
|
|
429
|
+
body: {
|
|
430
|
+
phone,
|
|
431
|
+
data: (_c = options === null || options === void 0 ? void 0 : options.data) !== null && _c !== void 0 ? _c : {},
|
|
432
|
+
create_user: (_d = options === null || options === void 0 ? void 0 : options.shouldCreateUser) !== null && _d !== void 0 ? _d : true,
|
|
433
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
434
|
+
channel: (_e = options === null || options === void 0 ? void 0 : options.channel) !== null && _e !== void 0 ? _e : 'sms',
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
return { data: { user: null, session: null, messageId: data === null || data === void 0 ? void 0 : data.message_id }, error };
|
|
468
438
|
}
|
|
469
|
-
|
|
439
|
+
throw new AuthInvalidCredentialsError('You must provide either an email or phone number.');
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
if (isAuthError(error)) {
|
|
443
|
+
return { data: { user: null, session: null }, error };
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
470
447
|
}
|
|
471
448
|
/**
|
|
472
449
|
* Log in a user given a User supplied OTP received via mobile.
|
|
473
450
|
*/
|
|
474
|
-
verifyOtp(params) {
|
|
451
|
+
async verifyOtp(params) {
|
|
475
452
|
var _a, _b;
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if
|
|
479
|
-
|
|
480
|
-
yield this._removeSession();
|
|
481
|
-
}
|
|
482
|
-
const { data, error } = yield _request(this.fetch, 'POST', `${this.url}/verify`, {
|
|
483
|
-
headers: this.headers,
|
|
484
|
-
body: Object.assign(Object.assign({}, params), { gotrue_meta_security: { captcha_token: (_a = params.options) === null || _a === void 0 ? void 0 : _a.captchaToken } }),
|
|
485
|
-
redirectTo: (_b = params.options) === null || _b === void 0 ? void 0 : _b.redirectTo,
|
|
486
|
-
xform: _sessionResponse,
|
|
487
|
-
});
|
|
488
|
-
if (error) {
|
|
489
|
-
throw error;
|
|
490
|
-
}
|
|
491
|
-
if (!data) {
|
|
492
|
-
throw new Error('An error occurred on token verification.');
|
|
493
|
-
}
|
|
494
|
-
const session = data.session;
|
|
495
|
-
const user = data.user;
|
|
496
|
-
if (session === null || session === void 0 ? void 0 : session.access_token) {
|
|
497
|
-
yield this._saveSession(session);
|
|
498
|
-
yield this._notifyAllSubscribers('SIGNED_IN', session);
|
|
499
|
-
}
|
|
500
|
-
return { data: { user, session }, error: null };
|
|
453
|
+
try {
|
|
454
|
+
if (params.type !== 'email_change' && params.type !== 'phone_change') {
|
|
455
|
+
// we don't want to remove the authenticated session if the user is performing an email_change or phone_change verification
|
|
456
|
+
await this._removeSession();
|
|
501
457
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
458
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/verify`, {
|
|
459
|
+
headers: this.headers,
|
|
460
|
+
body: Object.assign(Object.assign({}, params), { gotrue_meta_security: { captcha_token: (_a = params.options) === null || _a === void 0 ? void 0 : _a.captchaToken } }),
|
|
461
|
+
redirectTo: (_b = params.options) === null || _b === void 0 ? void 0 : _b.redirectTo,
|
|
462
|
+
xform: _sessionResponse,
|
|
463
|
+
});
|
|
464
|
+
if (error) {
|
|
506
465
|
throw error;
|
|
507
466
|
}
|
|
508
|
-
|
|
467
|
+
if (!data) {
|
|
468
|
+
throw new Error('An error occurred on token verification.');
|
|
469
|
+
}
|
|
470
|
+
const session = data.session;
|
|
471
|
+
const user = data.user;
|
|
472
|
+
if (session === null || session === void 0 ? void 0 : session.access_token) {
|
|
473
|
+
await this._saveSession(session);
|
|
474
|
+
await this._notifyAllSubscribers('SIGNED_IN', session);
|
|
475
|
+
}
|
|
476
|
+
return { data: { user, session }, error: null };
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
if (isAuthError(error)) {
|
|
480
|
+
return { data: { user: null, session: null }, error };
|
|
481
|
+
}
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
509
484
|
}
|
|
510
485
|
/**
|
|
511
486
|
* Attempts a single-sign on using an enterprise Identity Provider. A
|
|
@@ -521,211 +496,201 @@ export default class GoTrueClient {
|
|
|
521
496
|
* If you have built an organization-specific login page, you can use the
|
|
522
497
|
* organization's SSO Identity Provider UUID directly instead.
|
|
523
498
|
*/
|
|
524
|
-
signInWithSSO(params) {
|
|
499
|
+
async signInWithSSO(params) {
|
|
525
500
|
var _a, _b, _c;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
return { data: null, error };
|
|
540
|
-
}
|
|
541
|
-
throw error;
|
|
501
|
+
try {
|
|
502
|
+
await this._removeSession();
|
|
503
|
+
return await _request(this.fetch, 'POST', `${this.url}/sso`, {
|
|
504
|
+
body: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, ('providerId' in params ? { provider_id: params.providerId } : null)), ('domain' in params ? { domain: params.domain } : null)), { redirect_to: (_b = (_a = params.options) === null || _a === void 0 ? void 0 : _a.redirectTo) !== null && _b !== void 0 ? _b : undefined }), (((_c = params === null || params === void 0 ? void 0 : params.options) === null || _c === void 0 ? void 0 : _c.captchaToken)
|
|
505
|
+
? { gotrue_meta_security: { captcha_token: params.options.captchaToken } }
|
|
506
|
+
: null)), { skip_http_redirect: true }),
|
|
507
|
+
headers: this.headers,
|
|
508
|
+
xform: _ssoResponse,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
if (isAuthError(error)) {
|
|
513
|
+
return { data: null, error };
|
|
542
514
|
}
|
|
543
|
-
|
|
515
|
+
throw error;
|
|
516
|
+
}
|
|
544
517
|
}
|
|
545
518
|
/**
|
|
546
519
|
* Sends a reauthentication OTP to the user's email or phone number.
|
|
547
520
|
* Requires the user to be signed-in.
|
|
548
521
|
*/
|
|
549
|
-
reauthenticate() {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
522
|
+
async reauthenticate() {
|
|
523
|
+
try {
|
|
524
|
+
const { data: { session }, error: sessionError, } = await this.getSession();
|
|
525
|
+
if (sessionError)
|
|
526
|
+
throw sessionError;
|
|
527
|
+
if (!session)
|
|
528
|
+
throw new AuthSessionMissingError();
|
|
529
|
+
const { error } = await _request(this.fetch, 'GET', `${this.url}/reauthenticate`, {
|
|
530
|
+
headers: this.headers,
|
|
531
|
+
jwt: session.access_token,
|
|
532
|
+
});
|
|
533
|
+
return { data: { user: null, session: null }, error };
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
if (isAuthError(error)) {
|
|
561
537
|
return { data: { user: null, session: null }, error };
|
|
562
538
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
return { data: { user: null, session: null }, error };
|
|
566
|
-
}
|
|
567
|
-
throw error;
|
|
568
|
-
}
|
|
569
|
-
});
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
570
541
|
}
|
|
571
542
|
/**
|
|
572
543
|
* Resends an existing signup confirmation email, email change email, SMS OTP or phone change OTP.
|
|
573
544
|
*/
|
|
574
|
-
resend(credentials) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const endpoint = `${this.url}/resend`;
|
|
579
|
-
if ('email' in credentials) {
|
|
580
|
-
const { email, type, options } = credentials;
|
|
581
|
-
const { error } = yield _request(this.fetch, 'POST', endpoint, {
|
|
582
|
-
headers: this.headers,
|
|
583
|
-
body: {
|
|
584
|
-
email,
|
|
585
|
-
type,
|
|
586
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
587
|
-
},
|
|
588
|
-
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
589
|
-
});
|
|
590
|
-
return { data: { user: null, session: null }, error };
|
|
591
|
-
}
|
|
592
|
-
else if ('phone' in credentials) {
|
|
593
|
-
const { phone, type, options } = credentials;
|
|
594
|
-
const { data, error } = yield _request(this.fetch, 'POST', endpoint, {
|
|
595
|
-
headers: this.headers,
|
|
596
|
-
body: {
|
|
597
|
-
phone,
|
|
598
|
-
type,
|
|
599
|
-
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
600
|
-
},
|
|
601
|
-
});
|
|
602
|
-
return { data: { user: null, session: null, messageId: data === null || data === void 0 ? void 0 : data.message_id }, error };
|
|
603
|
-
}
|
|
604
|
-
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a type');
|
|
545
|
+
async resend(credentials) {
|
|
546
|
+
try {
|
|
547
|
+
if (credentials.type != 'email_change' && credentials.type != 'phone_change') {
|
|
548
|
+
await this._removeSession();
|
|
605
549
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
|
|
550
|
+
const endpoint = `${this.url}/resend`;
|
|
551
|
+
if ('email' in credentials) {
|
|
552
|
+
const { email, type, options } = credentials;
|
|
553
|
+
const { error } = await _request(this.fetch, 'POST', endpoint, {
|
|
554
|
+
headers: this.headers,
|
|
555
|
+
body: {
|
|
556
|
+
email,
|
|
557
|
+
type,
|
|
558
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
559
|
+
},
|
|
560
|
+
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
561
|
+
});
|
|
562
|
+
return { data: { user: null, session: null }, error };
|
|
611
563
|
}
|
|
612
|
-
|
|
564
|
+
else if ('phone' in credentials) {
|
|
565
|
+
const { phone, type, options } = credentials;
|
|
566
|
+
const { data, error } = await _request(this.fetch, 'POST', endpoint, {
|
|
567
|
+
headers: this.headers,
|
|
568
|
+
body: {
|
|
569
|
+
phone,
|
|
570
|
+
type,
|
|
571
|
+
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
return { data: { user: null, session: null, messageId: data === null || data === void 0 ? void 0 : data.message_id }, error };
|
|
575
|
+
}
|
|
576
|
+
throw new AuthInvalidCredentialsError('You must provide either an email or phone number and a type');
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (isAuthError(error)) {
|
|
580
|
+
return { data: { user: null, session: null }, error };
|
|
581
|
+
}
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
613
584
|
}
|
|
614
585
|
/**
|
|
615
586
|
* Returns the session, refreshing it if necessary.
|
|
616
587
|
* 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.
|
|
617
588
|
*/
|
|
618
|
-
getSession() {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (maybeSession
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
yield this._removeSession();
|
|
636
|
-
}
|
|
589
|
+
async getSession() {
|
|
590
|
+
// make sure we've read the session from the url if there is one
|
|
591
|
+
// save to just await, as long we make sure _initialize() never throws
|
|
592
|
+
await this.initializePromise;
|
|
593
|
+
this._debug('#getSession()', 'begin');
|
|
594
|
+
try {
|
|
595
|
+
let currentSession = null;
|
|
596
|
+
if (this.persistSession) {
|
|
597
|
+
const maybeSession = await getItemAsync(this.storage, this.storageKey);
|
|
598
|
+
this._debug('#getSession()', 'session from storage', maybeSession);
|
|
599
|
+
if (maybeSession !== null) {
|
|
600
|
+
if (this._isValidSession(maybeSession)) {
|
|
601
|
+
currentSession = maybeSession;
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
this._debug('#getSession()', 'session from storage is not valid');
|
|
605
|
+
await this._removeSession();
|
|
637
606
|
}
|
|
638
607
|
}
|
|
639
|
-
else {
|
|
640
|
-
currentSession = this.inMemorySession;
|
|
641
|
-
this._debug('#getSession()', 'session from memory', currentSession);
|
|
642
|
-
}
|
|
643
|
-
if (!currentSession) {
|
|
644
|
-
return { data: { session: null }, error: null };
|
|
645
|
-
}
|
|
646
|
-
const hasExpired = currentSession.expires_at
|
|
647
|
-
? currentSession.expires_at <= Date.now() / 1000
|
|
648
|
-
: false;
|
|
649
|
-
this._debug('#getSession()', `session has${hasExpired ? '' : ' not'} expired`, 'expires_at', currentSession.expires_at);
|
|
650
|
-
if (!hasExpired) {
|
|
651
|
-
return { data: { session: currentSession }, error: null };
|
|
652
|
-
}
|
|
653
|
-
const { session, error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
654
|
-
if (error) {
|
|
655
|
-
return { data: { session: null }, error };
|
|
656
|
-
}
|
|
657
|
-
return { data: { session }, error: null };
|
|
658
608
|
}
|
|
659
|
-
|
|
660
|
-
this.
|
|
609
|
+
else {
|
|
610
|
+
currentSession = this.inMemorySession;
|
|
611
|
+
this._debug('#getSession()', 'session from memory', currentSession);
|
|
661
612
|
}
|
|
662
|
-
|
|
663
|
-
|
|
613
|
+
if (!currentSession) {
|
|
614
|
+
return { data: { session: null }, error: null };
|
|
615
|
+
}
|
|
616
|
+
const hasExpired = currentSession.expires_at
|
|
617
|
+
? currentSession.expires_at <= Date.now() / 1000
|
|
618
|
+
: false;
|
|
619
|
+
this._debug('#getSession()', `session has${hasExpired ? '' : ' not'} expired`, 'expires_at', currentSession.expires_at);
|
|
620
|
+
if (!hasExpired) {
|
|
621
|
+
return { data: { session: currentSession }, error: null };
|
|
622
|
+
}
|
|
623
|
+
const { session, error } = await this._callRefreshToken(currentSession.refresh_token);
|
|
624
|
+
if (error) {
|
|
625
|
+
return { data: { session: null }, error };
|
|
626
|
+
}
|
|
627
|
+
return { data: { session }, error: null };
|
|
628
|
+
}
|
|
629
|
+
finally {
|
|
630
|
+
this._debug('#getSession()', 'end');
|
|
631
|
+
}
|
|
632
|
+
}
|
|
664
633
|
/**
|
|
665
634
|
* Gets the current user details if there is an existing session.
|
|
666
635
|
* @param jwt Takes in an optional access token jwt. If no jwt is provided, getUser() will attempt to get the jwt from the current session.
|
|
667
636
|
*/
|
|
668
|
-
getUser(jwt) {
|
|
637
|
+
async getUser(jwt) {
|
|
669
638
|
var _a, _b;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
throw error;
|
|
676
|
-
}
|
|
677
|
-
// Default to Authorization header if there is no existing session
|
|
678
|
-
jwt = (_b = (_a = data.session) === null || _a === void 0 ? void 0 : _a.access_token) !== null && _b !== void 0 ? _b : undefined;
|
|
639
|
+
try {
|
|
640
|
+
if (!jwt) {
|
|
641
|
+
const { data, error } = await this.getSession();
|
|
642
|
+
if (error) {
|
|
643
|
+
throw error;
|
|
679
644
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
jwt: jwt,
|
|
683
|
-
xform: _userResponse,
|
|
684
|
-
});
|
|
645
|
+
// Default to Authorization header if there is no existing session
|
|
646
|
+
jwt = (_b = (_a = data.session) === null || _a === void 0 ? void 0 : _a.access_token) !== null && _b !== void 0 ? _b : undefined;
|
|
685
647
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
648
|
+
return await _request(this.fetch, 'GET', `${this.url}/user`, {
|
|
649
|
+
headers: this.headers,
|
|
650
|
+
jwt: jwt,
|
|
651
|
+
xform: _userResponse,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
if (isAuthError(error)) {
|
|
656
|
+
return { data: { user: null }, error };
|
|
691
657
|
}
|
|
692
|
-
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
693
660
|
}
|
|
694
661
|
/**
|
|
695
662
|
* Updates user data for a logged in user.
|
|
696
663
|
*/
|
|
697
|
-
updateUser(attributes, options = {}) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
throw sessionError;
|
|
703
|
-
}
|
|
704
|
-
if (!sessionData.session) {
|
|
705
|
-
throw new AuthSessionMissingError();
|
|
706
|
-
}
|
|
707
|
-
const session = sessionData.session;
|
|
708
|
-
const { data, error: userError } = yield _request(this.fetch, 'PUT', `${this.url}/user`, {
|
|
709
|
-
headers: this.headers,
|
|
710
|
-
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
711
|
-
body: attributes,
|
|
712
|
-
jwt: session.access_token,
|
|
713
|
-
xform: _userResponse,
|
|
714
|
-
});
|
|
715
|
-
if (userError)
|
|
716
|
-
throw userError;
|
|
717
|
-
session.user = data.user;
|
|
718
|
-
yield this._saveSession(session);
|
|
719
|
-
yield this._notifyAllSubscribers('USER_UPDATED', session);
|
|
720
|
-
return { data: { user: session.user }, error: null };
|
|
721
|
-
}
|
|
722
|
-
catch (error) {
|
|
723
|
-
if (isAuthError(error)) {
|
|
724
|
-
return { data: { user: null }, error };
|
|
725
|
-
}
|
|
726
|
-
throw error;
|
|
664
|
+
async updateUser(attributes, options = {}) {
|
|
665
|
+
try {
|
|
666
|
+
const { data: sessionData, error: sessionError } = await this.getSession();
|
|
667
|
+
if (sessionError) {
|
|
668
|
+
throw sessionError;
|
|
727
669
|
}
|
|
728
|
-
|
|
670
|
+
if (!sessionData.session) {
|
|
671
|
+
throw new AuthSessionMissingError();
|
|
672
|
+
}
|
|
673
|
+
const session = sessionData.session;
|
|
674
|
+
const { data, error: userError } = await _request(this.fetch, 'PUT', `${this.url}/user`, {
|
|
675
|
+
headers: this.headers,
|
|
676
|
+
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
677
|
+
body: attributes,
|
|
678
|
+
jwt: session.access_token,
|
|
679
|
+
xform: _userResponse,
|
|
680
|
+
});
|
|
681
|
+
if (userError)
|
|
682
|
+
throw userError;
|
|
683
|
+
session.user = data.user;
|
|
684
|
+
await this._saveSession(session);
|
|
685
|
+
await this._notifyAllSubscribers('USER_UPDATED', session);
|
|
686
|
+
return { data: { user: session.user }, error: null };
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
if (isAuthError(error)) {
|
|
690
|
+
return { data: { user: null }, error };
|
|
691
|
+
}
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
729
694
|
}
|
|
730
695
|
/**
|
|
731
696
|
* Decodes a JWT (without performing any validation).
|
|
@@ -738,56 +703,54 @@ export default class GoTrueClient {
|
|
|
738
703
|
* If the refresh token or access token in the current session is invalid, an error will be thrown.
|
|
739
704
|
* @param currentSession The current session that minimally contains an access token and refresh token.
|
|
740
705
|
*/
|
|
741
|
-
setSession(currentSession) {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
return { data: { user: null, session: null }, error: error };
|
|
760
|
-
}
|
|
761
|
-
if (!refreshedSession) {
|
|
762
|
-
return { data: { user: null, session: null }, error: null };
|
|
763
|
-
}
|
|
764
|
-
session = refreshedSession;
|
|
706
|
+
async setSession(currentSession) {
|
|
707
|
+
try {
|
|
708
|
+
if (!currentSession.access_token || !currentSession.refresh_token) {
|
|
709
|
+
throw new AuthSessionMissingError();
|
|
710
|
+
}
|
|
711
|
+
const timeNow = Date.now() / 1000;
|
|
712
|
+
let expiresAt = timeNow;
|
|
713
|
+
let hasExpired = true;
|
|
714
|
+
let session = null;
|
|
715
|
+
const payload = decodeJWTPayload(currentSession.access_token);
|
|
716
|
+
if (payload.exp) {
|
|
717
|
+
expiresAt = payload.exp;
|
|
718
|
+
hasExpired = expiresAt <= timeNow;
|
|
719
|
+
}
|
|
720
|
+
if (hasExpired) {
|
|
721
|
+
const { session: refreshedSession, error } = await this._callRefreshToken(currentSession.refresh_token);
|
|
722
|
+
if (error) {
|
|
723
|
+
return { data: { user: null, session: null }, error: error };
|
|
765
724
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (error) {
|
|
769
|
-
throw error;
|
|
770
|
-
}
|
|
771
|
-
session = {
|
|
772
|
-
access_token: currentSession.access_token,
|
|
773
|
-
refresh_token: currentSession.refresh_token,
|
|
774
|
-
user: data.user,
|
|
775
|
-
token_type: 'bearer',
|
|
776
|
-
expires_in: expiresAt - timeNow,
|
|
777
|
-
expires_at: expiresAt,
|
|
778
|
-
};
|
|
779
|
-
yield this._saveSession(session);
|
|
780
|
-
yield this._notifyAllSubscribers('SIGNED_IN', session);
|
|
725
|
+
if (!refreshedSession) {
|
|
726
|
+
return { data: { user: null, session: null }, error: null };
|
|
781
727
|
}
|
|
782
|
-
|
|
728
|
+
session = refreshedSession;
|
|
783
729
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
730
|
+
else {
|
|
731
|
+
const { data, error } = await this.getUser(currentSession.access_token);
|
|
732
|
+
if (error) {
|
|
733
|
+
throw error;
|
|
787
734
|
}
|
|
788
|
-
|
|
735
|
+
session = {
|
|
736
|
+
access_token: currentSession.access_token,
|
|
737
|
+
refresh_token: currentSession.refresh_token,
|
|
738
|
+
user: data.user,
|
|
739
|
+
token_type: 'bearer',
|
|
740
|
+
expires_in: expiresAt - timeNow,
|
|
741
|
+
expires_at: expiresAt,
|
|
742
|
+
};
|
|
743
|
+
await this._saveSession(session);
|
|
744
|
+
await this._notifyAllSubscribers('SIGNED_IN', session);
|
|
789
745
|
}
|
|
790
|
-
|
|
746
|
+
return { data: { user: session.user, session }, error: null };
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
if (isAuthError(error)) {
|
|
750
|
+
return { data: { session: null, user: null }, error };
|
|
751
|
+
}
|
|
752
|
+
throw error;
|
|
753
|
+
}
|
|
791
754
|
}
|
|
792
755
|
/**
|
|
793
756
|
* Returns a new session, regardless of expiry status.
|
|
@@ -795,118 +758,114 @@ export default class GoTrueClient {
|
|
|
795
758
|
* If the current session's refresh token is invalid, an error will be thrown.
|
|
796
759
|
* @param currentSession The current session. If passed in, it must contain a refresh token.
|
|
797
760
|
*/
|
|
798
|
-
refreshSession(currentSession) {
|
|
761
|
+
async refreshSession(currentSession) {
|
|
799
762
|
var _a;
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
const { data, error } = yield this.getSession();
|
|
804
|
-
if (error) {
|
|
805
|
-
throw error;
|
|
806
|
-
}
|
|
807
|
-
currentSession = (_a = data.session) !== null && _a !== void 0 ? _a : undefined;
|
|
808
|
-
}
|
|
809
|
-
if (!(currentSession === null || currentSession === void 0 ? void 0 : currentSession.refresh_token)) {
|
|
810
|
-
throw new AuthSessionMissingError();
|
|
811
|
-
}
|
|
812
|
-
const { session, error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
763
|
+
try {
|
|
764
|
+
if (!currentSession) {
|
|
765
|
+
const { data, error } = await this.getSession();
|
|
813
766
|
if (error) {
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
if (!session) {
|
|
817
|
-
return { data: { user: null, session: null }, error: null };
|
|
767
|
+
throw error;
|
|
818
768
|
}
|
|
819
|
-
|
|
769
|
+
currentSession = (_a = data.session) !== null && _a !== void 0 ? _a : undefined;
|
|
820
770
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
return { data: { user: null, session: null }, error };
|
|
824
|
-
}
|
|
825
|
-
throw error;
|
|
771
|
+
if (!(currentSession === null || currentSession === void 0 ? void 0 : currentSession.refresh_token)) {
|
|
772
|
+
throw new AuthSessionMissingError();
|
|
826
773
|
}
|
|
827
|
-
|
|
774
|
+
const { session, error } = await this._callRefreshToken(currentSession.refresh_token);
|
|
775
|
+
if (error) {
|
|
776
|
+
return { data: { user: null, session: null }, error: error };
|
|
777
|
+
}
|
|
778
|
+
if (!session) {
|
|
779
|
+
return { data: { user: null, session: null }, error: null };
|
|
780
|
+
}
|
|
781
|
+
return { data: { user: session.user, session }, error: null };
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
if (isAuthError(error)) {
|
|
785
|
+
return { data: { user: null, session: null }, error };
|
|
786
|
+
}
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
828
789
|
}
|
|
829
790
|
/**
|
|
830
791
|
* Gets the session data from a URL string
|
|
831
792
|
*/
|
|
832
|
-
_getSessionFromUrl(isPKCEFlow) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const { data, error } = yield this.exchangeCodeForSession(authCode);
|
|
848
|
-
if (error)
|
|
849
|
-
throw error;
|
|
850
|
-
if (!data.session)
|
|
851
|
-
throw new AuthPKCEGrantCodeExchangeError('No session detected.');
|
|
852
|
-
let url = new URL(window.location.href);
|
|
853
|
-
url.searchParams.delete('code');
|
|
854
|
-
window.history.replaceState(window.history.state, '', url.toString());
|
|
855
|
-
return { data: { session: data.session, redirectType: null }, error: null };
|
|
856
|
-
}
|
|
857
|
-
const error_description = getParameterByName('error_description');
|
|
858
|
-
if (error_description) {
|
|
859
|
-
const error_code = getParameterByName('error_code');
|
|
860
|
-
if (!error_code)
|
|
861
|
-
throw new AuthImplicitGrantRedirectError('No error_code detected.');
|
|
862
|
-
const error = getParameterByName('error');
|
|
863
|
-
if (!error)
|
|
864
|
-
throw new AuthImplicitGrantRedirectError('No error detected.');
|
|
865
|
-
throw new AuthImplicitGrantRedirectError(error_description, { error, code: error_code });
|
|
866
|
-
}
|
|
867
|
-
const provider_token = getParameterByName('provider_token');
|
|
868
|
-
const provider_refresh_token = getParameterByName('provider_refresh_token');
|
|
869
|
-
const access_token = getParameterByName('access_token');
|
|
870
|
-
if (!access_token)
|
|
871
|
-
throw new AuthImplicitGrantRedirectError('No access_token detected.');
|
|
872
|
-
const expires_in = getParameterByName('expires_in');
|
|
873
|
-
if (!expires_in)
|
|
874
|
-
throw new AuthImplicitGrantRedirectError('No expires_in detected.');
|
|
875
|
-
const refresh_token = getParameterByName('refresh_token');
|
|
876
|
-
if (!refresh_token)
|
|
877
|
-
throw new AuthImplicitGrantRedirectError('No refresh_token detected.');
|
|
878
|
-
const token_type = getParameterByName('token_type');
|
|
879
|
-
if (!token_type)
|
|
880
|
-
throw new AuthImplicitGrantRedirectError('No token_type detected.');
|
|
881
|
-
const timeNow = Math.round(Date.now() / 1000);
|
|
882
|
-
const expires_at = timeNow + parseInt(expires_in);
|
|
883
|
-
const { data, error } = yield this.getUser(access_token);
|
|
793
|
+
async _getSessionFromUrl(isPKCEFlow) {
|
|
794
|
+
try {
|
|
795
|
+
if (!isBrowser())
|
|
796
|
+
throw new AuthImplicitGrantRedirectError('No browser detected.');
|
|
797
|
+
if (this.flowType === 'implicit' && !this._isImplicitGrantFlow()) {
|
|
798
|
+
throw new AuthImplicitGrantRedirectError('Not a valid implicit grant flow url.');
|
|
799
|
+
}
|
|
800
|
+
else if (this.flowType == 'pkce' && !isPKCEFlow) {
|
|
801
|
+
throw new AuthPKCEGrantCodeExchangeError('Not a valid PKCE flow url.');
|
|
802
|
+
}
|
|
803
|
+
if (isPKCEFlow) {
|
|
804
|
+
const authCode = getParameterByName('code');
|
|
805
|
+
if (!authCode)
|
|
806
|
+
throw new AuthPKCEGrantCodeExchangeError('No code detected.');
|
|
807
|
+
const { data, error } = await this.exchangeCodeForSession(authCode);
|
|
884
808
|
if (error)
|
|
885
809
|
throw error;
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
810
|
+
if (!data.session)
|
|
811
|
+
throw new AuthPKCEGrantCodeExchangeError('No session detected.');
|
|
812
|
+
let url = new URL(window.location.href);
|
|
813
|
+
url.searchParams.delete('code');
|
|
814
|
+
window.history.replaceState(window.history.state, '', url.toString());
|
|
815
|
+
return { data: { session: data.session, redirectType: null }, error: null };
|
|
816
|
+
}
|
|
817
|
+
const error_description = getParameterByName('error_description');
|
|
818
|
+
if (error_description) {
|
|
819
|
+
const error_code = getParameterByName('error_code');
|
|
820
|
+
if (!error_code)
|
|
821
|
+
throw new AuthImplicitGrantRedirectError('No error_code detected.');
|
|
822
|
+
const error = getParameterByName('error');
|
|
823
|
+
if (!error)
|
|
824
|
+
throw new AuthImplicitGrantRedirectError('No error detected.');
|
|
825
|
+
throw new AuthImplicitGrantRedirectError(error_description, { error, code: error_code });
|
|
826
|
+
}
|
|
827
|
+
const provider_token = getParameterByName('provider_token');
|
|
828
|
+
const provider_refresh_token = getParameterByName('provider_refresh_token');
|
|
829
|
+
const access_token = getParameterByName('access_token');
|
|
830
|
+
if (!access_token)
|
|
831
|
+
throw new AuthImplicitGrantRedirectError('No access_token detected.');
|
|
832
|
+
const expires_in = getParameterByName('expires_in');
|
|
833
|
+
if (!expires_in)
|
|
834
|
+
throw new AuthImplicitGrantRedirectError('No expires_in detected.');
|
|
835
|
+
const refresh_token = getParameterByName('refresh_token');
|
|
836
|
+
if (!refresh_token)
|
|
837
|
+
throw new AuthImplicitGrantRedirectError('No refresh_token detected.');
|
|
838
|
+
const token_type = getParameterByName('token_type');
|
|
839
|
+
if (!token_type)
|
|
840
|
+
throw new AuthImplicitGrantRedirectError('No token_type detected.');
|
|
841
|
+
const timeNow = Math.round(Date.now() / 1000);
|
|
842
|
+
const expires_at = timeNow + parseInt(expires_in);
|
|
843
|
+
const { data, error } = await this.getUser(access_token);
|
|
844
|
+
if (error)
|
|
907
845
|
throw error;
|
|
846
|
+
const user = data.user;
|
|
847
|
+
const session = {
|
|
848
|
+
provider_token,
|
|
849
|
+
provider_refresh_token,
|
|
850
|
+
access_token,
|
|
851
|
+
expires_in: parseInt(expires_in),
|
|
852
|
+
expires_at,
|
|
853
|
+
refresh_token,
|
|
854
|
+
token_type,
|
|
855
|
+
user,
|
|
856
|
+
};
|
|
857
|
+
const redirectType = getParameterByName('type');
|
|
858
|
+
// Remove tokens from URL
|
|
859
|
+
window.location.hash = '';
|
|
860
|
+
this._debug('#_getSessionFromUrl()', 'clearing window.location.hash');
|
|
861
|
+
return { data: { session, redirectType }, error: null };
|
|
862
|
+
}
|
|
863
|
+
catch (error) {
|
|
864
|
+
if (isAuthError(error)) {
|
|
865
|
+
return { data: { session: null, redirectType: null }, error };
|
|
908
866
|
}
|
|
909
|
-
|
|
867
|
+
throw error;
|
|
868
|
+
}
|
|
910
869
|
}
|
|
911
870
|
/**
|
|
912
871
|
* Checks if the current URL contains parameters given by an implicit oauth grant flow (https://www.rfc-editor.org/rfc/rfc6749.html#section-4.2)
|
|
@@ -919,11 +878,9 @@ export default class GoTrueClient {
|
|
|
919
878
|
/**
|
|
920
879
|
* Checks if the current URL and backing storage contain parameters given by a PKCE flow
|
|
921
880
|
*/
|
|
922
|
-
_isPKCEFlow() {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
return Boolean(getParameterByName('code')) && Boolean(currentStorageContent);
|
|
926
|
-
});
|
|
881
|
+
async _isPKCEFlow() {
|
|
882
|
+
const currentStorageContent = await getItemAsync(this.storage, `${this.storageKey}-code-verifier`);
|
|
883
|
+
return Boolean(getParameterByName('code')) && Boolean(currentStorageContent);
|
|
927
884
|
}
|
|
928
885
|
/**
|
|
929
886
|
* Inside a browser context, `signOut()` will remove the logged in user from the browser session
|
|
@@ -934,31 +891,29 @@ export default class GoTrueClient {
|
|
|
934
891
|
*
|
|
935
892
|
* If using others scope, no `SIGNED_OUT` event is fired!
|
|
936
893
|
*/
|
|
937
|
-
signOut({ scope } = { scope: 'global' }) {
|
|
894
|
+
async signOut({ scope } = { scope: 'global' }) {
|
|
938
895
|
var _a;
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
return { error };
|
|
952
|
-
}
|
|
896
|
+
const { data, error: sessionError } = await this.getSession();
|
|
897
|
+
if (sessionError) {
|
|
898
|
+
return { error: sessionError };
|
|
899
|
+
}
|
|
900
|
+
const accessToken = (_a = data.session) === null || _a === void 0 ? void 0 : _a.access_token;
|
|
901
|
+
if (accessToken) {
|
|
902
|
+
const { error } = await this.admin.signOut(accessToken, scope);
|
|
903
|
+
if (error) {
|
|
904
|
+
// ignore 404s since user might not exist anymore
|
|
905
|
+
// ignore 401s since an invalid or expired JWT should sign out the current session
|
|
906
|
+
if (!(isAuthApiError(error) && (error.status === 404 || error.status === 401))) {
|
|
907
|
+
return { error };
|
|
953
908
|
}
|
|
954
909
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
910
|
+
}
|
|
911
|
+
if (scope !== 'others') {
|
|
912
|
+
await this._removeSession();
|
|
913
|
+
await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`);
|
|
914
|
+
await this._notifyAllSubscribers('SIGNED_OUT', null);
|
|
915
|
+
}
|
|
916
|
+
return { error: null };
|
|
962
917
|
}
|
|
963
918
|
/**
|
|
964
919
|
* Receive a notification every time an auth event happens.
|
|
@@ -979,22 +934,20 @@ export default class GoTrueClient {
|
|
|
979
934
|
this._emitInitialSession(id);
|
|
980
935
|
return { data: { subscription } };
|
|
981
936
|
}
|
|
982
|
-
_emitInitialSession(id) {
|
|
937
|
+
async _emitInitialSession(id) {
|
|
983
938
|
var _a, _b;
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
});
|
|
939
|
+
try {
|
|
940
|
+
const { data: { session }, error, } = await this.getSession();
|
|
941
|
+
if (error)
|
|
942
|
+
throw error;
|
|
943
|
+
await ((_a = this.stateChangeEmitters.get(id)) === null || _a === void 0 ? void 0 : _a.callback('INITIAL_SESSION', session));
|
|
944
|
+
this._debug('INITIAL_SESSION', 'callback id', id, 'session', session);
|
|
945
|
+
}
|
|
946
|
+
catch (err) {
|
|
947
|
+
await ((_b = this.stateChangeEmitters.get(id)) === null || _b === void 0 ? void 0 : _b.callback('INITIAL_SESSION', null));
|
|
948
|
+
this._debug('INITIAL_SESSION', 'callback id', id, 'error', err);
|
|
949
|
+
console.error(err);
|
|
950
|
+
}
|
|
998
951
|
}
|
|
999
952
|
/**
|
|
1000
953
|
* Sends a password reset request to an email address.
|
|
@@ -1003,72 +956,68 @@ export default class GoTrueClient {
|
|
|
1003
956
|
* @param options.redirectTo The URL to send the user to after they click the password reset link.
|
|
1004
957
|
* @param options.captchaToken Verification token received when the user completes the captcha on the site.
|
|
1005
958
|
*/
|
|
1006
|
-
resetPasswordForEmail(email, options = {}) {
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
return { data: null, error };
|
|
1031
|
-
}
|
|
1032
|
-
throw error;
|
|
959
|
+
async resetPasswordForEmail(email, options = {}) {
|
|
960
|
+
let codeChallenge = null;
|
|
961
|
+
let codeChallengeMethod = null;
|
|
962
|
+
if (this.flowType === 'pkce') {
|
|
963
|
+
const codeVerifier = generatePKCEVerifier();
|
|
964
|
+
await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier);
|
|
965
|
+
codeChallenge = await generatePKCEChallenge(codeVerifier);
|
|
966
|
+
codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256';
|
|
967
|
+
}
|
|
968
|
+
try {
|
|
969
|
+
return await _request(this.fetch, 'POST', `${this.url}/recover`, {
|
|
970
|
+
body: {
|
|
971
|
+
email,
|
|
972
|
+
code_challenge: codeChallenge,
|
|
973
|
+
code_challenge_method: codeChallengeMethod,
|
|
974
|
+
gotrue_meta_security: { captcha_token: options.captchaToken },
|
|
975
|
+
},
|
|
976
|
+
headers: this.headers,
|
|
977
|
+
redirectTo: options.redirectTo,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
if (isAuthError(error)) {
|
|
982
|
+
return { data: null, error };
|
|
1033
983
|
}
|
|
1034
|
-
|
|
984
|
+
throw error;
|
|
985
|
+
}
|
|
1035
986
|
}
|
|
1036
987
|
/**
|
|
1037
988
|
* Generates a new JWT.
|
|
1038
989
|
* @param refreshToken A valid refresh token that was returned on login.
|
|
1039
990
|
*/
|
|
1040
|
-
_refreshAccessToken(refreshToken) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
return { data: { session: null, user: null }, error };
|
|
1065
|
-
}
|
|
1066
|
-
throw error;
|
|
1067
|
-
}
|
|
1068
|
-
finally {
|
|
1069
|
-
this._debug(debugName, 'end');
|
|
991
|
+
async _refreshAccessToken(refreshToken) {
|
|
992
|
+
const debugName = `#_refreshAccessToken(${refreshToken.substring(0, 5)}...)`;
|
|
993
|
+
this._debug(debugName, 'begin');
|
|
994
|
+
try {
|
|
995
|
+
const startedAt = Date.now();
|
|
996
|
+
// will attempt to refresh the token with exponential backoff
|
|
997
|
+
return await retryable(async (attempt) => {
|
|
998
|
+
await sleep(attempt * 200); // 0, 200, 400, 800, ...
|
|
999
|
+
this._debug(debugName, 'refreshing attempt', attempt);
|
|
1000
|
+
return await _request(this.fetch, 'POST', `${this.url}/token?grant_type=refresh_token`, {
|
|
1001
|
+
body: { refresh_token: refreshToken },
|
|
1002
|
+
headers: this.headers,
|
|
1003
|
+
xform: _sessionResponse,
|
|
1004
|
+
});
|
|
1005
|
+
}, (attempt, _, result) => result &&
|
|
1006
|
+
result.error &&
|
|
1007
|
+
isAuthRetryableFetchError(result.error) &&
|
|
1008
|
+
// retryable only if the request can be sent before the backoff overflows the tick duration
|
|
1009
|
+
Date.now() + (attempt + 1) * 200 - startedAt < AUTO_REFRESH_TICK_DURATION);
|
|
1010
|
+
}
|
|
1011
|
+
catch (error) {
|
|
1012
|
+
this._debug(debugName, 'error', error);
|
|
1013
|
+
if (isAuthError(error)) {
|
|
1014
|
+
return { data: { session: null, user: null }, error };
|
|
1070
1015
|
}
|
|
1071
|
-
|
|
1016
|
+
throw error;
|
|
1017
|
+
}
|
|
1018
|
+
finally {
|
|
1019
|
+
this._debug(debugName, 'end');
|
|
1020
|
+
}
|
|
1072
1021
|
}
|
|
1073
1022
|
_isValidSession(maybeSession) {
|
|
1074
1023
|
const isValidSession = typeof maybeSession === 'object' &&
|
|
@@ -1078,172 +1027,160 @@ export default class GoTrueClient {
|
|
|
1078
1027
|
'expires_at' in maybeSession;
|
|
1079
1028
|
return isValidSession;
|
|
1080
1029
|
}
|
|
1081
|
-
_handleProviderSignIn(provider, options) {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
queryParams: options.queryParams,
|
|
1087
|
-
});
|
|
1088
|
-
this._debug('#_handleProviderSignIn()', 'provider', provider, 'options', options, 'url', url);
|
|
1089
|
-
// try to open on the browser
|
|
1090
|
-
if (isBrowser() && !options.skipBrowserRedirect) {
|
|
1091
|
-
window.location.assign(url);
|
|
1092
|
-
}
|
|
1093
|
-
return { data: { provider, url }, error: null };
|
|
1030
|
+
async _handleProviderSignIn(provider, options) {
|
|
1031
|
+
const url = await this._getUrlForProvider(provider, {
|
|
1032
|
+
redirectTo: options.redirectTo,
|
|
1033
|
+
scopes: options.scopes,
|
|
1034
|
+
queryParams: options.queryParams,
|
|
1094
1035
|
});
|
|
1036
|
+
this._debug('#_handleProviderSignIn()', 'provider', provider, 'options', options, 'url', url);
|
|
1037
|
+
// try to open on the browser
|
|
1038
|
+
if (isBrowser() && !options.skipBrowserRedirect) {
|
|
1039
|
+
window.location.assign(url);
|
|
1040
|
+
}
|
|
1041
|
+
return { data: { provider, url }, error: null };
|
|
1095
1042
|
}
|
|
1096
1043
|
/**
|
|
1097
1044
|
* Recovers the session from LocalStorage and refreshes
|
|
1098
1045
|
* Note: this method is async to accommodate for AsyncStorage e.g. in React native.
|
|
1099
1046
|
*/
|
|
1100
|
-
_recoverAndRefresh() {
|
|
1047
|
+
async _recoverAndRefresh() {
|
|
1101
1048
|
var _a;
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
yield this._removeSession();
|
|
1112
|
-
}
|
|
1113
|
-
return;
|
|
1049
|
+
const debugName = '#_recoverAndRefresh()';
|
|
1050
|
+
this._debug(debugName, 'begin');
|
|
1051
|
+
try {
|
|
1052
|
+
const currentSession = await getItemAsync(this.storage, this.storageKey);
|
|
1053
|
+
this._debug(debugName, 'session from storage', currentSession);
|
|
1054
|
+
if (!this._isValidSession(currentSession)) {
|
|
1055
|
+
this._debug(debugName, 'session is not valid');
|
|
1056
|
+
if (currentSession !== null) {
|
|
1057
|
+
await this._removeSession();
|
|
1114
1058
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const timeNow = Math.round(Date.now() / 1000);
|
|
1062
|
+
const expiresWithMargin = ((_a = currentSession.expires_at) !== null && _a !== void 0 ? _a : Infinity) < timeNow + EXPIRY_MARGIN;
|
|
1063
|
+
this._debug(debugName, `session has${expiresWithMargin ? '' : ' not'} expired with margin of ${EXPIRY_MARGIN}s`);
|
|
1064
|
+
if (expiresWithMargin) {
|
|
1065
|
+
if (this.autoRefreshToken && currentSession.refresh_token) {
|
|
1066
|
+
const { error } = await this._callRefreshToken(currentSession.refresh_token);
|
|
1067
|
+
if (error) {
|
|
1068
|
+
console.error(error);
|
|
1069
|
+
if (!isAuthRetryableFetchError(error)) {
|
|
1070
|
+
this._debug(debugName, 'refresh failed with a non-retryable error, removing the session', error);
|
|
1071
|
+
await this._removeSession();
|
|
1127
1072
|
}
|
|
1128
1073
|
}
|
|
1129
1074
|
}
|
|
1130
|
-
else {
|
|
1131
|
-
// no need to persist currentSession again, as we just loaded it from
|
|
1132
|
-
// local storage; persisting it again may overwrite a value saved by
|
|
1133
|
-
// another client with access to the same local storage
|
|
1134
|
-
yield this._notifyAllSubscribers('SIGNED_IN', currentSession);
|
|
1135
|
-
}
|
|
1136
1075
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
finally {
|
|
1143
|
-
this._debug(debugName, 'end');
|
|
1076
|
+
else {
|
|
1077
|
+
// no need to persist currentSession again, as we just loaded it from
|
|
1078
|
+
// local storage; persisting it again may overwrite a value saved by
|
|
1079
|
+
// another client with access to the same local storage
|
|
1080
|
+
await this._notifyAllSubscribers('SIGNED_IN', currentSession);
|
|
1144
1081
|
}
|
|
1145
|
-
}
|
|
1082
|
+
}
|
|
1083
|
+
catch (err) {
|
|
1084
|
+
this._debug(debugName, 'error', err);
|
|
1085
|
+
console.error(err);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
finally {
|
|
1089
|
+
this._debug(debugName, 'end');
|
|
1090
|
+
}
|
|
1146
1091
|
}
|
|
1147
|
-
_callRefreshToken(refreshToken) {
|
|
1092
|
+
async _callRefreshToken(refreshToken) {
|
|
1148
1093
|
var _a, _b;
|
|
1149
|
-
|
|
1150
|
-
|
|
1094
|
+
if (!refreshToken) {
|
|
1095
|
+
throw new AuthSessionMissingError();
|
|
1096
|
+
}
|
|
1097
|
+
// refreshing is already in progress
|
|
1098
|
+
if (this.refreshingDeferred) {
|
|
1099
|
+
return this.refreshingDeferred.promise;
|
|
1100
|
+
}
|
|
1101
|
+
const debugName = `#_callRefreshToken(${refreshToken.substring(0, 5)}...)`;
|
|
1102
|
+
this._debug(debugName, 'begin');
|
|
1103
|
+
try {
|
|
1104
|
+
this.refreshingDeferred = new Deferred();
|
|
1105
|
+
const { data, error } = await this._refreshAccessToken(refreshToken);
|
|
1106
|
+
if (error)
|
|
1107
|
+
throw error;
|
|
1108
|
+
if (!data.session)
|
|
1151
1109
|
throw new AuthSessionMissingError();
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
const {
|
|
1162
|
-
|
|
1163
|
-
throw error;
|
|
1164
|
-
if (!data.session)
|
|
1165
|
-
throw new AuthSessionMissingError();
|
|
1166
|
-
yield this._saveSession(data.session);
|
|
1167
|
-
yield this._notifyAllSubscribers('TOKEN_REFRESHED', data.session);
|
|
1168
|
-
const result = { session: data.session, error: null };
|
|
1169
|
-
this.refreshingDeferred.resolve(result);
|
|
1110
|
+
await this._saveSession(data.session);
|
|
1111
|
+
await this._notifyAllSubscribers('TOKEN_REFRESHED', data.session);
|
|
1112
|
+
const result = { session: data.session, error: null };
|
|
1113
|
+
this.refreshingDeferred.resolve(result);
|
|
1114
|
+
return result;
|
|
1115
|
+
}
|
|
1116
|
+
catch (error) {
|
|
1117
|
+
this._debug(debugName, 'error', error);
|
|
1118
|
+
if (isAuthError(error)) {
|
|
1119
|
+
const result = { session: null, error };
|
|
1120
|
+
(_a = this.refreshingDeferred) === null || _a === void 0 ? void 0 : _a.resolve(result);
|
|
1170
1121
|
return result;
|
|
1171
1122
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
(_b = this.refreshingDeferred) === null || _b === void 0 ? void 0 : _b.reject(error);
|
|
1180
|
-
throw error;
|
|
1181
|
-
}
|
|
1182
|
-
finally {
|
|
1183
|
-
this.refreshingDeferred = null;
|
|
1184
|
-
this._debug(debugName, 'end');
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1123
|
+
(_b = this.refreshingDeferred) === null || _b === void 0 ? void 0 : _b.reject(error);
|
|
1124
|
+
throw error;
|
|
1125
|
+
}
|
|
1126
|
+
finally {
|
|
1127
|
+
this.refreshingDeferred = null;
|
|
1128
|
+
this._debug(debugName, 'end');
|
|
1129
|
+
}
|
|
1187
1130
|
}
|
|
1188
|
-
_notifyAllSubscribers(event, session, broadcast = true) {
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1131
|
+
async _notifyAllSubscribers(event, session, broadcast = true) {
|
|
1132
|
+
const debugName = `#_notifyAllSubscribers(${event})`;
|
|
1133
|
+
this._debug(debugName, 'begin', session, `broadcast = ${broadcast}`);
|
|
1134
|
+
try {
|
|
1135
|
+
if (this.broadcastChannel && broadcast) {
|
|
1136
|
+
this.broadcastChannel.postMessage({ event, session });
|
|
1137
|
+
}
|
|
1138
|
+
const errors = [];
|
|
1139
|
+
const promises = Array.from(this.stateChangeEmitters.values()).map(async (x) => {
|
|
1140
|
+
try {
|
|
1141
|
+
await x.callback(event, session);
|
|
1195
1142
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
try {
|
|
1199
|
-
yield x.callback(event, session);
|
|
1200
|
-
}
|
|
1201
|
-
catch (e) {
|
|
1202
|
-
errors.push(e);
|
|
1203
|
-
}
|
|
1204
|
-
}));
|
|
1205
|
-
yield Promise.all(promises);
|
|
1206
|
-
if (errors.length > 0) {
|
|
1207
|
-
for (let i = 0; i < errors.length; i += 1) {
|
|
1208
|
-
console.error(errors[i]);
|
|
1209
|
-
}
|
|
1210
|
-
throw errors[0];
|
|
1143
|
+
catch (e) {
|
|
1144
|
+
errors.push(e);
|
|
1211
1145
|
}
|
|
1146
|
+
});
|
|
1147
|
+
await Promise.all(promises);
|
|
1148
|
+
if (errors.length > 0) {
|
|
1149
|
+
for (let i = 0; i < errors.length; i += 1) {
|
|
1150
|
+
console.error(errors[i]);
|
|
1151
|
+
}
|
|
1152
|
+
throw errors[0];
|
|
1212
1153
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
}
|
|
1154
|
+
}
|
|
1155
|
+
finally {
|
|
1156
|
+
this._debug(debugName, 'end');
|
|
1157
|
+
}
|
|
1217
1158
|
}
|
|
1218
1159
|
/**
|
|
1219
1160
|
* set currentSession and currentUser
|
|
1220
1161
|
* process to _startAutoRefreshToken if possible
|
|
1221
1162
|
*/
|
|
1222
|
-
_saveSession(session) {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
});
|
|
1163
|
+
async _saveSession(session) {
|
|
1164
|
+
this._debug('#_saveSession()', session);
|
|
1165
|
+
if (!this.persistSession) {
|
|
1166
|
+
this.inMemorySession = session;
|
|
1167
|
+
}
|
|
1168
|
+
if (this.persistSession && session.expires_at) {
|
|
1169
|
+
await this._persistSession(session);
|
|
1170
|
+
}
|
|
1232
1171
|
}
|
|
1233
1172
|
_persistSession(currentSession) {
|
|
1234
1173
|
this._debug('#_persistSession()', currentSession);
|
|
1235
1174
|
return setItemAsync(this.storage, this.storageKey, currentSession);
|
|
1236
1175
|
}
|
|
1237
|
-
_removeSession() {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
}
|
|
1246
|
-
});
|
|
1176
|
+
async _removeSession() {
|
|
1177
|
+
this._debug('#_removeSession()');
|
|
1178
|
+
if (this.persistSession) {
|
|
1179
|
+
await removeItemAsync(this.storage, this.storageKey);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
this.inMemorySession = null;
|
|
1183
|
+
}
|
|
1247
1184
|
}
|
|
1248
1185
|
/**
|
|
1249
1186
|
* Removes any registered visibilitychange callback.
|
|
@@ -1268,45 +1205,41 @@ export default class GoTrueClient {
|
|
|
1268
1205
|
* This is the private implementation of {@link #startAutoRefresh}. Use this
|
|
1269
1206
|
* within the library.
|
|
1270
1207
|
*/
|
|
1271
|
-
_startAutoRefresh() {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
yield this._autoRefreshTokenTick();
|
|
1295
|
-
});
|
|
1208
|
+
async _startAutoRefresh() {
|
|
1209
|
+
await this._stopAutoRefresh();
|
|
1210
|
+
this._debug('#_startAutoRefresh()');
|
|
1211
|
+
const ticker = setInterval(() => this._autoRefreshTokenTick(), AUTO_REFRESH_TICK_DURATION);
|
|
1212
|
+
this.autoRefreshTicker = ticker;
|
|
1213
|
+
if (ticker && typeof ticker === 'object' && typeof ticker.unref === 'function') {
|
|
1214
|
+
// ticker is a NodeJS Timeout object that has an `unref` method
|
|
1215
|
+
// https://nodejs.org/api/timers.html#timeoutunref
|
|
1216
|
+
// When auto refresh is used in NodeJS (like for testing) the
|
|
1217
|
+
// `setInterval` is preventing the process from being marked as
|
|
1218
|
+
// finished and tests run endlessly. This can be prevented by calling
|
|
1219
|
+
// `unref()` on the returned object.
|
|
1220
|
+
ticker.unref();
|
|
1221
|
+
// @ts-ignore
|
|
1222
|
+
}
|
|
1223
|
+
else if (typeof Deno !== 'undefined' && typeof Deno.unrefTimer === 'function') {
|
|
1224
|
+
// similar like for NodeJS, but with the Deno API
|
|
1225
|
+
// https://deno.land/api@latest?unstable&s=Deno.unrefTimer
|
|
1226
|
+
// @ts-ignore
|
|
1227
|
+
Deno.unrefTimer(ticker);
|
|
1228
|
+
}
|
|
1229
|
+
// run the tick immediately
|
|
1230
|
+
await this._autoRefreshTokenTick();
|
|
1296
1231
|
}
|
|
1297
1232
|
/**
|
|
1298
1233
|
* This is the private implementation of {@link #stopAutoRefresh}. Use this
|
|
1299
1234
|
* within the library.
|
|
1300
1235
|
*/
|
|
1301
|
-
_stopAutoRefresh() {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1236
|
+
async _stopAutoRefresh() {
|
|
1237
|
+
this._debug('#_stopAutoRefresh()');
|
|
1238
|
+
const ticker = this.autoRefreshTicker;
|
|
1239
|
+
this.autoRefreshTicker = null;
|
|
1240
|
+
if (ticker) {
|
|
1241
|
+
clearInterval(ticker);
|
|
1242
|
+
}
|
|
1310
1243
|
}
|
|
1311
1244
|
/**
|
|
1312
1245
|
* Starts an auto-refresh process in the background. The session is checked
|
|
@@ -1330,11 +1263,9 @@ export default class GoTrueClient {
|
|
|
1330
1263
|
*
|
|
1331
1264
|
* {@see #stopAutoRefresh}
|
|
1332
1265
|
*/
|
|
1333
|
-
startAutoRefresh() {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
yield this._startAutoRefresh();
|
|
1337
|
-
});
|
|
1266
|
+
async startAutoRefresh() {
|
|
1267
|
+
this._removeVisibilityChangedCallback();
|
|
1268
|
+
await this._startAutoRefresh();
|
|
1338
1269
|
}
|
|
1339
1270
|
/**
|
|
1340
1271
|
* Stops an active auto refresh process running in the background (if any).
|
|
@@ -1344,94 +1275,86 @@ export default class GoTrueClient {
|
|
|
1344
1275
|
*
|
|
1345
1276
|
* See {@link #startAutoRefresh} for more details.
|
|
1346
1277
|
*/
|
|
1347
|
-
stopAutoRefresh() {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
yield this._stopAutoRefresh();
|
|
1351
|
-
});
|
|
1278
|
+
async stopAutoRefresh() {
|
|
1279
|
+
this._removeVisibilityChangedCallback();
|
|
1280
|
+
await this._stopAutoRefresh();
|
|
1352
1281
|
}
|
|
1353
1282
|
/**
|
|
1354
1283
|
* Runs the auto refresh token tick.
|
|
1355
1284
|
*/
|
|
1356
|
-
_autoRefreshTokenTick() {
|
|
1357
|
-
|
|
1358
|
-
|
|
1285
|
+
async _autoRefreshTokenTick() {
|
|
1286
|
+
this._debug('#_autoRefreshTokenTick()', 'begin');
|
|
1287
|
+
try {
|
|
1288
|
+
const now = Date.now();
|
|
1359
1289
|
try {
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
this._debug('#_autoRefreshTokenTick()', 'no session');
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
// session will expire in this many ticks (or has already expired if <= 0)
|
|
1368
|
-
const expiresInTicks = Math.floor((session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION);
|
|
1369
|
-
this._debug('#_autoRefreshTokenTick()', `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`);
|
|
1370
|
-
if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
|
|
1371
|
-
yield this._callRefreshToken(session.refresh_token);
|
|
1372
|
-
}
|
|
1290
|
+
const { data: { session }, } = await this.getSession();
|
|
1291
|
+
if (!session || !session.refresh_token || !session.expires_at) {
|
|
1292
|
+
this._debug('#_autoRefreshTokenTick()', 'no session');
|
|
1293
|
+
return;
|
|
1373
1294
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1295
|
+
// session will expire in this many ticks (or has already expired if <= 0)
|
|
1296
|
+
const expiresInTicks = Math.floor((session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION);
|
|
1297
|
+
this._debug('#_autoRefreshTokenTick()', `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`);
|
|
1298
|
+
if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
|
|
1299
|
+
await this._callRefreshToken(session.refresh_token);
|
|
1376
1300
|
}
|
|
1377
1301
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1302
|
+
catch (e) {
|
|
1303
|
+
console.error('Auto refresh tick failed with error. This is likely a transient error.', e);
|
|
1380
1304
|
}
|
|
1381
|
-
}
|
|
1305
|
+
}
|
|
1306
|
+
finally {
|
|
1307
|
+
this._debug('#_autoRefreshTokenTick()', 'end');
|
|
1308
|
+
}
|
|
1382
1309
|
}
|
|
1383
1310
|
/**
|
|
1384
1311
|
* Registers callbacks on the browser / platform, which in-turn run
|
|
1385
1312
|
* algorithms when the browser window/tab are in foreground. On non-browser
|
|
1386
1313
|
* platforms it assumes always foreground.
|
|
1387
1314
|
*/
|
|
1388
|
-
_handleVisibilityChange() {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
if (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
}
|
|
1408
|
-
});
|
|
1315
|
+
async _handleVisibilityChange() {
|
|
1316
|
+
this._debug('#_handleVisibilityChange()');
|
|
1317
|
+
if (!isBrowser() || !(window === null || window === void 0 ? void 0 : window.addEventListener)) {
|
|
1318
|
+
if (this.autoRefreshToken) {
|
|
1319
|
+
// in non-browser environments the refresh token ticker runs always
|
|
1320
|
+
this.startAutoRefresh();
|
|
1321
|
+
}
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
try {
|
|
1325
|
+
this.visibilityChangedCallback = async () => await this._onVisibilityChanged(false);
|
|
1326
|
+
window === null || window === void 0 ? void 0 : window.addEventListener('visibilitychange', this.visibilityChangedCallback);
|
|
1327
|
+
// now immediately call the visbility changed callback to setup with the
|
|
1328
|
+
// current visbility state
|
|
1329
|
+
await this._onVisibilityChanged(true); // initial call
|
|
1330
|
+
}
|
|
1331
|
+
catch (error) {
|
|
1332
|
+
console.error('_handleVisibilityChange', error);
|
|
1333
|
+
}
|
|
1409
1334
|
}
|
|
1410
1335
|
/**
|
|
1411
1336
|
* Callback registered with `window.addEventListener('visibilitychange')`.
|
|
1412
1337
|
*/
|
|
1413
|
-
_onVisibilityChanged(isInitial) {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
if (
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
this._startAutoRefresh();
|
|
1427
|
-
}
|
|
1338
|
+
async _onVisibilityChanged(isInitial) {
|
|
1339
|
+
this._debug(`#_onVisibilityChanged(${isInitial})`, 'visibilityState', document.visibilityState);
|
|
1340
|
+
if (document.visibilityState === 'visible') {
|
|
1341
|
+
if (!isInitial) {
|
|
1342
|
+
// initial visibility change setup is handled in another flow under #initialize()
|
|
1343
|
+
await this.initializePromise;
|
|
1344
|
+
await this._recoverAndRefresh();
|
|
1345
|
+
this._debug('#_onVisibilityChanged()', 'finished waiting for initialize, _recoverAndRefresh');
|
|
1346
|
+
}
|
|
1347
|
+
if (this.autoRefreshToken) {
|
|
1348
|
+
// in browser environments the refresh token ticker runs only on focused tabs
|
|
1349
|
+
// which prevents race conditions
|
|
1350
|
+
this._startAutoRefresh();
|
|
1428
1351
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1352
|
+
}
|
|
1353
|
+
else if (document.visibilityState === 'hidden') {
|
|
1354
|
+
if (this.autoRefreshToken) {
|
|
1355
|
+
this._stopAutoRefresh();
|
|
1433
1356
|
}
|
|
1434
|
-
}
|
|
1357
|
+
}
|
|
1435
1358
|
}
|
|
1436
1359
|
/**
|
|
1437
1360
|
* Generates the relevant login URL for a third-party provider.
|
|
@@ -1439,213 +1362,197 @@ export default class GoTrueClient {
|
|
|
1439
1362
|
* @param options.scopes A space-separated list of scopes granted to the OAuth application.
|
|
1440
1363
|
* @param options.queryParams An object of key-value pairs containing query parameters granted to the OAuth application.
|
|
1441
1364
|
*/
|
|
1442
|
-
_getUrlForProvider(provider, options) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
return `${this.url}/authorize?${urlParams.join('&')}`;
|
|
1468
|
-
});
|
|
1365
|
+
async _getUrlForProvider(provider, options) {
|
|
1366
|
+
const urlParams = [`provider=${encodeURIComponent(provider)}`];
|
|
1367
|
+
if (options === null || options === void 0 ? void 0 : options.redirectTo) {
|
|
1368
|
+
urlParams.push(`redirect_to=${encodeURIComponent(options.redirectTo)}`);
|
|
1369
|
+
}
|
|
1370
|
+
if (options === null || options === void 0 ? void 0 : options.scopes) {
|
|
1371
|
+
urlParams.push(`scopes=${encodeURIComponent(options.scopes)}`);
|
|
1372
|
+
}
|
|
1373
|
+
if (this.flowType === 'pkce') {
|
|
1374
|
+
const codeVerifier = generatePKCEVerifier();
|
|
1375
|
+
await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier);
|
|
1376
|
+
const codeChallenge = await generatePKCEChallenge(codeVerifier);
|
|
1377
|
+
const codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256';
|
|
1378
|
+
this._debug('PKCE', 'code verifier', `${codeVerifier.substring(0, 5)}...`, 'code challenge', codeChallenge, 'method', codeChallengeMethod);
|
|
1379
|
+
const flowParams = new URLSearchParams({
|
|
1380
|
+
code_challenge: `${encodeURIComponent(codeChallenge)}`,
|
|
1381
|
+
code_challenge_method: `${encodeURIComponent(codeChallengeMethod)}`,
|
|
1382
|
+
});
|
|
1383
|
+
urlParams.push(flowParams.toString());
|
|
1384
|
+
}
|
|
1385
|
+
if (options === null || options === void 0 ? void 0 : options.queryParams) {
|
|
1386
|
+
const query = new URLSearchParams(options.queryParams);
|
|
1387
|
+
urlParams.push(query.toString());
|
|
1388
|
+
}
|
|
1389
|
+
return `${this.url}/authorize?${urlParams.join('&')}`;
|
|
1469
1390
|
}
|
|
1470
|
-
_unenroll(params) {
|
|
1391
|
+
async _unenroll(params) {
|
|
1471
1392
|
var _a;
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
return { data: null, error: sessionError };
|
|
1477
|
-
}
|
|
1478
|
-
return yield _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
|
|
1479
|
-
headers: this.headers,
|
|
1480
|
-
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1481
|
-
});
|
|
1393
|
+
try {
|
|
1394
|
+
const { data: sessionData, error: sessionError } = await this.getSession();
|
|
1395
|
+
if (sessionError) {
|
|
1396
|
+
return { data: null, error: sessionError };
|
|
1482
1397
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1398
|
+
return await _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
|
|
1399
|
+
headers: this.headers,
|
|
1400
|
+
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
catch (error) {
|
|
1404
|
+
if (isAuthError(error)) {
|
|
1405
|
+
return { data: null, error };
|
|
1488
1406
|
}
|
|
1489
|
-
|
|
1407
|
+
throw error;
|
|
1408
|
+
}
|
|
1490
1409
|
}
|
|
1491
1410
|
/**
|
|
1492
1411
|
* {@see GoTrueMFAApi#enroll}
|
|
1493
1412
|
*/
|
|
1494
|
-
_enroll(params) {
|
|
1413
|
+
async _enroll(params) {
|
|
1495
1414
|
var _a, _b;
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
return { data: null, error: sessionError };
|
|
1501
|
-
}
|
|
1502
|
-
const { data, error } = yield _request(this.fetch, 'POST', `${this.url}/factors`, {
|
|
1503
|
-
body: {
|
|
1504
|
-
friendly_name: params.friendlyName,
|
|
1505
|
-
factor_type: params.factorType,
|
|
1506
|
-
issuer: params.issuer,
|
|
1507
|
-
},
|
|
1508
|
-
headers: this.headers,
|
|
1509
|
-
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1510
|
-
});
|
|
1511
|
-
if (error) {
|
|
1512
|
-
return { data: null, error };
|
|
1513
|
-
}
|
|
1514
|
-
if ((_b = data === null || data === void 0 ? void 0 : data.totp) === null || _b === void 0 ? void 0 : _b.qr_code) {
|
|
1515
|
-
data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`;
|
|
1516
|
-
}
|
|
1517
|
-
return { data, error: null };
|
|
1415
|
+
try {
|
|
1416
|
+
const { data: sessionData, error: sessionError } = await this.getSession();
|
|
1417
|
+
if (sessionError) {
|
|
1418
|
+
return { data: null, error: sessionError };
|
|
1518
1419
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1420
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors`, {
|
|
1421
|
+
body: {
|
|
1422
|
+
friendly_name: params.friendlyName,
|
|
1423
|
+
factor_type: params.factorType,
|
|
1424
|
+
issuer: params.issuer,
|
|
1425
|
+
},
|
|
1426
|
+
headers: this.headers,
|
|
1427
|
+
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1428
|
+
});
|
|
1429
|
+
if (error) {
|
|
1430
|
+
return { data: null, error };
|
|
1524
1431
|
}
|
|
1525
|
-
|
|
1432
|
+
if ((_b = data === null || data === void 0 ? void 0 : data.totp) === null || _b === void 0 ? void 0 : _b.qr_code) {
|
|
1433
|
+
data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`;
|
|
1434
|
+
}
|
|
1435
|
+
return { data, error: null };
|
|
1436
|
+
}
|
|
1437
|
+
catch (error) {
|
|
1438
|
+
if (isAuthError(error)) {
|
|
1439
|
+
return { data: null, error };
|
|
1440
|
+
}
|
|
1441
|
+
throw error;
|
|
1442
|
+
}
|
|
1526
1443
|
}
|
|
1527
1444
|
/**
|
|
1528
1445
|
* {@see GoTrueMFAApi#verify}
|
|
1529
1446
|
*/
|
|
1530
|
-
_verify(params) {
|
|
1447
|
+
async _verify(params) {
|
|
1531
1448
|
var _a;
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return { data: null, error: sessionError };
|
|
1537
|
-
}
|
|
1538
|
-
const { data, error } = yield _request(this.fetch, 'POST', `${this.url}/factors/${params.factorId}/verify`, {
|
|
1539
|
-
body: { code: params.code, challenge_id: params.challengeId },
|
|
1540
|
-
headers: this.headers,
|
|
1541
|
-
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1542
|
-
});
|
|
1543
|
-
if (error) {
|
|
1544
|
-
return { data: null, error };
|
|
1545
|
-
}
|
|
1546
|
-
yield this._saveSession(Object.assign({ expires_at: Math.round(Date.now() / 1000) + data.expires_in }, data));
|
|
1547
|
-
yield this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data);
|
|
1548
|
-
return { data, error };
|
|
1449
|
+
try {
|
|
1450
|
+
const { data: sessionData, error: sessionError } = await this.getSession();
|
|
1451
|
+
if (sessionError) {
|
|
1452
|
+
return { data: null, error: sessionError };
|
|
1549
1453
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1454
|
+
const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors/${params.factorId}/verify`, {
|
|
1455
|
+
body: { code: params.code, challenge_id: params.challengeId },
|
|
1456
|
+
headers: this.headers,
|
|
1457
|
+
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1458
|
+
});
|
|
1459
|
+
if (error) {
|
|
1460
|
+
return { data: null, error };
|
|
1555
1461
|
}
|
|
1556
|
-
|
|
1462
|
+
await this._saveSession(Object.assign({ expires_at: Math.round(Date.now() / 1000) + data.expires_in }, data));
|
|
1463
|
+
await this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data);
|
|
1464
|
+
return { data, error };
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
if (isAuthError(error)) {
|
|
1468
|
+
return { data: null, error };
|
|
1469
|
+
}
|
|
1470
|
+
throw error;
|
|
1471
|
+
}
|
|
1557
1472
|
}
|
|
1558
1473
|
/**
|
|
1559
1474
|
* {@see GoTrueMFAApi#challenge}
|
|
1560
1475
|
*/
|
|
1561
|
-
_challenge(params) {
|
|
1476
|
+
async _challenge(params) {
|
|
1562
1477
|
var _a;
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
return { data: null, error: sessionError };
|
|
1568
|
-
}
|
|
1569
|
-
return yield _request(this.fetch, 'POST', `${this.url}/factors/${params.factorId}/challenge`, {
|
|
1570
|
-
headers: this.headers,
|
|
1571
|
-
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1572
|
-
});
|
|
1478
|
+
try {
|
|
1479
|
+
const { data: sessionData, error: sessionError } = await this.getSession();
|
|
1480
|
+
if (sessionError) {
|
|
1481
|
+
return { data: null, error: sessionError };
|
|
1573
1482
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1483
|
+
return await _request(this.fetch, 'POST', `${this.url}/factors/${params.factorId}/challenge`, {
|
|
1484
|
+
headers: this.headers,
|
|
1485
|
+
jwt: (_a = sessionData === null || sessionData === void 0 ? void 0 : sessionData.session) === null || _a === void 0 ? void 0 : _a.access_token,
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
if (isAuthError(error)) {
|
|
1490
|
+
return { data: null, error };
|
|
1579
1491
|
}
|
|
1580
|
-
|
|
1492
|
+
throw error;
|
|
1493
|
+
}
|
|
1581
1494
|
}
|
|
1582
1495
|
/**
|
|
1583
1496
|
* {@see GoTrueMFAApi#challengeAndVerify}
|
|
1584
1497
|
*/
|
|
1585
|
-
_challengeAndVerify(params) {
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
code: params.code,
|
|
1597
|
-
});
|
|
1498
|
+
async _challengeAndVerify(params) {
|
|
1499
|
+
const { data: challengeData, error: challengeError } = await this._challenge({
|
|
1500
|
+
factorId: params.factorId,
|
|
1501
|
+
});
|
|
1502
|
+
if (challengeError) {
|
|
1503
|
+
return { data: null, error: challengeError };
|
|
1504
|
+
}
|
|
1505
|
+
return await this._verify({
|
|
1506
|
+
factorId: params.factorId,
|
|
1507
|
+
challengeId: challengeData.id,
|
|
1508
|
+
code: params.code,
|
|
1598
1509
|
});
|
|
1599
1510
|
}
|
|
1600
1511
|
/**
|
|
1601
1512
|
* {@see GoTrueMFAApi#listFactors}
|
|
1602
1513
|
*/
|
|
1603
|
-
_listFactors() {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
};
|
|
1618
|
-
});
|
|
1514
|
+
async _listFactors() {
|
|
1515
|
+
const { data: { user }, error: userError, } = await this.getUser();
|
|
1516
|
+
if (userError) {
|
|
1517
|
+
return { data: null, error: userError };
|
|
1518
|
+
}
|
|
1519
|
+
const factors = (user === null || user === void 0 ? void 0 : user.factors) || [];
|
|
1520
|
+
const totp = factors.filter((factor) => factor.factor_type === 'totp' && factor.status === 'verified');
|
|
1521
|
+
return {
|
|
1522
|
+
data: {
|
|
1523
|
+
all: factors,
|
|
1524
|
+
totp,
|
|
1525
|
+
},
|
|
1526
|
+
error: null,
|
|
1527
|
+
};
|
|
1619
1528
|
}
|
|
1620
1529
|
/**
|
|
1621
1530
|
* {@see GoTrueMFAApi#getAuthenticatorAssuranceLevel}
|
|
1622
1531
|
*/
|
|
1623
|
-
_getAuthenticatorAssuranceLevel() {
|
|
1532
|
+
async _getAuthenticatorAssuranceLevel() {
|
|
1624
1533
|
var _a, _b;
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null };
|
|
1648
|
-
});
|
|
1534
|
+
const { data: { session }, error: sessionError, } = await this.getSession();
|
|
1535
|
+
if (sessionError) {
|
|
1536
|
+
return { data: null, error: sessionError };
|
|
1537
|
+
}
|
|
1538
|
+
if (!session) {
|
|
1539
|
+
return {
|
|
1540
|
+
data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
|
|
1541
|
+
error: null,
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
const payload = this._decodeJWT(session.access_token);
|
|
1545
|
+
let currentLevel = null;
|
|
1546
|
+
if (payload.aal) {
|
|
1547
|
+
currentLevel = payload.aal;
|
|
1548
|
+
}
|
|
1549
|
+
let nextLevel = currentLevel;
|
|
1550
|
+
const verifiedFactors = (_b = (_a = session.user.factors) === null || _a === void 0 ? void 0 : _a.filter((factor) => factor.status === 'verified')) !== null && _b !== void 0 ? _b : [];
|
|
1551
|
+
if (verifiedFactors.length > 0) {
|
|
1552
|
+
nextLevel = 'aal2';
|
|
1553
|
+
}
|
|
1554
|
+
const currentAuthenticationMethods = payload.amr || [];
|
|
1555
|
+
return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null };
|
|
1649
1556
|
}
|
|
1650
1557
|
}
|
|
1651
1558
|
GoTrueClient.nextInstanceID = 0;
|