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