@faable/auth-js 1.2.2 → 1.3.1
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/README.md +26 -1
- package/dist/Base.d.ts +0 -1
- package/dist/BaseLog.d.ts +0 -1
- package/dist/FaableAuthApi.d.ts +0 -1
- package/dist/FaableAuthClient.d.ts +5 -1
- package/dist/entrypoints/faableauth.d.ts +6 -0
- package/dist/faableauth.js +7 -0
- package/dist/faableauth.js.map +1 -0
- package/dist/lib/constants.d.ts +0 -1
- package/dist/lib/errors.d.ts +0 -1
- package/dist/lib/fetch.d.ts +8 -5
- package/dist/lib/globals.d.ts +4 -0
- package/dist/lib/helpers.d.ts +18 -8
- package/dist/lib/jwt.d.ts +0 -1
- package/dist/lib/local-storage.d.ts +0 -1
- package/dist/lib/storage_helpers.d.ts +0 -1
- package/dist/lib/types.d.ts +0 -1
- package/dist/lib/url_helpers.d.ts +7 -0
- package/dist/lib/version.d.ts +0 -1
- package/dist/lock/Lock.d.ts +0 -1
- package/dist/lock/locks.d.ts +0 -1
- package/dist/utils.d.ts +0 -1
- package/package.json +5 -24
- package/dist/Base.d.ts.map +0 -1
- package/dist/Base.js +0 -16
- package/dist/BaseLog.d.ts.map +0 -1
- package/dist/BaseLog.js +0 -21
- package/dist/FaableAuthApi.d.ts.map +0 -1
- package/dist/FaableAuthApi.js +0 -37
- package/dist/FaableAuthClient.d.ts.map +0 -1
- package/dist/FaableAuthClient.js +0 -1073
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -13
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/constants.js +0 -13
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js +0 -100
- package/dist/lib/fetch.d.ts.map +0 -1
- package/dist/lib/fetch.js +0 -53
- package/dist/lib/helpers.d.ts.map +0 -1
- package/dist/lib/helpers.js +0 -257
- package/dist/lib/jwt.d.ts.map +0 -1
- package/dist/lib/jwt.js +0 -139
- package/dist/lib/local-storage.d.ts.map +0 -1
- package/dist/lib/local-storage.js +0 -45
- package/dist/lib/storage_helpers.d.ts.map +0 -1
- package/dist/lib/storage_helpers.js +0 -34
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js +0 -6
- package/dist/lib/version.d.ts.map +0 -1
- package/dist/lib/version.js +0 -5
- package/dist/lock/Lock.d.ts.map +0 -1
- package/dist/lock/Lock.js +0 -82
- package/dist/lock/locks.d.ts.map +0 -1
- package/dist/lock/locks.js +0 -137
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -88
package/dist/FaableAuthClient.js
DELETED
|
@@ -1,1073 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.FaableAuthClient = void 0;
|
|
16
|
-
const utils_1 = require("./utils");
|
|
17
|
-
const jwt_1 = require("./lib/jwt");
|
|
18
|
-
const helpers_1 = require("./lib/helpers");
|
|
19
|
-
const constants_1 = require("./lib/constants");
|
|
20
|
-
const local_storage_1 = require("./lib/local-storage");
|
|
21
|
-
const errors_1 = require("./lib/errors");
|
|
22
|
-
const storage_helpers_1 = require("./lib/storage_helpers");
|
|
23
|
-
const FaableAuthApi_1 = __importDefault(require("./FaableAuthApi"));
|
|
24
|
-
const locks_1 = require("./lock/locks");
|
|
25
|
-
const fetch_1 = require("./lib/fetch");
|
|
26
|
-
const Base_1 = require("./Base");
|
|
27
|
-
const Lock_1 = require("./lock/Lock");
|
|
28
|
-
/** Current session will be checked for refresh at this interval. */
|
|
29
|
-
const AUTO_REFRESH_TICK_DURATION = 30 * 1000;
|
|
30
|
-
/**
|
|
31
|
-
* A token refresh will be attempted this many ticks before the current session expires. */
|
|
32
|
-
const AUTO_REFRESH_TICK_THRESHOLD = 3;
|
|
33
|
-
class FaableAuthClient extends Base_1.Base {
|
|
34
|
-
constructor(config) {
|
|
35
|
-
var _a;
|
|
36
|
-
super({ debug: config.debug });
|
|
37
|
-
this.initializePromise = null;
|
|
38
|
-
this.detectSessionInUrl = true;
|
|
39
|
-
this.autoRefreshTicker = null;
|
|
40
|
-
this.visibilityChangedCallback = null;
|
|
41
|
-
this.refreshingDeferred = null;
|
|
42
|
-
/**
|
|
43
|
-
* Used to broadcast state change events to other tabs listening.
|
|
44
|
-
*/
|
|
45
|
-
this.broadcastChannel = null;
|
|
46
|
-
this.stateChangeEmitters = new Map();
|
|
47
|
-
this.sessionCheckExpiryDays = 1;
|
|
48
|
-
this.redirect_uri = config.redirect_uri || "";
|
|
49
|
-
this.domainUrl = (0, utils_1.getDomain)(config.domain);
|
|
50
|
-
this.tokenIssuer = (0, utils_1.getTokenIssuer)("", this.domainUrl);
|
|
51
|
-
this.clientId = config.clientId;
|
|
52
|
-
this.api = new FaableAuthApi_1.default(this.domainUrl, { debug: config.debug });
|
|
53
|
-
this.storageKey = config.storageKey || constants_1.STORAGE_KEY;
|
|
54
|
-
this.flowType = config.flowType || "implicit";
|
|
55
|
-
this.storage = config.storage || local_storage_1.localStorageAdapter;
|
|
56
|
-
this.lock = new Lock_1.Lock({
|
|
57
|
-
lock: config.lock,
|
|
58
|
-
storageKey: this.storageKey,
|
|
59
|
-
debug: config.debug,
|
|
60
|
-
});
|
|
61
|
-
if ((0, helpers_1.isBrowser)() &&
|
|
62
|
-
globalThis.BroadcastChannel &&
|
|
63
|
-
// this.persistSession &&
|
|
64
|
-
this.storageKey) {
|
|
65
|
-
try {
|
|
66
|
-
this.broadcastChannel = new globalThis.BroadcastChannel(this.storageKey);
|
|
67
|
-
}
|
|
68
|
-
catch (e) {
|
|
69
|
-
console.error("Failed to create a new BroadcastChannel, multi-tab state changes will not be available", e);
|
|
70
|
-
}
|
|
71
|
-
(_a = this.broadcastChannel) === null || _a === void 0 ? void 0 : _a.addEventListener("message", (event) => __awaiter(this, void 0, void 0, function* () {
|
|
72
|
-
this._debug("received broadcast notification from other tab or client", event);
|
|
73
|
-
yield this._notifyAllSubscribers(event.data.event, event.data.session, false); // broadcast = false so we don't get an endless loop of messages
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
this.autoRefreshToken = true;
|
|
77
|
-
this.initialize();
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Initializes the client session either from the url or from storage.
|
|
81
|
-
* This method is automatically called when instantiating the client, but should also be called
|
|
82
|
-
* manually when checking for an error from an auth redirect (oauth, magiclink, password recovery, etc).
|
|
83
|
-
*/
|
|
84
|
-
initialize() {
|
|
85
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
-
if (this.initializePromise) {
|
|
87
|
-
return yield this.initializePromise;
|
|
88
|
-
}
|
|
89
|
-
this.initializePromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
return yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
-
return yield this._initialize();
|
|
92
|
-
}));
|
|
93
|
-
}))();
|
|
94
|
-
return yield this.initializePromise;
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* IMPORTANT:
|
|
99
|
-
* 1. Never throw in this method, as it is called from the constructor
|
|
100
|
-
* 2. Never return a session from this method as it would be cached over
|
|
101
|
-
* the whole lifetime of the client
|
|
102
|
-
*/
|
|
103
|
-
_initialize() {
|
|
104
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
-
try {
|
|
106
|
-
const isPKCEFlow = (0, helpers_1.isBrowser)() ? yield this._isPKCEFlow() : false;
|
|
107
|
-
this._debug("#_initialize()", "begin", "is PKCE flow", isPKCEFlow);
|
|
108
|
-
if (isPKCEFlow ||
|
|
109
|
-
(this.detectSessionInUrl && this._isImplicitGrantFlow())) {
|
|
110
|
-
const { data, error } = yield this._getSessionFromURL(isPKCEFlow);
|
|
111
|
-
if (error) {
|
|
112
|
-
this._debug("#_initialize()", "error detecting session from URL", error);
|
|
113
|
-
// hacky workaround to keep the existing session if there's an error returned from identity linking
|
|
114
|
-
// TODO: once error codes are ready, we should match against it instead of the message
|
|
115
|
-
if ((error === null || error === void 0 ? void 0 : error.message) === "Identity is already linked" ||
|
|
116
|
-
(error === null || error === void 0 ? void 0 : error.message) === "Identity is already linked to another user") {
|
|
117
|
-
return { error };
|
|
118
|
-
}
|
|
119
|
-
// failed login attempt via url,
|
|
120
|
-
// remove old session as in verifyOtp, signUp and signInWith*
|
|
121
|
-
yield this._removeSession();
|
|
122
|
-
return { error };
|
|
123
|
-
}
|
|
124
|
-
const { session, redirectType } = data;
|
|
125
|
-
this._debug("#_initialize()", "detected session in URL", session, "redirect type", redirectType);
|
|
126
|
-
yield this._saveSession(session);
|
|
127
|
-
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
128
|
-
if (redirectType === "recovery") {
|
|
129
|
-
yield this._notifyAllSubscribers("PASSWORD_RECOVERY", session);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
yield this._notifyAllSubscribers("SIGNED_IN", session);
|
|
133
|
-
}
|
|
134
|
-
}), 0);
|
|
135
|
-
return { error: null };
|
|
136
|
-
}
|
|
137
|
-
// no login attempt via callback url try to recover session from storage
|
|
138
|
-
yield this._recoverAndRefresh();
|
|
139
|
-
return { error: null };
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
143
|
-
return { error };
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
error: new errors_1.AuthUnknownError("Unexpected error during initialization", error),
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
yield this._handleVisibilityChange();
|
|
151
|
-
this._debug("#_initialize()", "end");
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Gets the session data from a URL string
|
|
157
|
-
*/
|
|
158
|
-
_getSessionFromURL(isPKCEFlow) {
|
|
159
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
-
try {
|
|
161
|
-
if (!(0, helpers_1.isBrowser)())
|
|
162
|
-
throw new errors_1.AuthImplicitGrantRedirectError("No browser detected.");
|
|
163
|
-
if (this.flowType === "implicit" && !this._isImplicitGrantFlow()) {
|
|
164
|
-
throw new errors_1.AuthImplicitGrantRedirectError("Not a valid implicit grant flow url.");
|
|
165
|
-
}
|
|
166
|
-
else if (this.flowType == "pkce" && !isPKCEFlow) {
|
|
167
|
-
throw new errors_1.AuthPKCEGrantCodeExchangeError("Not a valid PKCE flow url.");
|
|
168
|
-
}
|
|
169
|
-
const params = (0, helpers_1.parseParametersFromURL)(window.location.href);
|
|
170
|
-
if (isPKCEFlow) {
|
|
171
|
-
if (!params.code)
|
|
172
|
-
throw new errors_1.AuthPKCEGrantCodeExchangeError("No code detected.");
|
|
173
|
-
const { data, error } = yield this._exchangeCodeForSession(params.code);
|
|
174
|
-
if (error)
|
|
175
|
-
throw error;
|
|
176
|
-
const url = new URL(window.location.href);
|
|
177
|
-
url.searchParams.delete("code");
|
|
178
|
-
window.history.replaceState(window.history.state, "", url.toString());
|
|
179
|
-
return {
|
|
180
|
-
data: { session: data.session, redirectType: null },
|
|
181
|
-
error: null,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
if (params.error || params.error_description || params.error_code) {
|
|
185
|
-
throw new errors_1.AuthImplicitGrantRedirectError(params.error_description ||
|
|
186
|
-
"Error in URL with unspecified error_description", {
|
|
187
|
-
error: params.error || "unspecified_error",
|
|
188
|
-
code: params.error_code || "unspecified_code",
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
const { provider_token, provider_refresh_token, access_token, refresh_token, expires_in, expires_at, token_type, } = params;
|
|
192
|
-
if (!access_token || !expires_in || !refresh_token || !token_type) {
|
|
193
|
-
throw new errors_1.AuthImplicitGrantRedirectError("No session defined in URL");
|
|
194
|
-
}
|
|
195
|
-
const timeNow = Math.round(Date.now() / 1000);
|
|
196
|
-
const expiresIn = parseInt(expires_in);
|
|
197
|
-
let expiresAt = timeNow + expiresIn;
|
|
198
|
-
if (expires_at) {
|
|
199
|
-
expiresAt = parseInt(expires_at);
|
|
200
|
-
}
|
|
201
|
-
const actuallyExpiresIn = expiresAt - timeNow;
|
|
202
|
-
if (actuallyExpiresIn * 1000 <= AUTO_REFRESH_TICK_DURATION) {
|
|
203
|
-
console.warn(`@supabase/gotrue-js: Session as retrieved from URL expires in ${actuallyExpiresIn}s, should have been closer to ${expiresIn}s`);
|
|
204
|
-
}
|
|
205
|
-
const issuedAt = expiresAt - expiresIn;
|
|
206
|
-
if (timeNow - issuedAt >= 120) {
|
|
207
|
-
console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued over 120s ago, URL could be stale", issuedAt, expiresAt, timeNow);
|
|
208
|
-
}
|
|
209
|
-
else if (timeNow - issuedAt < 0) {
|
|
210
|
-
console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued in the future? Check the device clok for skew", issuedAt, expiresAt, timeNow);
|
|
211
|
-
}
|
|
212
|
-
const { data, error } = yield this._getUser(access_token);
|
|
213
|
-
if (error)
|
|
214
|
-
throw error;
|
|
215
|
-
const session = {
|
|
216
|
-
provider_token,
|
|
217
|
-
provider_refresh_token,
|
|
218
|
-
access_token,
|
|
219
|
-
expires_in: expiresIn,
|
|
220
|
-
expires_at: expiresAt,
|
|
221
|
-
refresh_token,
|
|
222
|
-
token_type,
|
|
223
|
-
user: data.user,
|
|
224
|
-
};
|
|
225
|
-
// Remove tokens from URL
|
|
226
|
-
window.location.hash = "";
|
|
227
|
-
this._debug("#_getSessionFromURL()", "clearing window.location.hash");
|
|
228
|
-
return { data: { session, redirectType: params.type }, error: null };
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
this._debug(error);
|
|
232
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
233
|
-
return { data: { session: null, redirectType: null }, error };
|
|
234
|
-
}
|
|
235
|
-
throw error;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
_exchangeCodeForSession(authCode) {
|
|
240
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
-
const storageItem = yield (0, storage_helpers_1.getItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
|
|
242
|
-
const [codeVerifier, redirectType] = (storageItem !== null && storageItem !== void 0 ? storageItem : "").split("/");
|
|
243
|
-
const { data, error } = yield (0, fetch_1._post)(`${this.domainUrl}/oauth/token`, {
|
|
244
|
-
client_id: this.clientId,
|
|
245
|
-
grant_type: "authorization_code",
|
|
246
|
-
code: authCode,
|
|
247
|
-
code_verifier: codeVerifier,
|
|
248
|
-
}, { transform: helpers_1._sessionResponse });
|
|
249
|
-
yield (0, storage_helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
|
|
250
|
-
if (error) {
|
|
251
|
-
return { data: { user: null, session: null, redirectType: null }, error };
|
|
252
|
-
}
|
|
253
|
-
else if (!data || !data.session || !data.user) {
|
|
254
|
-
return {
|
|
255
|
-
data: { user: null, session: null, redirectType: null },
|
|
256
|
-
error: new errors_1.AuthInvalidTokenResponseError(),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
let session = data.session;
|
|
260
|
-
if (session) {
|
|
261
|
-
const { data: userdata, error } = yield this._getUser(session.access_token);
|
|
262
|
-
if (error) {
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
session = Object.assign(Object.assign({}, session), { user: userdata.user });
|
|
266
|
-
data.session = session;
|
|
267
|
-
yield this._saveSession(session);
|
|
268
|
-
yield this._notifyAllSubscribers("SIGNED_IN", session);
|
|
269
|
-
}
|
|
270
|
-
return {
|
|
271
|
-
data: Object.assign(Object.assign({}, data), { redirectType: redirectType !== null && redirectType !== void 0 ? redirectType : null }),
|
|
272
|
-
error,
|
|
273
|
-
};
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Registers callbacks on the browser / platform, which in-turn run
|
|
278
|
-
* algorithms when the browser window/tab are in foreground. On non-browser
|
|
279
|
-
* platforms it assumes always foreground.
|
|
280
|
-
*/
|
|
281
|
-
_handleVisibilityChange() {
|
|
282
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
283
|
-
this._debug("#_handleVisibilityChange()");
|
|
284
|
-
if (!(0, helpers_1.isBrowser)() || !(window === null || window === void 0 ? void 0 : window.addEventListener)) {
|
|
285
|
-
if (this.autoRefreshToken) {
|
|
286
|
-
// in non-browser environments the refresh token ticker runs always
|
|
287
|
-
this.startAutoRefresh();
|
|
288
|
-
}
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
try {
|
|
292
|
-
this.visibilityChangedCallback = () => __awaiter(this, void 0, void 0, function* () { return yield this._onVisibilityChanged(false); });
|
|
293
|
-
window === null || window === void 0 ? void 0 : window.addEventListener("visibilitychange", this.visibilityChangedCallback);
|
|
294
|
-
// now immediately call the visbility changed callback to setup with the
|
|
295
|
-
// current visbility state
|
|
296
|
-
yield this._onVisibilityChanged(true); // initial call
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
console.error("_handleVisibilityChange", error);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Callback registered with `window.addEventListener('visibilitychange')`.
|
|
305
|
-
*/
|
|
306
|
-
_onVisibilityChanged(calledFromInitialize) {
|
|
307
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
-
const methodName = `#_onVisibilityChanged(${calledFromInitialize})`;
|
|
309
|
-
this._debug(methodName, "visibilityState", document.visibilityState);
|
|
310
|
-
if (document.visibilityState === "visible") {
|
|
311
|
-
if (this.autoRefreshToken) {
|
|
312
|
-
// in browser environments the refresh token ticker runs only on focused tabs
|
|
313
|
-
// which prevents race conditions
|
|
314
|
-
this._startAutoRefresh();
|
|
315
|
-
}
|
|
316
|
-
if (!calledFromInitialize) {
|
|
317
|
-
// called when the visibility has changed, i.e. the browser
|
|
318
|
-
// transitioned from hidden -> visible so we need to see if the session
|
|
319
|
-
// should be recovered immediately... but to do that we need to acquire
|
|
320
|
-
// the lock first asynchronously
|
|
321
|
-
yield this.initializePromise;
|
|
322
|
-
yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
323
|
-
if (document.visibilityState !== "visible") {
|
|
324
|
-
this._debug(methodName, "acquired the lock to recover the session, but the browser visibilityState is no longer visible, aborting");
|
|
325
|
-
// visibility has changed while waiting for the lock, abort
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
// recover the session
|
|
329
|
-
yield this._recoverAndRefresh();
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
else if (document.visibilityState === "hidden") {
|
|
334
|
-
if (this.autoRefreshToken) {
|
|
335
|
-
this._stopAutoRefresh();
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Recovers the session from LocalStorage and refreshes
|
|
342
|
-
* Note: this method is async to accommodate for AsyncStorage e.g. in React native.
|
|
343
|
-
*/
|
|
344
|
-
_recoverAndRefresh() {
|
|
345
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
346
|
-
var _a;
|
|
347
|
-
const debugName = "#_recoverAndRefresh()";
|
|
348
|
-
this._debug(debugName, "begin");
|
|
349
|
-
try {
|
|
350
|
-
const currentSession = yield (0, storage_helpers_1.getItemAsync)(this.storage, this.storageKey);
|
|
351
|
-
this._debug(debugName, "session from storage", currentSession);
|
|
352
|
-
if (!this._isValidSession(currentSession)) {
|
|
353
|
-
this._debug(debugName, "session is not valid");
|
|
354
|
-
if (currentSession !== null) {
|
|
355
|
-
yield this._removeSession();
|
|
356
|
-
}
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
const timeNow = Math.round(Date.now() / 1000);
|
|
360
|
-
const expiresWithMargin = ((_a = currentSession.expires_at) !== null && _a !== void 0 ? _a : Infinity) < timeNow + constants_1.EXPIRY_MARGIN;
|
|
361
|
-
this._debug(debugName, `session has${expiresWithMargin ? "" : " not"} expired with margin of ${constants_1.EXPIRY_MARGIN}s`);
|
|
362
|
-
if (expiresWithMargin) {
|
|
363
|
-
if (this.autoRefreshToken && currentSession.refresh_token) {
|
|
364
|
-
const { error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
365
|
-
if (error) {
|
|
366
|
-
console.error(error);
|
|
367
|
-
if (!(0, errors_1.isAuthRetryableFetchError)(error)) {
|
|
368
|
-
this._debug(debugName, "refresh failed with a non-retryable error, removing the session", error);
|
|
369
|
-
yield this._removeSession();
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
// no need to persist currentSession again, as we just loaded it from
|
|
376
|
-
// local storage; persisting it again may overwrite a value saved by
|
|
377
|
-
// another client with access to the same local storage
|
|
378
|
-
yield this._notifyAllSubscribers("SIGNED_IN", currentSession);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
catch (err) {
|
|
382
|
-
this._debug(debugName, "error", err);
|
|
383
|
-
console.error(err);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
finally {
|
|
387
|
-
this._debug(debugName, "end");
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* Removes any registered visibilitychange callback.
|
|
393
|
-
*
|
|
394
|
-
* {@see #startAutoRefresh}
|
|
395
|
-
* {@see #stopAutoRefresh}
|
|
396
|
-
*/
|
|
397
|
-
_removeVisibilityChangedCallback() {
|
|
398
|
-
this._debug("#_removeVisibilityChangedCallback()");
|
|
399
|
-
const callback = this.visibilityChangedCallback;
|
|
400
|
-
this.visibilityChangedCallback = null;
|
|
401
|
-
try {
|
|
402
|
-
if (callback && (0, helpers_1.isBrowser)() && (window === null || window === void 0 ? void 0 : window.removeEventListener)) {
|
|
403
|
-
window.removeEventListener("visibilitychange", callback);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
catch (e) {
|
|
407
|
-
console.error("removing visibilitychange callback failed", e);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Starts an auto-refresh process in the background. The session is checked
|
|
412
|
-
* every few seconds. Close to the time of expiration a process is started to
|
|
413
|
-
* refresh the session. If refreshing fails it will be retried for as long as
|
|
414
|
-
* necessary.
|
|
415
|
-
*
|
|
416
|
-
* If you set the {@link GoTrueClientOptions#autoRefreshToken} you don't need
|
|
417
|
-
* to call this function, it will be called for you.
|
|
418
|
-
*
|
|
419
|
-
* On browsers the refresh process works only when the tab/window is in the
|
|
420
|
-
* foreground to conserve resources as well as prevent race conditions and
|
|
421
|
-
* flooding auth with requests. If you call this method any managed
|
|
422
|
-
* visibility change callback will be removed and you must manage visibility
|
|
423
|
-
* changes on your own.
|
|
424
|
-
*
|
|
425
|
-
* On non-browser platforms the refresh process works *continuously* in the
|
|
426
|
-
* background, which may not be desirable. You should hook into your
|
|
427
|
-
* platform's foreground indication mechanism and call these methods
|
|
428
|
-
* appropriately to conserve resources.
|
|
429
|
-
*
|
|
430
|
-
* {@see #stopAutoRefresh}
|
|
431
|
-
*/
|
|
432
|
-
startAutoRefresh() {
|
|
433
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
434
|
-
this._removeVisibilityChangedCallback();
|
|
435
|
-
yield this._startAutoRefresh();
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* This is the private implementation of {@link #startAutoRefresh}. Use this
|
|
440
|
-
* within the library.
|
|
441
|
-
*/
|
|
442
|
-
_startAutoRefresh() {
|
|
443
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
444
|
-
yield this._stopAutoRefresh();
|
|
445
|
-
this._debug("#_startAutoRefresh()");
|
|
446
|
-
const ticker = setInterval(() => this._autoRefreshTokenTick(), AUTO_REFRESH_TICK_DURATION);
|
|
447
|
-
this.autoRefreshTicker = ticker;
|
|
448
|
-
if (ticker &&
|
|
449
|
-
typeof ticker === "object" &&
|
|
450
|
-
typeof ticker.unref === "function") {
|
|
451
|
-
// ticker is a NodeJS Timeout object that has an `unref` method
|
|
452
|
-
// https://nodejs.org/api/timers.html#timeoutunref
|
|
453
|
-
// When auto refresh is used in NodeJS (like for testing) the
|
|
454
|
-
// `setInterval` is preventing the process from being marked as
|
|
455
|
-
// finished and tests run endlessly. This can be prevented by calling
|
|
456
|
-
// `unref()` on the returned object.
|
|
457
|
-
ticker.unref();
|
|
458
|
-
// @ts-ignore
|
|
459
|
-
}
|
|
460
|
-
else if (typeof globalThis.Deno !== "undefined" &&
|
|
461
|
-
typeof globalThis.Deno.unrefTimer === "function") {
|
|
462
|
-
// similar like for NodeJS, but with the Deno API
|
|
463
|
-
// https://deno.land/api@latest?unstable&s=Deno.unrefTimer
|
|
464
|
-
// @ts-ignore
|
|
465
|
-
Deno.unrefTimer(ticker);
|
|
466
|
-
}
|
|
467
|
-
// run the tick immediately, but in the next pass of the event loop so that
|
|
468
|
-
// #_initialize can be allowed to complete without recursively waiting on
|
|
469
|
-
// itself
|
|
470
|
-
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
471
|
-
yield this.initializePromise;
|
|
472
|
-
yield this._autoRefreshTokenTick();
|
|
473
|
-
}), 0);
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* This is the private implementation of {@link #stopAutoRefresh}. Use this
|
|
478
|
-
* within the library.
|
|
479
|
-
*/
|
|
480
|
-
_stopAutoRefresh() {
|
|
481
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
482
|
-
this._debug("#_stopAutoRefresh()");
|
|
483
|
-
const ticker = this.autoRefreshTicker;
|
|
484
|
-
this.autoRefreshTicker = null;
|
|
485
|
-
if (ticker) {
|
|
486
|
-
clearInterval(ticker);
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Runs the auto refresh token tick.
|
|
492
|
-
*/
|
|
493
|
-
_autoRefreshTokenTick() {
|
|
494
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
495
|
-
this._debug("#_autoRefreshTokenTick()", "begin");
|
|
496
|
-
try {
|
|
497
|
-
yield this.lock._acquireLock(0, () => __awaiter(this, void 0, void 0, function* () {
|
|
498
|
-
try {
|
|
499
|
-
const now = Date.now();
|
|
500
|
-
try {
|
|
501
|
-
return yield this._useSession((result) => __awaiter(this, void 0, void 0, function* () {
|
|
502
|
-
const { data: { session }, } = result;
|
|
503
|
-
if (!session || !session.refresh_token || !session.expires_at) {
|
|
504
|
-
this._debug("#_autoRefreshTokenTick()", "no session");
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
// session will expire in this many ticks (or has already expired if <= 0)
|
|
508
|
-
const expiresInTicks = Math.floor((session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION);
|
|
509
|
-
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`);
|
|
510
|
-
if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
|
|
511
|
-
yield this._callRefreshToken(session.refresh_token);
|
|
512
|
-
}
|
|
513
|
-
}));
|
|
514
|
-
}
|
|
515
|
-
catch (e) {
|
|
516
|
-
console.error("Auto refresh tick failed with error. This is likely a transient error.", e);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
finally {
|
|
520
|
-
this._debug("#_autoRefreshTokenTick()", "end");
|
|
521
|
-
}
|
|
522
|
-
}));
|
|
523
|
-
}
|
|
524
|
-
catch (e) {
|
|
525
|
-
if (e.isAcquireTimeout || e instanceof locks_1.LockAcquireTimeoutError) {
|
|
526
|
-
this._debug("auto refresh token tick lock not available");
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
throw e;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Checks if the current URL and backing storage contain parameters given by a PKCE flow
|
|
536
|
-
*/
|
|
537
|
-
_isPKCEFlow() {
|
|
538
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
539
|
-
const params = (0, helpers_1.parseParametersFromURL)(window.location.href);
|
|
540
|
-
const currentStorageContent = yield (0, storage_helpers_1.getItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
|
|
541
|
-
return !!(params.code && currentStorageContent);
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* 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)
|
|
546
|
-
*/
|
|
547
|
-
_isImplicitGrantFlow() {
|
|
548
|
-
const params = (0, helpers_1.parseParametersFromURL)(window.location.href);
|
|
549
|
-
return !!((0, helpers_1.isBrowser)() && (params.access_token || params.error_description));
|
|
550
|
-
}
|
|
551
|
-
_scope() {
|
|
552
|
-
return this.scope || "openid profile email";
|
|
553
|
-
}
|
|
554
|
-
_getUrlForConnection(url, params) {
|
|
555
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
556
|
-
let urlParams = params.queryParams || {};
|
|
557
|
-
const authorize_params = {
|
|
558
|
-
client_id: this.clientId,
|
|
559
|
-
response_type: params.response_type,
|
|
560
|
-
redirect_uri: params.redirectTo || this.redirect_uri || window.location.origin,
|
|
561
|
-
scope: params.scopes || this._scope(),
|
|
562
|
-
};
|
|
563
|
-
if (this.flowType === "pkce") {
|
|
564
|
-
const [codeChallenge, codeChallengeMethod] = yield (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
|
|
565
|
-
urlParams = Object.assign(Object.assign({}, urlParams), { code_challenge: codeChallenge, code_challenge_method: codeChallengeMethod });
|
|
566
|
-
}
|
|
567
|
-
// Set connection if specified
|
|
568
|
-
if (params.connection) {
|
|
569
|
-
authorize_params.connection = params.connection;
|
|
570
|
-
}
|
|
571
|
-
// Merge authorize params with urlParams
|
|
572
|
-
return `${url}?${new URLSearchParams(Object.assign(Object.assign({}, urlParams), authorize_params))}`;
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
signInWithOauthConnection(credentials) {
|
|
576
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
577
|
-
return yield this._handleConnectionSignIn({
|
|
578
|
-
connection: credentials.connection,
|
|
579
|
-
redirectTo: credentials === null || credentials === void 0 ? void 0 : credentials.redirectTo,
|
|
580
|
-
scopes: credentials === null || credentials === void 0 ? void 0 : credentials.scopes,
|
|
581
|
-
queryParams: credentials.queryParams,
|
|
582
|
-
skipBrowserRedirect: credentials.skipBrowserRedirect,
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
_handleConnectionSignIn(options) {
|
|
587
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
588
|
-
const url = yield this._getUrlForConnection(`${this.domainUrl}/authorize`, {
|
|
589
|
-
response_type: (0, helpers_1.isBrowser)() ? "code" : "token",
|
|
590
|
-
connection: options.connection,
|
|
591
|
-
redirectTo: options.redirectTo,
|
|
592
|
-
scopes: options.scopes,
|
|
593
|
-
queryParams: options.queryParams,
|
|
594
|
-
});
|
|
595
|
-
this._debug("#_handleProviderSignIn()", "options", options, "url", url);
|
|
596
|
-
// try to open on the browser
|
|
597
|
-
if ((0, helpers_1.isBrowser)() && !options.skipBrowserRedirect) {
|
|
598
|
-
window.location.assign(url);
|
|
599
|
-
}
|
|
600
|
-
return { data: { url }, error: null };
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Sets the session data from the current session. If the current session is expired, setSession will take care of refreshing it to obtain a new session.
|
|
605
|
-
* If the refresh token or access token in the current session is invalid, an error will be thrown.
|
|
606
|
-
* @param currentSession The current session that minimally contains an access token and refresh token.
|
|
607
|
-
*/
|
|
608
|
-
setSession(currentSession) {
|
|
609
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
610
|
-
yield this.initializePromise;
|
|
611
|
-
return yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
612
|
-
return yield this._setSession(currentSession);
|
|
613
|
-
}));
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Returns the session, refreshing it if necessary.
|
|
618
|
-
*
|
|
619
|
-
* 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.
|
|
620
|
-
*
|
|
621
|
-
* **IMPORTANT:** This method loads values directly from the storage attached
|
|
622
|
-
* to the client. If that storage is based on request cookies for example,
|
|
623
|
-
* the values in it may not be authentic and therefore it's strongly advised
|
|
624
|
-
* against using this method and its results in such circumstances. A warning
|
|
625
|
-
* will be emitted if this is detected. Use {@link #getUser()} instead.
|
|
626
|
-
*/
|
|
627
|
-
getSession() {
|
|
628
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
629
|
-
yield this.initializePromise;
|
|
630
|
-
const result = yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
631
|
-
return this._useSession((result) => __awaiter(this, void 0, void 0, function* () {
|
|
632
|
-
return result;
|
|
633
|
-
}));
|
|
634
|
-
}));
|
|
635
|
-
return result;
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Use instead of {@link #getSession} inside the library. It is
|
|
640
|
-
* semantically usually what you want, as getting a session involves some
|
|
641
|
-
* processing afterwards that requires only one client operating on the
|
|
642
|
-
* session at once across multiple tabs or processes.
|
|
643
|
-
*/
|
|
644
|
-
_useSession(fn) {
|
|
645
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
646
|
-
this._debug("#_useSession", "begin");
|
|
647
|
-
try {
|
|
648
|
-
// the use of __loadSession here is the only correct use of the function!
|
|
649
|
-
const result = yield this.__loadSession();
|
|
650
|
-
return yield fn(result);
|
|
651
|
-
}
|
|
652
|
-
finally {
|
|
653
|
-
this._debug("#_useSession", "end");
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* NEVER USE DIRECTLY!
|
|
659
|
-
*
|
|
660
|
-
* Always use {@link #_useSession}.
|
|
661
|
-
*/
|
|
662
|
-
__loadSession() {
|
|
663
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
664
|
-
this._debug("#__loadSession()", "begin");
|
|
665
|
-
if (!this.lock.lockAcquired) {
|
|
666
|
-
this._debug("#__loadSession()", "used outside of an acquired lock!", new Error().stack);
|
|
667
|
-
}
|
|
668
|
-
try {
|
|
669
|
-
let currentSession = null;
|
|
670
|
-
const maybeSession = yield (0, storage_helpers_1.getItemAsync)(this.storage, this.storageKey);
|
|
671
|
-
this._debug("#getSession()", "session from storage", maybeSession);
|
|
672
|
-
if (maybeSession !== null) {
|
|
673
|
-
if (this._isValidSession(maybeSession)) {
|
|
674
|
-
currentSession = maybeSession;
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
this._debug("#getSession()", "session from storage is not valid");
|
|
678
|
-
yield this._removeSession();
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
if (!currentSession) {
|
|
682
|
-
return { data: { session: null }, error: null };
|
|
683
|
-
}
|
|
684
|
-
const hasExpired = currentSession.expires_at
|
|
685
|
-
? currentSession.expires_at <= Date.now() / 1000
|
|
686
|
-
: false;
|
|
687
|
-
this._debug("#__loadSession()", `session has${hasExpired ? "" : " not"} expired`, "expires_at", currentSession.expires_at);
|
|
688
|
-
if (!hasExpired) {
|
|
689
|
-
if (this.storage.isServer) {
|
|
690
|
-
const proxySession = new Proxy(currentSession, {
|
|
691
|
-
get(target, prop, receiver) {
|
|
692
|
-
if (prop === "user") {
|
|
693
|
-
// only show warning when the user object is being accessed from the server
|
|
694
|
-
console.warn("Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and many not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.");
|
|
695
|
-
}
|
|
696
|
-
return Reflect.get(target, prop, receiver);
|
|
697
|
-
},
|
|
698
|
-
});
|
|
699
|
-
currentSession = proxySession;
|
|
700
|
-
}
|
|
701
|
-
return { data: { session: currentSession }, error: null };
|
|
702
|
-
}
|
|
703
|
-
const { session, error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
704
|
-
if (error) {
|
|
705
|
-
return { data: { session: null }, error };
|
|
706
|
-
}
|
|
707
|
-
return { data: { session }, error: null };
|
|
708
|
-
}
|
|
709
|
-
finally {
|
|
710
|
-
this._debug("#__loadSession()", "end");
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
_removeSession() {
|
|
715
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
716
|
-
this._debug("#_removeSession()");
|
|
717
|
-
yield (0, storage_helpers_1.removeItemAsync)(this.storage, this.storageKey);
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
_isValidSession(maybeSession) {
|
|
721
|
-
const isValidSession = typeof maybeSession === "object" &&
|
|
722
|
-
maybeSession !== null &&
|
|
723
|
-
"access_token" in maybeSession &&
|
|
724
|
-
"refresh_token" in maybeSession &&
|
|
725
|
-
"expires_at" in maybeSession;
|
|
726
|
-
return isValidSession;
|
|
727
|
-
}
|
|
728
|
-
_setSession(currentSession) {
|
|
729
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
730
|
-
try {
|
|
731
|
-
if (!currentSession.access_token || !currentSession.refresh_token) {
|
|
732
|
-
throw new errors_1.AuthSessionMissingError();
|
|
733
|
-
}
|
|
734
|
-
const timeNow = Date.now() / 1000;
|
|
735
|
-
let expiresAt = timeNow;
|
|
736
|
-
let hasExpired = true;
|
|
737
|
-
let session = null;
|
|
738
|
-
const payload = (0, jwt_1.decodeJWTPayload)(currentSession.access_token);
|
|
739
|
-
if (payload.exp) {
|
|
740
|
-
expiresAt = payload.exp;
|
|
741
|
-
hasExpired = expiresAt <= timeNow;
|
|
742
|
-
}
|
|
743
|
-
if (hasExpired) {
|
|
744
|
-
const { session: refreshedSession, error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
745
|
-
if (error) {
|
|
746
|
-
return { data: { user: null, session: null }, error: error };
|
|
747
|
-
}
|
|
748
|
-
if (!refreshedSession) {
|
|
749
|
-
return { data: { user: null, session: null }, error: null };
|
|
750
|
-
}
|
|
751
|
-
session = refreshedSession;
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
const { data, error } = yield this._getUser(currentSession.access_token);
|
|
755
|
-
if (error) {
|
|
756
|
-
throw error;
|
|
757
|
-
}
|
|
758
|
-
session = {
|
|
759
|
-
access_token: currentSession.access_token,
|
|
760
|
-
refresh_token: currentSession.refresh_token,
|
|
761
|
-
user: data.user,
|
|
762
|
-
token_type: "bearer",
|
|
763
|
-
expires_in: expiresAt - timeNow,
|
|
764
|
-
expires_at: expiresAt,
|
|
765
|
-
};
|
|
766
|
-
yield this._saveSession(session);
|
|
767
|
-
yield this._notifyAllSubscribers("SIGNED_IN", session);
|
|
768
|
-
}
|
|
769
|
-
return { data: { user: session.user, session }, error: null };
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
772
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
773
|
-
return { data: { session: null, user: null }, error };
|
|
774
|
-
}
|
|
775
|
-
throw error;
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* set currentSession and currentUser
|
|
781
|
-
* process to _startAutoRefreshToken if possible
|
|
782
|
-
*/
|
|
783
|
-
_saveSession(session) {
|
|
784
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
785
|
-
this._debug("#_saveSession()", session);
|
|
786
|
-
yield (0, storage_helpers_1.setItemAsync)(this.storage, this.storageKey, session);
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
_getUser(access_token) {
|
|
790
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
791
|
-
if (!access_token)
|
|
792
|
-
throw new Error("Cannot fetch user without token");
|
|
793
|
-
this._debug("#_getUser() begin");
|
|
794
|
-
const res = yield (0, fetch_1._get)(`${this.domainUrl}/me`, {
|
|
795
|
-
token: access_token,
|
|
796
|
-
});
|
|
797
|
-
this._debug("#_getUser() end");
|
|
798
|
-
return { data: { user: res.data }, error: res.error };
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
_callRefreshToken(refreshToken) {
|
|
802
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
803
|
-
var _a, _b;
|
|
804
|
-
if (!refreshToken) {
|
|
805
|
-
throw new errors_1.AuthSessionMissingError();
|
|
806
|
-
}
|
|
807
|
-
// refreshing is already in progress
|
|
808
|
-
if (this.refreshingDeferred) {
|
|
809
|
-
return this.refreshingDeferred.promise;
|
|
810
|
-
}
|
|
811
|
-
const debugName = `#_callRefreshToken(${refreshToken.substring(0, 5)}...)`;
|
|
812
|
-
this._debug(debugName, "begin");
|
|
813
|
-
try {
|
|
814
|
-
this.refreshingDeferred = new helpers_1.Deferred();
|
|
815
|
-
const { data, error } = yield this._refreshAccessToken(refreshToken);
|
|
816
|
-
if (error)
|
|
817
|
-
throw error;
|
|
818
|
-
if (!data.session)
|
|
819
|
-
throw new errors_1.AuthSessionMissingError();
|
|
820
|
-
yield this._saveSession(data.session);
|
|
821
|
-
yield this._notifyAllSubscribers("TOKEN_REFRESHED", data.session);
|
|
822
|
-
const result = { session: data.session, error: null };
|
|
823
|
-
this.refreshingDeferred.resolve(result);
|
|
824
|
-
return result;
|
|
825
|
-
}
|
|
826
|
-
catch (error) {
|
|
827
|
-
this._debug(debugName, "error", error);
|
|
828
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
829
|
-
const result = { session: null, error };
|
|
830
|
-
if (!(0, errors_1.isAuthRetryableFetchError)(error)) {
|
|
831
|
-
yield this._removeSession();
|
|
832
|
-
yield this._notifyAllSubscribers("SIGNED_OUT", null);
|
|
833
|
-
}
|
|
834
|
-
(_a = this.refreshingDeferred) === null || _a === void 0 ? void 0 : _a.resolve(result);
|
|
835
|
-
return result;
|
|
836
|
-
}
|
|
837
|
-
(_b = this.refreshingDeferred) === null || _b === void 0 ? void 0 : _b.reject(error);
|
|
838
|
-
throw error;
|
|
839
|
-
}
|
|
840
|
-
finally {
|
|
841
|
-
this.refreshingDeferred = null;
|
|
842
|
-
this._debug(debugName, "end");
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Generates a new JWT.
|
|
848
|
-
* @param refreshToken A valid refresh token that was returned on login.
|
|
849
|
-
*/
|
|
850
|
-
_refreshAccessToken(refreshToken) {
|
|
851
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
852
|
-
const debugName = `#_refreshAccessToken(${refreshToken.substring(0, 5)}...)`;
|
|
853
|
-
this._debug(debugName, "begin");
|
|
854
|
-
try {
|
|
855
|
-
const startedAt = Date.now();
|
|
856
|
-
// will attempt to refresh the token with exponential backoff
|
|
857
|
-
return yield (0, helpers_1.retryable)((attempt) => __awaiter(this, void 0, void 0, function* () {
|
|
858
|
-
var _a;
|
|
859
|
-
if (attempt > 0) {
|
|
860
|
-
yield (0, helpers_1.sleep)(200 * Math.pow(2, attempt - 1)); // 200, 400, 800, ...
|
|
861
|
-
}
|
|
862
|
-
this._debug(debugName, "refreshing attempt", attempt);
|
|
863
|
-
// return await _post(`${this.url}/token?grant_type=refresh_token`, {
|
|
864
|
-
// body: { refresh_token: refreshToken },
|
|
865
|
-
// headers: this.headers,
|
|
866
|
-
// xform: _sessionResponse,
|
|
867
|
-
// });
|
|
868
|
-
const session_res = yield (0, fetch_1._post)(`${this.domainUrl}/oauth/token`, {
|
|
869
|
-
client_id: this.clientId,
|
|
870
|
-
grant_type: "refresh_token",
|
|
871
|
-
refresh_token: refreshToken,
|
|
872
|
-
}, { transform: helpers_1._sessionResponse });
|
|
873
|
-
const user_res = yield this._getUser((_a = session_res.data.session) === null || _a === void 0 ? void 0 : _a.access_token);
|
|
874
|
-
const { user } = user_res.data;
|
|
875
|
-
const x = {
|
|
876
|
-
data: {
|
|
877
|
-
session: Object.assign(Object.assign({}, session_res.data.session), { user }),
|
|
878
|
-
user,
|
|
879
|
-
},
|
|
880
|
-
error: null,
|
|
881
|
-
};
|
|
882
|
-
// this._debug(x);
|
|
883
|
-
return x;
|
|
884
|
-
}), (attempt, error) => {
|
|
885
|
-
const nextBackOffInterval = 200 * Math.pow(2, attempt);
|
|
886
|
-
return (error &&
|
|
887
|
-
(0, errors_1.isAuthRetryableFetchError)(error) &&
|
|
888
|
-
// retryable only if the request can be sent before the backoff overflows the tick duration
|
|
889
|
-
Date.now() + nextBackOffInterval - startedAt <
|
|
890
|
-
AUTO_REFRESH_TICK_DURATION);
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
catch (error) {
|
|
894
|
-
this._debug(debugName, "error", error);
|
|
895
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
896
|
-
return { data: { session: null, user: null }, error };
|
|
897
|
-
}
|
|
898
|
-
throw error;
|
|
899
|
-
}
|
|
900
|
-
finally {
|
|
901
|
-
this._debug(debugName, "end");
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
_notifyAllSubscribers(event_1, session_1) {
|
|
906
|
-
return __awaiter(this, arguments, void 0, function* (event, session, broadcast = true) {
|
|
907
|
-
const debugName = `#_notifyAllSubscribers(${event})`;
|
|
908
|
-
this._debug(debugName, "begin", session, `broadcast = ${broadcast}`);
|
|
909
|
-
try {
|
|
910
|
-
if (this.broadcastChannel && broadcast) {
|
|
911
|
-
this.broadcastChannel.postMessage({ event, session });
|
|
912
|
-
}
|
|
913
|
-
const errors = [];
|
|
914
|
-
const promises = Array.from(this.stateChangeEmitters.values()).map((x) => __awaiter(this, void 0, void 0, function* () {
|
|
915
|
-
try {
|
|
916
|
-
yield x.callback(event, session);
|
|
917
|
-
}
|
|
918
|
-
catch (e) {
|
|
919
|
-
errors.push(e);
|
|
920
|
-
}
|
|
921
|
-
}));
|
|
922
|
-
yield Promise.all(promises);
|
|
923
|
-
if (errors.length > 0) {
|
|
924
|
-
for (let i = 0; i < errors.length; i += 1) {
|
|
925
|
-
console.error(errors[i]);
|
|
926
|
-
}
|
|
927
|
-
throw errors[0];
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
finally {
|
|
931
|
-
this._debug(debugName, "end");
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Inside a browser context, `signOut()` will remove the logged in user from the browser session and log them out - removing all items from localstorage and then trigger a `"SIGNED_OUT"` event.
|
|
937
|
-
*
|
|
938
|
-
* For server-side management, you can revoke all refresh tokens for a user by passing a user's JWT through to `auth.api.signOut(JWT: string)`.
|
|
939
|
-
* There is no way to revoke a user's access token jwt until it expires. It is recommended to set a shorter expiry on the jwt for this reason.
|
|
940
|
-
*
|
|
941
|
-
* If using `others` scope, no `SIGNED_OUT` event is fired!
|
|
942
|
-
*/
|
|
943
|
-
signOut() {
|
|
944
|
-
return __awaiter(this, arguments, void 0, function* (options = { scope: "global" }) {
|
|
945
|
-
yield this.initializePromise;
|
|
946
|
-
return yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
947
|
-
return yield this._signOut(options);
|
|
948
|
-
}));
|
|
949
|
-
});
|
|
950
|
-
}
|
|
951
|
-
_signOut() {
|
|
952
|
-
return __awaiter(this, arguments, void 0, function* ({ scope } = { scope: "global" }) {
|
|
953
|
-
return yield this._useSession((result) => __awaiter(this, void 0, void 0, function* () {
|
|
954
|
-
var _a;
|
|
955
|
-
const { data, error: sessionError } = result;
|
|
956
|
-
if (sessionError) {
|
|
957
|
-
return { error: sessionError };
|
|
958
|
-
}
|
|
959
|
-
const accessToken = (_a = data.session) === null || _a === void 0 ? void 0 : _a.access_token;
|
|
960
|
-
if (accessToken) {
|
|
961
|
-
const { error } = yield this.api.signOut({ client_id: this.clientId });
|
|
962
|
-
if (error) {
|
|
963
|
-
// ignore 404s since user might not exist anymore
|
|
964
|
-
// ignore 401s since an invalid or expired JWT should sign out the current session
|
|
965
|
-
if (!((0, errors_1.isAuthApiError)(error) &&
|
|
966
|
-
(error.status === 404 || error.status === 401))) {
|
|
967
|
-
return { error };
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
if (scope !== "others") {
|
|
972
|
-
yield this._removeSession();
|
|
973
|
-
yield (0, storage_helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
|
|
974
|
-
yield this._notifyAllSubscribers("SIGNED_OUT", null);
|
|
975
|
-
}
|
|
976
|
-
return { error: null };
|
|
977
|
-
}));
|
|
978
|
-
});
|
|
979
|
-
}
|
|
980
|
-
/**
|
|
981
|
-
* Receive a notification every time an auth event happens.
|
|
982
|
-
* @param callback A callback function to be invoked when an auth event happens.
|
|
983
|
-
*/
|
|
984
|
-
onAuthStateChange(callback) {
|
|
985
|
-
const id = (0, helpers_1.uuid)();
|
|
986
|
-
const subscription = {
|
|
987
|
-
id,
|
|
988
|
-
callback,
|
|
989
|
-
unsubscribe: () => {
|
|
990
|
-
this._debug("#unsubscribe()", "state change callback with id removed", id);
|
|
991
|
-
this.stateChangeEmitters.delete(id);
|
|
992
|
-
},
|
|
993
|
-
};
|
|
994
|
-
this._debug("#onAuthStateChange()", "registered callback with id", id);
|
|
995
|
-
this.stateChangeEmitters.set(id, subscription);
|
|
996
|
-
(() => __awaiter(this, void 0, void 0, function* () {
|
|
997
|
-
yield this.initializePromise;
|
|
998
|
-
yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
999
|
-
this._emitInitialSession(id);
|
|
1000
|
-
}));
|
|
1001
|
-
}))();
|
|
1002
|
-
return { data: { subscription } };
|
|
1003
|
-
}
|
|
1004
|
-
_emitInitialSession(id) {
|
|
1005
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1006
|
-
return yield this._useSession((result) => __awaiter(this, void 0, void 0, function* () {
|
|
1007
|
-
var _a, _b;
|
|
1008
|
-
try {
|
|
1009
|
-
const { data: { session }, error, } = result;
|
|
1010
|
-
if (error)
|
|
1011
|
-
throw error;
|
|
1012
|
-
yield ((_a = this.stateChangeEmitters
|
|
1013
|
-
.get(id)) === null || _a === void 0 ? void 0 : _a.callback("INITIAL_SESSION", session));
|
|
1014
|
-
this._debug("INITIAL_SESSION", "callback id", id, "session", session);
|
|
1015
|
-
}
|
|
1016
|
-
catch (err) {
|
|
1017
|
-
yield ((_b = this.stateChangeEmitters
|
|
1018
|
-
.get(id)) === null || _b === void 0 ? void 0 : _b.callback("INITIAL_SESSION", null));
|
|
1019
|
-
this._debug("INITIAL_SESSION", "callback id", id, "error", err);
|
|
1020
|
-
console.error(err);
|
|
1021
|
-
}
|
|
1022
|
-
}));
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Returns a new session, regardless of expiry status.
|
|
1027
|
-
* Takes in an optional current session. If not passed in, then refreshSession() will attempt to retrieve it from getSession().
|
|
1028
|
-
* If the current session's refresh token is invalid, an error will be thrown.
|
|
1029
|
-
* @param currentSession The current session. If passed in, it must contain a refresh token.
|
|
1030
|
-
*/
|
|
1031
|
-
refreshSession(currentSession) {
|
|
1032
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1033
|
-
yield this.initializePromise;
|
|
1034
|
-
return yield this.lock._acquireLock(-1, () => __awaiter(this, void 0, void 0, function* () {
|
|
1035
|
-
return yield this._refreshSession(currentSession);
|
|
1036
|
-
}));
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
_refreshSession(currentSession) {
|
|
1040
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1041
|
-
try {
|
|
1042
|
-
return yield this._useSession((result) => __awaiter(this, void 0, void 0, function* () {
|
|
1043
|
-
var _a;
|
|
1044
|
-
if (!currentSession) {
|
|
1045
|
-
const { data, error } = result;
|
|
1046
|
-
if (error) {
|
|
1047
|
-
throw error;
|
|
1048
|
-
}
|
|
1049
|
-
currentSession = (_a = data.session) !== null && _a !== void 0 ? _a : undefined;
|
|
1050
|
-
}
|
|
1051
|
-
if (!(currentSession === null || currentSession === void 0 ? void 0 : currentSession.refresh_token)) {
|
|
1052
|
-
throw new errors_1.AuthSessionMissingError();
|
|
1053
|
-
}
|
|
1054
|
-
const { session, error } = yield this._callRefreshToken(currentSession.refresh_token);
|
|
1055
|
-
if (error) {
|
|
1056
|
-
return { data: { user: null, session: null }, error: error };
|
|
1057
|
-
}
|
|
1058
|
-
if (!session) {
|
|
1059
|
-
return { data: { user: null, session: null }, error: null };
|
|
1060
|
-
}
|
|
1061
|
-
return { data: { user: session.user, session }, error: null };
|
|
1062
|
-
}));
|
|
1063
|
-
}
|
|
1064
|
-
catch (error) {
|
|
1065
|
-
if ((0, errors_1.isAuthError)(error)) {
|
|
1066
|
-
return { data: { user: null, session: null }, error };
|
|
1067
|
-
}
|
|
1068
|
-
throw error;
|
|
1069
|
-
}
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
exports.FaableAuthClient = FaableAuthClient;
|