@leancodepl/login-manager 8.5.0 → 8.5.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/index.cjs.js ADDED
@@ -0,0 +1,554 @@
1
+ 'use strict';
2
+
3
+ var buffer = require('buffer');
4
+
5
+ function _extends() {
6
+ _extends = Object.assign || function assign(target) {
7
+ for(var i = 1; i < arguments.length; i++){
8
+ var source = arguments[i];
9
+ for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
10
+ }
11
+ return target;
12
+ };
13
+ return _extends.apply(this, arguments);
14
+ }
15
+
16
+ class BaseLoginManager {
17
+ trySignIn(username, password) {
18
+ return this.acquireToken(this.buildSignInRequest(username, password));
19
+ }
20
+ trySignInWithFacebook(accessToken) {
21
+ return this.acquireToken(this.buildSignInWithFacebookRequest(accessToken));
22
+ }
23
+ trySignInWithOneTimeToken(token) {
24
+ return this.acquireToken(this.buildSignInWithOneTimeTokenRequest(token));
25
+ }
26
+ trySignInWithGoogle(accessToken) {
27
+ return this.acquireToken(this.buildSignInWithGoogleRequest(accessToken));
28
+ }
29
+ trySignInWithLinkedIn(accessToken) {
30
+ return this.acquireToken(this.buildSignInWithLinkedInRequest(accessToken));
31
+ }
32
+ async tryRefreshToken() {
33
+ const token = await this.storage.getToken();
34
+ if (token !== null) {
35
+ return await this.tryRefreshTokenInternal(token);
36
+ } else {
37
+ return null;
38
+ }
39
+ }
40
+ tryRefreshTokenInternal(token) {
41
+ if (!this.isRefreshingToken) {
42
+ this.isRefreshingToken = true;
43
+ this.acquireToken(this.buildRefreshRequest(token)).then((result)=>{
44
+ this.isRefreshingToken = false;
45
+ this.refreshTokenCallbacks.forEach((c)=>c(result.type === "success"));
46
+ this.refreshTokenCallbacks = [];
47
+ });
48
+ }
49
+ return new Promise((resolve)=>{
50
+ this.refreshTokenCallbacks.push(resolve);
51
+ });
52
+ }
53
+ onChange(callback) {
54
+ this.callbacks.push(callback);
55
+ }
56
+ removeOnChange(callback) {
57
+ const idx = this.callbacks.indexOf(callback);
58
+ if (idx !== -1) {
59
+ this.callbacks.splice(idx, 1);
60
+ }
61
+ }
62
+ async acquireToken(init) {
63
+ try {
64
+ const result = await fetch(this.endpoint + "/connect/token", init);
65
+ if (!result.ok) {
66
+ if (result.status === 400) {
67
+ await this.signOut();
68
+ }
69
+ return {
70
+ type: "failure"
71
+ };
72
+ }
73
+ const tokenResult = await result.json();
74
+ const expDate = new Date();
75
+ expDate.setSeconds(new Date().getSeconds() + tokenResult.expires_in);
76
+ await this.storage.storeToken({
77
+ token: tokenResult.access_token,
78
+ refreshToken: tokenResult.refresh_token,
79
+ expirationDate: expDate
80
+ });
81
+ this.notify(true);
82
+ return {
83
+ type: "success"
84
+ };
85
+ } catch (e) {
86
+ console.warn("Cannot call Auth server, error: ", e);
87
+ return {
88
+ type: "networkError"
89
+ };
90
+ }
91
+ }
92
+ buildSignInRequest(username, password) {
93
+ const data = _extends({
94
+ grant_type: "password",
95
+ scope: this.scopes,
96
+ username: username,
97
+ password: password
98
+ }, this.additionalParams);
99
+ return {
100
+ method: "POST",
101
+ headers: this.prepareHeaders(),
102
+ body: new URLSearchParams(data)
103
+ };
104
+ }
105
+ buildSignInWithFacebookRequest(accessToken) {
106
+ const data = _extends({
107
+ grant_type: "facebook",
108
+ scope: this.scopes,
109
+ assertion: accessToken
110
+ }, this.additionalParams);
111
+ return {
112
+ method: "POST",
113
+ headers: this.prepareHeaders(),
114
+ body: new URLSearchParams(data)
115
+ };
116
+ }
117
+ buildSignInWithOneTimeTokenRequest(token) {
118
+ const data = _extends({
119
+ grant_type: "onetime",
120
+ scope: this.scopes,
121
+ token
122
+ }, this.additionalParams);
123
+ return {
124
+ method: "POST",
125
+ headers: this.prepareHeaders(),
126
+ body: new URLSearchParams(data)
127
+ };
128
+ }
129
+ buildSignInWithGoogleRequest(accessToken) {
130
+ const data = _extends({
131
+ grant_type: "google",
132
+ scope: this.scopes,
133
+ assertion: accessToken
134
+ }, this.additionalParams);
135
+ return {
136
+ method: "POST",
137
+ headers: this.prepareHeaders(),
138
+ body: new URLSearchParams(data)
139
+ };
140
+ }
141
+ buildSignInWithLinkedInRequest(accessToken) {
142
+ const data = _extends({
143
+ grant_type: "linkedin",
144
+ scope: this.scopes,
145
+ assertion: accessToken
146
+ }, this.additionalParams);
147
+ return {
148
+ method: "POST",
149
+ headers: this.prepareHeaders(),
150
+ body: new URLSearchParams(data)
151
+ };
152
+ }
153
+ buildRefreshRequest(token) {
154
+ const data = _extends({
155
+ grant_type: "refresh_token",
156
+ scope: this.scopes,
157
+ refresh_token: token.refreshToken || ""
158
+ }, this.additionalParams);
159
+ return {
160
+ method: "POST",
161
+ headers: this.prepareHeaders(),
162
+ body: new URLSearchParams(data)
163
+ };
164
+ }
165
+ prepareHeaders() {
166
+ const headers = new Headers();
167
+ if (this.clientSecret) {
168
+ const sec = buffer.Buffer.from(`${this.clientId}:${this.clientSecret}`, "binary").toString("base64");
169
+ headers.append("Authorization", "Basic " + sec);
170
+ }
171
+ headers.append("Content-Type", "application/x-www-form-urlencoded");
172
+ return headers;
173
+ }
174
+ notify(isSignedIn) {
175
+ for (const c of this.callbacks){
176
+ c(isSignedIn);
177
+ }
178
+ }
179
+ /* eslint-disable-next-line max-params */ constructor(storage, endpoint, clientSecret, clientId, scopes, additionalParams){
180
+ this.storage = storage;
181
+ this.endpoint = endpoint;
182
+ this.clientSecret = clientSecret;
183
+ this.clientId = clientId;
184
+ this.scopes = scopes;
185
+ this.additionalParams = additionalParams;
186
+ this.callbacks = [];
187
+ this.refreshTokenCallbacks = [];
188
+ this.isRefreshingToken = false;
189
+ if (!clientSecret) {
190
+ this.additionalParams = _extends({}, additionalParams, {
191
+ client_id: clientId
192
+ });
193
+ }
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Error thrown when token refresh fails.
199
+ *
200
+ * Indicates that the refresh token is invalid or expired, requiring user to sign in again.
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * try {
205
+ * const token = await loginManager.getToken();
206
+ * } catch (error) {
207
+ * if (error instanceof CannotRefreshToken) {
208
+ * console.log('User needs to sign in again');
209
+ * }
210
+ * }
211
+ * ```
212
+ */ class CannotRefreshToken extends Error {
213
+ constructor(m){
214
+ super(m);
215
+ Object.setPrototypeOf(this, CannotRefreshToken.prototype);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Manages OAuth2 authentication with asynchronous token storage.
221
+ *
222
+ * Extends BaseLoginManager to work with async storage implementations like IndexedDB or remote storage.
223
+ * Handles token refresh, expiration, and authentication state management.
224
+ *
225
+ * @param storage - Token storage implementation
226
+ * @param endpoint - OAuth2 server endpoint
227
+ * @param clientSecret - Client secret for authentication
228
+ * @param clientId - OAuth2 client identifier
229
+ * @param scopes - Space-separated OAuth2 scopes
230
+ * @param additionalParams - Additional OAuth2 parameters
231
+ * @example
232
+ * ```typescript
233
+ * const tokenStorage = new CustomAsyncStorage();
234
+ * const loginManager = new AsyncLoginManager(
235
+ * tokenStorage,
236
+ * 'https://api.example.com',
237
+ * 'client_secret',
238
+ * 'client_id',
239
+ * 'openid profile'
240
+ * );
241
+ * ```
242
+ */ class AsyncLoginManager extends BaseLoginManager {
243
+ async signOut() {
244
+ await this.storage.resetToken();
245
+ this.notify(false);
246
+ }
247
+ async isSigned() {
248
+ return await this.storage.getToken() !== null;
249
+ }
250
+ async getToken() {
251
+ const token = await this.storage.getToken();
252
+ if (token === null) {
253
+ return null;
254
+ } else if (token.expirationDate < new Date()) {
255
+ if (await this.tryRefreshTokenInternal(token)) {
256
+ var _this;
257
+ var _token;
258
+ return (_token = (_this = await this.storage.getToken()) == null ? void 0 : _this.token) != null ? _token : null;
259
+ } else {
260
+ throw new CannotRefreshToken("Cannot refresh access token after it has expired");
261
+ }
262
+ } else {
263
+ return token.token;
264
+ }
265
+ }
266
+ async load() {
267
+ const isSignedIn = await this.isSigned();
268
+ this.notify(isSignedIn);
269
+ }
270
+ }
271
+
272
+ /// <reference types="facebook-js-sdk" />
273
+ /**
274
+ * Integrates Facebook Login SDK for web applications.
275
+ *
276
+ * Handles Facebook authentication flow and provides access tokens for OAuth2 sign-in.
277
+ * Automatically loads Facebook SDK and manages login state.
278
+ *
279
+ * @param facebookAppId - Facebook App ID
280
+ * @param facebookPermissions - Comma-separated Facebook permissions
281
+ * @example
282
+ * ```typescript
283
+ * const facebookClient = new FacebookClient('your-app-id', 'email,public_profile');
284
+ * facebookClient.setup();
285
+ * facebookClient.login((accessToken) => {
286
+ * return loginManager.trySignInWithFacebook(accessToken);
287
+ * });
288
+ * ```
289
+ */ class FacebookClient {
290
+ get accessToken() {
291
+ return this.token;
292
+ }
293
+ setup(loginCallback) {
294
+ const ref = document.getElementsByTagName("script")[0];
295
+ const id = "facebook-jssdk";
296
+ if (document.getElementById(id)) {
297
+ this.initializeSDK();
298
+ this.getLoginStatus(loginCallback);
299
+ return;
300
+ }
301
+ const js = document.createElement("script");
302
+ js.id = id;
303
+ js.async = true;
304
+ js.src = "//connect.facebook.net/pl_PL/sdk.js";
305
+ if (ref.parentNode != null) {
306
+ ref.parentNode.insertBefore(js, ref);
307
+ }
308
+ js.onload = ()=>{
309
+ this.initializeSDK();
310
+ this.getLoginStatus(loginCallback);
311
+ };
312
+ }
313
+ login(callback) {
314
+ FB.login((response)=>{
315
+ if (response.status === "connected") {
316
+ this.isSignedIn = true;
317
+ this.token = response.authResponse.accessToken;
318
+ if (callback) {
319
+ callback(response.authResponse.accessToken);
320
+ }
321
+ } else {
322
+ this.isSignedIn = false;
323
+ }
324
+ }, {
325
+ scope: this.facebookPermissions
326
+ });
327
+ }
328
+ getLoginStatus(callback) {
329
+ FB.getLoginStatus((response)=>{
330
+ if (response.status === "connected") {
331
+ this.isSignedIn = true;
332
+ this.token = response.authResponse.accessToken;
333
+ if (callback) {
334
+ callback(response.authResponse.accessToken);
335
+ }
336
+ } else {
337
+ this.isSignedIn = false;
338
+ }
339
+ });
340
+ }
341
+ initializeSDK() {
342
+ FB.init({
343
+ appId: this.facebookAppId,
344
+ xfbml: true,
345
+ version: "v2.9"
346
+ });
347
+ }
348
+ constructor(facebookAppId, facebookPermissions){
349
+ this.facebookAppId = facebookAppId;
350
+ this.facebookPermissions = facebookPermissions;
351
+ this.isSignedIn = undefined;
352
+ this.token = "";
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Stores OAuth2 tokens in browser localStorage.
358
+ *
359
+ * Provides persistent token storage that survives browser sessions.
360
+ * Implements SyncTokenStorage interface for synchronous operations.
361
+ *
362
+ * @param tokenKey - localStorage key for access token (default: "token")
363
+ * @param refreshKey - localStorage key for refresh token (default: "refresh_token")
364
+ * @param expiryKey - localStorage key for expiry date (default: "expiration_date")
365
+ * @example
366
+ * ```typescript
367
+ * const storage = new LocalTokenStorage();
368
+ * const loginManager = new SyncLoginManager(storage, endpoint, secret, clientId, scopes);
369
+ * ```
370
+ */ class LocalTokenStorage {
371
+ getToken() {
372
+ if (this.hasValue(this.tokenKey)) {
373
+ return {
374
+ token: this.getValue(this.tokenKey),
375
+ refreshToken: this.getValue(this.refreshKey),
376
+ expirationDate: new Date(Number(this.getValue(this.expiryKey)))
377
+ };
378
+ } else {
379
+ return null;
380
+ }
381
+ }
382
+ storeToken(token) {
383
+ this.setValue(this.tokenKey, token.token);
384
+ this.setValue(this.refreshKey, token.refreshToken);
385
+ this.setValue(this.expiryKey, token.expirationDate.getTime().toString());
386
+ }
387
+ resetToken() {
388
+ this.remove(this.tokenKey);
389
+ this.remove(this.refreshKey);
390
+ this.remove(this.expiryKey);
391
+ }
392
+ hasValue(key) {
393
+ return localStorage.getItem(key) !== null;
394
+ }
395
+ getValue(key) {
396
+ return localStorage.getItem(key);
397
+ }
398
+ setValue(key, val) {
399
+ localStorage.setItem(key, val);
400
+ }
401
+ remove(key) {
402
+ localStorage.removeItem(key);
403
+ }
404
+ constructor(tokenKey = "token", refreshKey = "refresh_token", expiryKey = "expiration_date"){
405
+ this.tokenKey = tokenKey;
406
+ this.refreshKey = refreshKey;
407
+ this.expiryKey = expiryKey;
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Stores OAuth2 tokens in memory.
413
+ *
414
+ * Provides temporary token storage that clears when the page is refreshed.
415
+ * Implements SyncTokenStorage interface for synchronous operations.
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * const storage = new MemoryTokenStorage();
420
+ * const loginManager = new SyncLoginManager(storage, endpoint, secret, clientId, scopes);
421
+ * ```
422
+ */ class MemoryTokenStorage {
423
+ getToken() {
424
+ return this.token;
425
+ }
426
+ storeToken(token) {
427
+ this.token = {
428
+ token: token.token,
429
+ refreshToken: token.refreshToken,
430
+ expirationDate: token.expirationDate
431
+ };
432
+ return Promise.resolve();
433
+ }
434
+ resetToken() {
435
+ this.token = null;
436
+ return Promise.resolve();
437
+ }
438
+ constructor(){
439
+ this.token = null;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Stores OAuth2 tokens in browser sessionStorage.
445
+ *
446
+ * Provides session-based token storage that clears when the browser tab is closed.
447
+ * Implements SyncTokenStorage interface for synchronous operations.
448
+ *
449
+ * @param tokenKey - sessionStorage key for access token (default: "token")
450
+ * @param refreshKey - sessionStorage key for refresh token (default: "refresh_token")
451
+ * @param expiryKey - sessionStorage key for expiry date (default: "expiration_date")
452
+ * @example
453
+ * ```typescript
454
+ * const storage = new SessionTokenStorage();
455
+ * const loginManager = new SyncLoginManager(storage, endpoint, secret, clientId, scopes);
456
+ * ```
457
+ */ class SessionTokenStorage {
458
+ getToken() {
459
+ if (this.hasValue(this.tokenKey)) {
460
+ return {
461
+ token: this.getValue(this.tokenKey),
462
+ refreshToken: this.getValue(this.refreshKey),
463
+ expirationDate: new Date(Number(this.getValue(this.expiryKey)))
464
+ };
465
+ } else {
466
+ return null;
467
+ }
468
+ }
469
+ storeToken(token) {
470
+ this.setValue(this.tokenKey, token.token);
471
+ this.setValue(this.refreshKey, token.refreshToken);
472
+ this.setValue(this.expiryKey, token.expirationDate.getTime().toString());
473
+ }
474
+ resetToken() {
475
+ this.remove(this.tokenKey);
476
+ this.remove(this.refreshKey);
477
+ this.remove(this.expiryKey);
478
+ }
479
+ hasValue(key) {
480
+ return sessionStorage.getItem(key) !== null;
481
+ }
482
+ getValue(key) {
483
+ return sessionStorage.getItem(key);
484
+ }
485
+ setValue(key, val) {
486
+ sessionStorage.setItem(key, val);
487
+ }
488
+ remove(key) {
489
+ sessionStorage.removeItem(key);
490
+ }
491
+ constructor(tokenKey = "token", refreshKey = "refresh_token", expiryKey = "expiration_date"){
492
+ this.tokenKey = tokenKey;
493
+ this.refreshKey = refreshKey;
494
+ this.expiryKey = expiryKey;
495
+ }
496
+ }
497
+
498
+ /**
499
+ * Manages OAuth2 authentication with synchronous token storage.
500
+ *
501
+ * Extends BaseLoginManager to work with sync storage implementations like localStorage or sessionStorage.
502
+ * Handles token refresh, expiration, and authentication state management.
503
+ *
504
+ * @param storage - Token storage implementation
505
+ * @param endpoint - OAuth2 server endpoint
506
+ * @param clientSecret - Client secret for authentication
507
+ * @param clientId - OAuth2 client identifier
508
+ * @param scopes - Space-separated OAuth2 scopes
509
+ * @param additionalParams - Additional OAuth2 parameters
510
+ * @example
511
+ * ```typescript
512
+ * const tokenStorage = new LocalTokenStorage();
513
+ * const loginManager = new SyncLoginManager(
514
+ * tokenStorage,
515
+ * 'https://api.example.com',
516
+ * 'client_secret',
517
+ * 'client_id',
518
+ * 'openid profile'
519
+ * );
520
+ * ```
521
+ */ class SyncLoginManager extends BaseLoginManager {
522
+ signOut() {
523
+ this.storage.resetToken();
524
+ this.notify(false);
525
+ }
526
+ isSigned() {
527
+ return this.storage.getToken() !== null;
528
+ }
529
+ async getToken() {
530
+ const token = this.storage.getToken();
531
+ if (token === null) {
532
+ return null;
533
+ } else if (token.expirationDate < new Date()) {
534
+ if (await this.tryRefreshTokenInternal(token)) {
535
+ var _this_storage_getToken;
536
+ var _this_storage_getToken_token;
537
+ return (_this_storage_getToken_token = (_this_storage_getToken = this.storage.getToken()) == null ? void 0 : _this_storage_getToken.token) != null ? _this_storage_getToken_token : null;
538
+ } else {
539
+ throw new CannotRefreshToken("Cannot refresh access token after it has expired");
540
+ }
541
+ } else {
542
+ return token.token;
543
+ }
544
+ }
545
+ }
546
+
547
+ exports.AsyncLoginManager = AsyncLoginManager;
548
+ exports.BaseLoginManager = BaseLoginManager;
549
+ exports.CannotRefreshToken = CannotRefreshToken;
550
+ exports.FacebookClient = FacebookClient;
551
+ exports.LocalTokenStorage = LocalTokenStorage;
552
+ exports.MemoryTokenStorage = MemoryTokenStorage;
553
+ exports.SessionTokenStorage = SessionTokenStorage;
554
+ exports.SyncLoginManager = SyncLoginManager;
package/index.cjs.mjs ADDED
@@ -0,0 +1,2 @@
1
+ export * from './index.cjs.js';
2
+ export { _default as default } from './index.cjs.default.js';
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";