jwtbutler 2.0.1 → 2.0.3

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.
@@ -1,765 +1,685 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.default = void 0;
8
- require("core-js/modules/es.array-buffer.constructor.js");
9
- require("core-js/modules/es.array-buffer.slice.js");
10
- require("core-js/modules/es.json.stringify.js");
11
- require("core-js/modules/es.regexp.exec.js");
12
- require("core-js/modules/es.string.replace.js");
13
- require("core-js/modules/es.typed-array.uint8-array.js");
14
- require("core-js/modules/es.typed-array.fill.js");
15
- require("core-js/modules/es.typed-array.set.js");
16
- require("core-js/modules/es.typed-array.sort.js");
17
- require("core-js/modules/esnext.iterator.constructor.js");
18
- require("core-js/modules/esnext.iterator.for-each.js");
19
- require("core-js/modules/esnext.typed-array.at.js");
20
- require("whatwg-fetch");
21
- var _helpers = _interopRequireDefault(require("./_helpers"));
22
- // use fetch
23
-
24
- class jwtbutler {
25
- constructor(config) {
26
- if (!('auth_login' in config)) {
27
- config.auth_login = 'email';
28
- }
29
- if (!('captcha' in config)) {
30
- config.captcha = false;
31
- }
32
- if (!('passkeys' in config)) {
33
- config.passkeys = false;
34
- }
35
- if (!('language' in config)) {
36
- config.language = 'en';
37
- }
38
- this.config = config;
39
- }
40
- isLoggedIn() {
41
- if (this.getPayload() === null) {
42
- return false;
43
- }
44
- return true;
45
- }
46
- getUserId() {
47
- let payload = this.getPayload();
48
- if (payload === null) {
49
- return null;
50
- }
51
- return payload.sub;
52
- }
53
- getPayload() {
54
- if (_helpers.default.cookieGet('access_token') === null) {
55
- return null;
56
- }
57
- try {
58
- return JSON.parse(atob(_helpers.default.cookieGet('access_token').split('.')[1]));
59
- } catch (e) {
60
- return null;
61
- }
62
- }
63
- logout() {
64
- return new Promise((resolve, reject) => {
65
- this.addLoadingState('logging-out');
66
- fetch(this.config.auth_server + '/logout', {
67
- method: 'POST',
68
- headers: {
69
- 'content-type': 'application/json',
70
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
71
- },
72
- cache: 'no-cache'
73
- }).then(res => res.json()).catch(err => err).then(response => {
74
- this.setCookies(null).then(() => {
75
- this.removeLoadingStates();
76
- resolve();
77
- }).catch(error => {
78
- reject(error);
79
- });
80
- });
81
- });
82
- }
83
- passkeyRegister() {
84
- return new Promise((resolve, reject) => {
85
- if (this.isLoggedIn() === false || !('credentials' in navigator)) {
86
- reject();
87
- return;
88
- }
89
- this.addLoadingState('logging-in');
90
- fetch(this.config.auth_server + '/passkey-register-options', {
91
- method: 'POST',
92
- headers: {
93
- 'content-type': 'application/json',
94
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
95
- },
96
- cache: 'no-cache'
97
- }).then(res => res.json()).catch(error => error).then(response => {
98
- if (response === undefined || response === null || response.success !== true) {
99
- this.removeLoadingStates();
100
- reject(response);
101
- return;
102
- }
103
- let publicKey = this.passkeyPublicKeyFromJson(response.data.publicKey);
104
- navigator.credentials.create({
105
- publicKey: publicKey
106
- }).then(credential => {
107
- if (credential === null) {
108
- this.removeLoadingStates();
109
- reject();
110
- return;
111
- }
112
- fetch(this.config.auth_server + '/passkey-register', {
113
- method: 'POST',
114
- body: JSON.stringify({
115
- credential: this.passkeyCredentialToJson(credential)
116
- }),
117
- headers: {
118
- 'content-type': 'application/json',
119
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
120
- },
121
- cache: 'no-cache'
122
- }).then(res => res.json()).catch(error => error).then(response => {
123
- this.removeLoadingStates();
124
- if (response !== undefined && response !== null && response.success === true) {
125
- resolve(response);
126
- } else {
127
- reject(response);
128
- }
129
- });
130
- }).catch(error => {
131
- this.removeLoadingStates();
132
- reject(error);
133
- });
134
- });
135
- });
136
- }
137
- passkeyDelete(id) {
138
- return new Promise((resolve, reject) => {
139
- if (this.isLoggedIn() === false) {
140
- reject();
141
- return;
142
- }
143
- this.addLoadingState('logging-in');
144
- fetch(this.config.auth_server + '/passkey-delete', {
145
- method: 'POST',
146
- body: JSON.stringify({
147
- id: id
148
- }),
149
- headers: {
150
- 'content-type': 'application/json',
151
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
152
- },
153
- cache: 'no-cache'
154
- }).then(res => res.json()).catch(error => error).then(response => {
155
- this.removeLoadingStates();
156
- if (response !== undefined && response !== null && response.success === true) {
157
- resolve(response);
158
- } else {
159
- reject(response);
160
- }
161
- });
162
- });
163
- }
164
- passkeyLogin() {
165
- let login = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
166
- return new Promise((resolve, reject) => {
167
- if (!('credentials' in navigator)) {
168
- reject();
169
- return;
170
- }
171
- this.addLoadingState('logging-in');
172
- let body = {};
173
- if (login !== null && login !== '') {
174
- body[this.config.auth_login] = login;
175
- }
176
- fetch(this.config.auth_server + '/passkey-login-options', {
177
- method: 'POST',
178
- body: JSON.stringify(body),
179
- headers: {
180
- 'content-type': 'application/json'
181
- },
182
- cache: 'no-cache'
183
- }).then(res => res.json()).catch(error => error).then(response => {
184
- if (response === undefined || response === null || response.success !== true) {
185
- this.removeLoadingStates();
186
- reject(response);
187
- return;
188
- }
189
- let publicKey = this.passkeyPublicKeyFromJson(response.data.publicKey);
190
- navigator.credentials.get({
191
- publicKey: publicKey
192
- }).then(credential => {
193
- if (credential === null) {
194
- this.removeLoadingStates();
195
- reject();
196
- return;
197
- }
198
- fetch(this.config.auth_server + '/passkey-login', {
199
- method: 'POST',
200
- body: JSON.stringify({
201
- credential: this.passkeyCredentialToJson(credential)
202
- }),
203
- headers: {
204
- 'content-type': 'application/json'
205
- },
206
- cache: 'no-cache'
207
- }).then(res => res.json()).catch(error => error).then(response => {
208
- if (response !== undefined && response !== null && response.success === true) {
209
- this.setCookies(response.data.access_token).then(() => {
210
- this.removeLoadingStates();
211
- resolve(response);
212
- }).catch(error => {
213
- this.removeLoadingStates();
214
- reject(error);
215
- });
216
- } else {
217
- this.removeLoadingStates();
218
- reject(response);
219
- }
220
- });
221
- }).catch(error => {
222
- this.removeLoadingStates();
223
- reject(error);
224
- });
225
- });
226
- });
227
- }
228
- login() {
229
- return new Promise((resolve, reject) => {
230
- if (_helpers.default.cookieGet('access_token') !== null) {
231
- this.addLoadingState('logging-in');
232
- fetch(this.config.auth_server + '/check', {
233
- method: 'POST',
234
- body: JSON.stringify({
235
- access_token: _helpers.default.cookieGet('access_token')
236
- }),
237
- headers: {
238
- 'content-type': 'application/json'
239
- },
240
- cache: 'no-cache'
241
- }).then(res => res.json()).catch(err => err).then(response => {
242
- if (response.success === true) {
243
- this.setCookies(_helpers.default.cookieGet('access_token')).then(() => {
244
- this.removeLoadingStates();
245
- resolve();
246
- }).catch(error => {
247
- reject(error);
248
- });
249
- } else {
250
- fetch(this.config.auth_server + '/refresh', {
251
- method: 'POST',
252
- headers: {
253
- 'content-type': 'application/json',
254
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
255
- },
256
- cache: 'no-cache'
257
- }).then(res => res.json()).catch(err => err).then(response => {
258
- if (response.success === true) {
259
- this.setCookies(response.data.access_token).then(() => {
260
- this.removeLoadingStates();
261
- resolve();
262
- }).catch(error => {
263
- reject(error);
264
- });
265
- } else {
266
- this.renderLoginFormWithPromise().then(() => {
267
- resolve();
268
- });
269
- }
270
- });
271
- }
272
- });
273
- } else {
274
- this.renderLoginFormWithPromise().then(() => {
275
- resolve();
276
- }).catch(() => {
277
- reject();
278
- });
279
- }
280
- });
281
- }
282
- fetch(url) {
283
- let args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
284
- return new Promise((resolve, reject) => {
285
- if (!('headers' in args)) {
286
- args.headers = {};
287
- }
288
- if (!('tries' in args)) {
289
- args.tries = 0;
290
- }
291
- args.tries++;
292
- if (args.tries > 3) {
293
- reject(null);
294
- return;
295
- } else if (this.isLoggedIn() === false) {
296
- this.login().then(() => {
297
- this.fetch(url, args).then(response => {
298
- resolve(response);
299
- }).catch(error => {
300
- reject(error);
301
- });
302
- }).catch(error => {
303
- reject(error);
304
- });
305
- } else {
306
- this.addLoadingState('fetching');
307
- args.headers.Authorization = 'Bearer ' + _helpers.default.cookieGet('access_token');
308
- fetch(url, args).then(v => v).catch(v => v).then(response => {
309
- this.removeLoadingStates();
310
- if (response.status === 401) {
311
- this.addLoadingState('logging-in');
312
- fetch(this.config.auth_server + '/refresh', {
313
- method: 'POST',
314
- headers: {
315
- 'content-type': 'application/json',
316
- Authorization: 'Bearer ' + _helpers.default.cookieGet('access_token')
317
- },
318
- cache: 'no-cache'
319
- }).then(res => res.json()).catch(error => error).then(response => {
320
- if (response.success === true) {
321
- this.setCookies(response.data.access_token).then(() => {
322
- this.removeLoadingStates();
323
- this.fetch(url, args).then(response => {
324
- resolve(response);
325
- }).catch(error => {
326
- reject(error);
327
- });
328
- }).catch(error => {
329
- this.removeLoadingStates();
330
- reject(error);
331
- });
332
- } else {
333
- this.removeLoadingStates();
334
- this.renderLoginFormWithPromise().then(() => {
335
- this.fetch(url, args).then(response => {
336
- resolve(response);
337
- }).catch(error => {
338
- reject(error);
339
- });
340
- }).catch(error => {
341
- reject(error);
342
- });
343
- }
344
- });
345
- } else {
346
- resolve(response);
347
- }
348
- });
349
- }
350
- });
351
- }
352
- setCookies() {
353
- let access_token = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
354
- return new Promise((resolve, reject) => {
355
- if (this.setCookieLoading === undefined) {
356
- this.setCookieLoading === false;
357
- }
358
- if (this.setCookieLoading === true) {
359
- resolve();
360
- return;
361
- }
362
- this.setCookieLoading = true;
363
- if (access_token !== null) {
364
- _helpers.default.cookieSet('access_token', access_token, 28);
365
- } else {
366
- _helpers.default.cookieDelete('access_token');
367
- }
368
- if (this.config.sso === undefined || this.config.sso.length === 1 && this.config.sso[0] === window.location.protocol + '//' + window.location.host) {
369
- this.setCookieLoading = false;
370
- resolve();
371
- return;
372
- }
373
- _helpers.default.remove(document.querySelector('.iframe_wrapper'));
374
- let iframe_wrapper = document.createElement('div');
375
- iframe_wrapper.setAttribute('class', 'iframe_wrapper');
376
- iframe_wrapper.style.position = 'absolute';
377
- iframe_wrapper.style.opacity = '0';
378
- document.body.appendChild(iframe_wrapper);
379
- let timeout = null;
380
- let _this = this,
381
- todo = this.config.sso.length - 1,
382
- waitForPostMessage = function (e) {
383
- if (_this.config.sso.indexOf(e.origin) === -1) {
384
- return;
385
- }
386
- if (e.data !== undefined && e.data !== null && 'success' in e.data && e.data.success === true) {
387
- todo--;
388
- }
389
- //console.log(todo);
390
- if (todo <= 0) {
391
- if (timeout) {
392
- //console.log('cleared timeout');
393
- clearTimeout(timeout);
394
- }
395
- window.removeEventListener('message', waitForPostMessage, false);
396
- _helpers.default.remove(document.querySelector('.iframe_wrapper'));
397
- _this.setCookieLoading = false;
398
- resolve();
399
- }
400
- };
401
- window.addEventListener('message', waitForPostMessage, false);
402
- timeout = setTimeout(() => {
403
- if (this.setCookieLoading === true) {
404
- //console.log('timeout');
405
- window.removeEventListener('message', waitForPostMessage, false);
406
- _helpers.default.remove(document.querySelector('.iframe_wrapper'));
407
- this.setCookieLoading = false;
408
- resolve();
409
- }
410
- }, 20000);
411
- this.config.sso.forEach(sso__value => {
412
- if (sso__value === window.location.protocol + '//' + window.location.host) {
413
- return;
414
- }
415
- let iframe = document.createElement('iframe');
416
- iframe.setAttribute('src', sso__value + '/sso.html');
417
- iframe.style.width = '1px';
418
- iframe.style.height = '1px';
419
- iframe.addEventListener('load', e => {
420
- iframe.contentWindow.postMessage({
421
- access_token: access_token
422
- }, sso__value);
423
- });
424
- document.querySelector('.iframe_wrapper').appendChild(iframe);
425
- });
426
- });
427
- }
428
- renderLoginFormWithPromise() {
429
- return new Promise((resolve, reject) => {
430
- this.buildUpLoginFormHtml();
431
- this.captchaRender().then(() => {
432
- this.bindLoginFormSubmit().then(() => {
433
- resolve();
434
- }).catch(error => {
435
- reject(error);
436
- });
437
- this.triggerLoginFormRenderedEvent();
438
- }).catch(error => {
439
- reject(error);
440
- });
441
- });
442
- }
443
- buildUpLoginFormHtml() {
444
- if (!('login_form' in this.config) || this.config.login_form == '') {
445
- this.config.login_form = "<div class=\"login-form\">\n <div class=\"login-form__inner\">\n <form class=\"login-form__form\">\n <ul class=\"login-form__items\">\n <li class=\"login-form__item\">\n <label class=\"login-form__label login-form__label--".concat(this.config.auth_login, "\" for=\"login-form__label--").concat(this.config.auth_login, "\">").concat(this.config.auth_login === 'email' ? 'E-Mail-Adresse' : this.config.auth_login === 'username' ? 'Benutzername' : this.config.auth_login, "</label>\n <input class=\"login-form__input login-form__input--").concat(this.config.auth_login, "\" id=\"login-form__label--").concat(this.config.auth_login, "\" type=\"text\" required=\"required\" autocomplete=\"").concat(this.config.auth_login, "\" name=\"").concat(this.config.auth_login, "\" />\n </li>\n <li class=\"login-form__item\">\n <label class=\"login-form__label login-form__label--password\" for=\"login-form__label--password\">Passwort</label>\n <input class=\"login-form__input login-form__input--password\" id=\"login-form__label--password\" type=\"password\" required=\"required\" autocomplete=\"current-password\" name=\"password\" />\n </li>\n ").concat(this.captchaEnabled() ? "<li class=\"login-form__item\">\n <div class=\"login-form__captcha\"></div>\n </li>" : '', "\n <li class=\"login-form__item\">\n <input class=\"login-form__submit\" type=\"submit\" value=\"Anmelden\" />\n </li>\n ").concat(this.passkeyEnabled() ? "<li class=\"login-form__item\">\n <button class=\"login-form__passkey\" type=\"button\">Mit Passkey anmelden</button>\n </li>" : '', "\n </ul>\n </form>\n </div>\n </div>");
446
- }
447
- let dom = new DOMParser().parseFromString(this.config.login_form, 'text/html').body.childNodes[0];
448
- this.config.login_form_class = dom.getAttribute('class').split(' ')[0];
449
- let submit = dom.querySelector('input[type="submit"]');
450
- let submitItem = submit !== null ? submit.closest('.' + this.config.login_form_class + '__item') || submit.closest('li') || submit.parentElement : null;
451
- let itemTag = submitItem !== null && submitItem.tagName.toLowerCase() === 'li' ? 'li' : 'div';
452
- if (this.captchaEnabled() && dom.querySelector('.' + this.config.login_form_class + '__captcha') === null) {
453
- if (submitItem !== null) {
454
- submitItem.insertAdjacentHTML('beforebegin', '<' + itemTag + ' class="' + this.config.login_form_class + '__item"><div class="' + this.config.login_form_class + '__captcha"></div></' + itemTag + '>');
455
- }
456
- }
457
- if (this.passkeyEnabled() && dom.querySelector('.' + this.config.login_form_class + '__passkey') === null) {
458
- if (submitItem !== null) {
459
- submitItem.insertAdjacentHTML('afterend', '<' + itemTag + ' class="' + this.config.login_form_class + '__item"><button class="' + this.config.login_form_class + '__passkey" type="button">Mit Passkey anmelden</button></' + itemTag + '>');
460
- }
461
- }
462
- _helpers.default.remove(document.querySelector('.' + this.config.login_form_class));
463
- this.addLoadingState('login-form-visible');
464
- let parent = document.body;
465
- if ('login_form_parent' in this.config && this.config.login_form_parent != '' && document.querySelector(this.config.login_form_parent) !== null) {
466
- parent = document.querySelector(this.config.login_form_parent);
467
- }
468
- parent.appendChild(dom);
469
- }
470
- triggerLoginFormRenderedEvent() {
471
- if ('login_form_rendered' in this.config && this.config.login_form_rendered != '' && typeof this.config.login_form_rendered === 'function') {
472
- this.config.login_form_rendered(document.querySelector('.' + this.config.login_form_class));
473
- }
474
- }
475
- captchaEnabled() {
476
- return this.config.captcha !== false && typeof this.config.captcha === 'object' && (!('provider' in this.config.captcha) || this.config.captcha.provider === 'hcaptcha') && 'sitekey' in this.config.captcha && this.config.captcha.sitekey !== '';
477
- }
478
- passkeyEnabled() {
479
- return this.config.passkeys !== false;
480
- }
481
- responseMessage(response) {
482
- let messages = {
483
- en: {
484
- _default: 'Not successful',
485
- 'unknown route': 'Unknown route!',
486
- 'captcha not successful': 'Captcha not successful',
487
- 'too many login attempts': 'Too many login attempts. Please try again later.',
488
- 'auth successful': 'Successfully logged in',
489
- 'auth not successful': 'Not successful',
490
- 'invalid token': 'Invalid token',
491
- 'logout successful': 'Successfully logged out',
492
- 'logout not successful': 'Logout not successful',
493
- 'valid token': 'Valid token',
494
- 'passkey registration options created': 'Passkey registration prepared',
495
- 'passkey registration options not created': 'Passkey registration not prepared',
496
- 'passkey registered': 'Passkey registered',
497
- 'passkey not registered': 'Passkey not registered',
498
- 'passkey login options created': 'Passkey login prepared',
499
- 'passkey login options not created': 'Passkey login not prepared',
500
- 'passkey auth not successful': 'Passkey not successful',
501
- 'passkey deleted': 'Passkey deleted',
502
- 'passkey not deleted': 'Passkey not deleted'
503
- },
504
- de: {
505
- _default: 'Nicht erfolgreich',
506
- 'unknown route': 'Unbekannte Route!',
507
- 'captcha not successful': 'Captcha nicht erfolgreich',
508
- 'too many login attempts': 'Zu viele Loginversuche. Bitte später erneut versuchen.',
509
- 'auth successful': 'Erfolgreich eingeloggt',
510
- 'auth not successful': 'Nicht erfolgreich',
511
- 'invalid token': 'Falsches Token',
512
- 'logout successful': 'Erfolgreich ausgeloggt',
513
- 'logout not successful': 'Nicht erfolgreich ausgeloggt',
514
- 'valid token': 'Korrektes Token',
515
- 'passkey registration options created': 'Passkey-Registrierung vorbereitet',
516
- 'passkey registration options not created': 'Passkey-Registrierung nicht vorbereitet',
517
- 'passkey registered': 'Passkey registriert',
518
- 'passkey not registered': 'Passkey nicht registriert',
519
- 'passkey login options created': 'Passkey-Login vorbereitet',
520
- 'passkey login options not created': 'Passkey-Login nicht vorbereitet',
521
- 'passkey auth not successful': 'Passkey nicht erfolgreich',
522
- 'passkey deleted': 'Passkey gelöscht',
523
- 'passkey not deleted': 'Passkey nicht gelöscht'
524
- }
525
- };
526
- let language = this.config.language in messages ? this.config.language : 'en';
527
- if (response !== undefined && response !== null && 'message' in response && response.message in messages[language]) {
528
- return messages[language][response.message];
529
- }
530
- if (response !== undefined && response !== null && 'public_message' in response) {
531
- return response.public_message;
532
- }
533
- return messages[language]._default;
534
- }
535
- captchaRender() {
536
- return new Promise((resolve, reject) => {
537
- if (!this.captchaEnabled()) {
538
- resolve();
539
- return;
540
- }
541
- this.captchaLoad().then(() => {
542
- let captcha = document.querySelector('.' + this.config.login_form_class + '__captcha');
543
- if (captcha === null) {
544
- resolve();
545
- return;
546
- }
547
- if (captcha.getAttribute('data-widget-id') !== null) {
548
- resolve();
549
- return;
550
- }
551
- let options = {
552
- sitekey: this.config.captcha.sitekey,
553
- theme: this.config.captcha.theme || 'light'
554
- };
555
- if (this.config.language !== '') {
556
- options.hl = this.config.language;
557
- }
558
- captcha.setAttribute('data-widget-id', window.hcaptcha.render(captcha, options));
559
- resolve();
560
- }).catch(error => {
561
- reject(error);
562
- });
563
- });
564
- }
565
- captchaLoad() {
566
- return new Promise((resolve, reject) => {
567
- if (!this.captchaEnabled() || 'hcaptcha' in window) {
568
- resolve();
569
- return;
570
- }
571
- if (document.querySelector('script[data-jwtbutler-hcaptcha]') !== null) {
572
- document.querySelector('script[data-jwtbutler-hcaptcha]').addEventListener('load', () => resolve());
573
- document.querySelector('script[data-jwtbutler-hcaptcha]').addEventListener('error', error => reject(error));
574
- return;
575
- }
576
- let script = document.createElement('script');
577
- script.setAttribute('src', 'https://js.hcaptcha.com/1/api.js?render=explicit');
578
- script.setAttribute('async', 'async');
579
- script.setAttribute('defer', 'defer');
580
- script.setAttribute('data-jwtbutler-hcaptcha', 'true');
581
- script.addEventListener('load', () => resolve());
582
- script.addEventListener('error', error => reject(error));
583
- document.head.appendChild(script);
584
- });
585
- }
586
- captchaToken(form) {
587
- return new Promise((resolve, reject) => {
588
- if (!this.captchaEnabled()) {
589
- resolve(null);
590
- return;
591
- }
592
- let captcha = form.querySelector('.' + this.config.login_form_class + '__captcha');
593
- if (captcha === null || !('hcaptcha' in window)) {
594
- reject({
595
- message: 'captcha not successful',
596
- public_message: 'Captcha not successful'
597
- });
598
- return;
599
- }
600
- let token = window.hcaptcha.getResponse(captcha.getAttribute('data-widget-id'));
601
- if (token === '') {
602
- reject({
603
- message: 'captcha not successful',
604
- public_message: 'Captcha not successful'
605
- });
606
- return;
607
- }
608
- resolve(token);
609
- });
610
- }
611
- captchaReset(form) {
612
- if (!this.captchaEnabled() || !('hcaptcha' in window)) {
613
- return;
614
- }
615
- let captcha = form.querySelector('.' + this.config.login_form_class + '__captcha');
616
- if (captcha === null || captcha.getAttribute('data-widget-id') === null) {
617
- return;
618
- }
619
- window.hcaptcha.reset(captcha.getAttribute('data-widget-id'));
620
- }
621
- bindLoginFormSubmit() {
622
- return new Promise((resolve, reject) => {
623
- let dom = document.querySelector('.' + this.config.login_form_class),
624
- form = dom.querySelector('form');
625
- form.addEventListener('submit', e => {
626
- this.addLoadingState('logging-in');
627
- if (form.querySelector('input[type="submit"]') !== null) {
628
- form.querySelector('input[type="submit"]').disabled = true;
629
- }
630
- _helpers.default.remove(dom.querySelector('.' + this.config.login_form_class + '__error'));
631
- let body = {
632
- [this.config.auth_login]: form.querySelector('input[name="' + this.config.auth_login + '"]').value,
633
- password: form.querySelector('input[name="password"]').value
634
- };
635
- this.captchaToken(form).then(captchaToken => {
636
- if (captchaToken !== null) {
637
- body['h-captcha-response'] = captchaToken;
638
- }
639
- return fetch(this.config.auth_server + '/login', {
640
- method: 'POST',
641
- body: JSON.stringify(body),
642
- headers: {
643
- 'content-type': 'application/json'
644
- },
645
- cache: 'no-cache'
646
- });
647
- }).then(res => res.json()).catch(error => error).then(response => {
648
- if (form.querySelector('input[type="submit"]') !== null) {
649
- form.querySelector('input[type="submit"]').disabled = false;
650
- }
651
- if (response !== undefined && response !== null && 'success' in response && response.success === true) {
652
- _helpers.default.remove(document.querySelector('.' + this.config.login_form_class));
653
- this.setCookies(response.data.access_token).then(() => {
654
- this.removeLoadingStates();
655
- resolve();
656
- }).catch(error => {
657
- reject(error);
658
- });
659
- } else {
660
- this.removeLoadingStates();
661
- this.addLoadingState('login-form-visible');
662
- this.captchaReset(form);
663
- form.insertAdjacentHTML('afterbegin', '<div class="' + this.config.login_form_class + '__error">' + this.responseMessage(response) + '</div>');
664
- }
665
- });
666
- e.preventDefault();
667
- }, false);
668
- if (form.querySelector('.' + this.config.login_form_class + '__passkey') !== null) {
669
- form.querySelector('.' + this.config.login_form_class + '__passkey').addEventListener('click', e => {
670
- this.addLoadingState('logging-in');
671
- _helpers.default.remove(dom.querySelector('.' + this.config.login_form_class + '__error'));
672
- let login = null;
673
- if (form.querySelector('input[name="' + this.config.auth_login + '"]') !== null) {
674
- login = form.querySelector('input[name="' + this.config.auth_login + '"]').value;
675
- }
676
- this.passkeyLogin(login).then(() => {
677
- _helpers.default.remove(document.querySelector('.' + this.config.login_form_class));
678
- this.removeLoadingStates();
679
- resolve();
680
- }).catch(response => {
681
- this.removeLoadingStates();
682
- this.addLoadingState('login-form-visible');
683
- form.insertAdjacentHTML('afterbegin', '<div class="' + this.config.login_form_class + '__error">' + this.responseMessage(response) + '</div>');
684
- });
685
- e.preventDefault();
686
- }, false);
687
- }
688
- });
689
- }
690
- passkeyPublicKeyFromJson(publicKey) {
691
- publicKey.challenge = this.passkeyBase64UrlToBuffer(publicKey.challenge);
692
- if ('user' in publicKey && 'id' in publicKey.user) {
693
- publicKey.user.id = this.passkeyBase64UrlToBuffer(publicKey.user.id);
694
- }
695
- if ('excludeCredentials' in publicKey) {
696
- publicKey.excludeCredentials.forEach(credential => {
697
- credential.id = this.passkeyBase64UrlToBuffer(credential.id);
698
- });
699
- }
700
- if ('allowCredentials' in publicKey) {
701
- publicKey.allowCredentials.forEach(credential => {
702
- credential.id = this.passkeyBase64UrlToBuffer(credential.id);
703
- });
704
- }
705
- return publicKey;
706
- }
707
- passkeyCredentialToJson(credential) {
708
- let response = {
709
- clientDataJSON: this.passkeyBufferToBase64Url(credential.response.clientDataJSON)
710
- };
711
- if ('attestationObject' in credential.response) {
712
- response.attestationObject = this.passkeyBufferToBase64Url(credential.response.attestationObject);
713
- if (typeof credential.response.getTransports === 'function') {
714
- response.transports = credential.response.getTransports();
715
- }
716
- }
717
- if ('authenticatorData' in credential.response) {
718
- response.authenticatorData = this.passkeyBufferToBase64Url(credential.response.authenticatorData);
719
- response.signature = this.passkeyBufferToBase64Url(credential.response.signature);
720
- response.userHandle = !('userHandle' in credential.response) || credential.response.userHandle === null ? null : this.passkeyBufferToBase64Url(credential.response.userHandle);
721
- }
722
- return {
723
- id: credential.id,
724
- rawId: this.passkeyBufferToBase64Url(credential.rawId),
725
- type: credential.type,
726
- response: response,
727
- clientExtensionResults: credential.getClientExtensionResults()
728
- };
729
- }
730
- passkeyBase64UrlToBuffer(value) {
731
- let base64 = value.replace(/-/g, '+').replace(/_/g, '/');
732
- while (base64.length % 4 !== 0) {
733
- base64 += '=';
734
- }
735
- let binary = atob(base64),
736
- bytes = new Uint8Array(binary.length);
737
- for (let i = 0; i < binary.length; i++) {
738
- bytes[i] = binary.charCodeAt(i);
739
- }
740
- return bytes.buffer;
741
- }
742
- passkeyBufferToBase64Url(buffer) {
743
- let binary = '',
744
- bytes = new Uint8Array(buffer);
745
- for (let i = 0; i < bytes.byteLength; i++) {
746
- binary += String.fromCharCode(bytes[i]);
747
- }
748
- return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
749
- }
750
- addLoadingState(state) {
751
- document.documentElement.classList.add('jwtbutler-' + state);
752
- if (state === 'logging-in' || state === 'logging-out') {
753
- document.documentElement.classList.add('jwtbutler-loading');
754
- }
755
- }
756
- removeLoadingStates() {
757
- document.documentElement.classList.remove('jwtbutler-logging-in');
758
- document.documentElement.classList.remove('jwtbutler-logging-out');
759
- document.documentElement.classList.remove('jwtbutler-loading');
760
- document.documentElement.classList.remove('jwtbutler-fetching');
761
- document.documentElement.classList.remove('jwtbutler-login-form-visible');
762
- }
763
- }
764
- exports.default = jwtbutler;
765
- window.jwtbutler = jwtbutler;
1
+ //#region _js/_helpers.js
2
+ var helpers = class {
3
+ static cookieExists(cookie_name) {
4
+ if (document.cookie !== void 0 && this.cookieGet(cookie_name) !== null) return true;
5
+ return false;
6
+ }
7
+ static cookieGet(cookie_name) {
8
+ var cookie_match = document.cookie.match(new RegExp(cookie_name + "=([^;]+)"));
9
+ if (cookie_match) return decodeURIComponent(cookie_match[1]);
10
+ return null;
11
+ }
12
+ static cookieSet(cookie_name, cookie_value, days) {
13
+ let samesite = "";
14
+ if (window.location.protocol.indexOf("https") > -1) samesite = "; SameSite=None; Secure";
15
+ document.cookie = cookie_name + "=" + encodeURIComponent(cookie_value) + "; expires=" + new Date((/* @__PURE__ */ new Date()).getTime() + days * 24 * 60 * 60 * 1e3).toUTCString() + "; path=/" + samesite;
16
+ }
17
+ static cookieDelete(cookie_name) {
18
+ let samesite = "";
19
+ if (window.location.protocol.indexOf("https") > -1) samesite = "; SameSite=None; Secure";
20
+ document.cookie = cookie_name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/" + samesite;
21
+ }
22
+ static remove(el) {
23
+ if (el !== null) el.parentNode.removeChild(el);
24
+ }
25
+ };
26
+ //#endregion
27
+ //#region _js/script.js
28
+ var jwtbutler = class {
29
+ constructor(config) {
30
+ if (!("auth_login" in config)) config.auth_login = "email";
31
+ if (!("captcha" in config)) config.captcha = false;
32
+ if (!("passkeys" in config)) config.passkeys = false;
33
+ if (!("language" in config)) config.language = "en";
34
+ this.config = config;
35
+ }
36
+ isLoggedIn() {
37
+ if (this.getPayload() === null) return false;
38
+ return true;
39
+ }
40
+ getUserId() {
41
+ let payload = this.getPayload();
42
+ if (payload === null) return null;
43
+ return payload.sub;
44
+ }
45
+ getPayload() {
46
+ if (helpers.cookieGet("access_token") === null) return null;
47
+ try {
48
+ return JSON.parse(atob(helpers.cookieGet("access_token").split(".")[1]));
49
+ } catch (e) {
50
+ return null;
51
+ }
52
+ }
53
+ logout() {
54
+ return new Promise((resolve, reject) => {
55
+ this.addLoadingState("logging-out");
56
+ fetch(this.config.auth_server + "/logout", {
57
+ method: "POST",
58
+ headers: {
59
+ "content-type": "application/json",
60
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
61
+ },
62
+ cache: "no-cache"
63
+ }).then((res) => res.json()).catch((err) => err).then((response) => {
64
+ this.setCookies(null).then(() => {
65
+ this.removeLoadingStates();
66
+ resolve();
67
+ }).catch((error) => {
68
+ reject(error);
69
+ });
70
+ });
71
+ });
72
+ }
73
+ passkeyRegister() {
74
+ return new Promise((resolve, reject) => {
75
+ if (this.isLoggedIn() === false || !("credentials" in navigator)) {
76
+ reject();
77
+ return;
78
+ }
79
+ this.addLoadingState("logging-in");
80
+ fetch(this.config.auth_server + "/passkey-register-options", {
81
+ method: "POST",
82
+ headers: {
83
+ "content-type": "application/json",
84
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
85
+ },
86
+ cache: "no-cache"
87
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
88
+ if (response === void 0 || response === null || response.success !== true) {
89
+ this.removeLoadingStates();
90
+ reject(response);
91
+ return;
92
+ }
93
+ let publicKey = this.passkeyPublicKeyFromJson(response.data.publicKey);
94
+ navigator.credentials.create({ publicKey }).then((credential) => {
95
+ if (credential === null) {
96
+ this.removeLoadingStates();
97
+ reject();
98
+ return;
99
+ }
100
+ fetch(this.config.auth_server + "/passkey-register", {
101
+ method: "POST",
102
+ body: JSON.stringify({ credential: this.passkeyCredentialToJson(credential) }),
103
+ headers: {
104
+ "content-type": "application/json",
105
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
106
+ },
107
+ cache: "no-cache"
108
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
109
+ this.removeLoadingStates();
110
+ if (response !== void 0 && response !== null && response.success === true) resolve(response);
111
+ else reject(response);
112
+ });
113
+ }).catch((error) => {
114
+ this.removeLoadingStates();
115
+ reject(error);
116
+ });
117
+ });
118
+ });
119
+ }
120
+ passkeyDelete(id) {
121
+ return new Promise((resolve, reject) => {
122
+ if (this.isLoggedIn() === false) {
123
+ reject();
124
+ return;
125
+ }
126
+ this.addLoadingState("logging-in");
127
+ fetch(this.config.auth_server + "/passkey-delete", {
128
+ method: "POST",
129
+ body: JSON.stringify({ id }),
130
+ headers: {
131
+ "content-type": "application/json",
132
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
133
+ },
134
+ cache: "no-cache"
135
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
136
+ this.removeLoadingStates();
137
+ if (response !== void 0 && response !== null && response.success === true) resolve(response);
138
+ else reject(response);
139
+ });
140
+ });
141
+ }
142
+ passkeyLogin(login = null) {
143
+ return new Promise((resolve, reject) => {
144
+ if (!("credentials" in navigator)) {
145
+ reject();
146
+ return;
147
+ }
148
+ this.addLoadingState("logging-in");
149
+ let body = {};
150
+ if (login !== null && login !== "") body[this.config.auth_login] = login;
151
+ fetch(this.config.auth_server + "/passkey-login-options", {
152
+ method: "POST",
153
+ body: JSON.stringify(body),
154
+ headers: { "content-type": "application/json" },
155
+ cache: "no-cache"
156
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
157
+ if (response === void 0 || response === null || response.success !== true) {
158
+ this.removeLoadingStates();
159
+ reject(response);
160
+ return;
161
+ }
162
+ let publicKey = this.passkeyPublicKeyFromJson(response.data.publicKey);
163
+ navigator.credentials.get({ publicKey }).then((credential) => {
164
+ if (credential === null) {
165
+ this.removeLoadingStates();
166
+ reject();
167
+ return;
168
+ }
169
+ fetch(this.config.auth_server + "/passkey-login", {
170
+ method: "POST",
171
+ body: JSON.stringify({ credential: this.passkeyCredentialToJson(credential) }),
172
+ headers: { "content-type": "application/json" },
173
+ cache: "no-cache"
174
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
175
+ if (response !== void 0 && response !== null && response.success === true) this.setCookies(response.data.access_token).then(() => {
176
+ this.removeLoadingStates();
177
+ resolve(response);
178
+ }).catch((error) => {
179
+ this.removeLoadingStates();
180
+ reject(error);
181
+ });
182
+ else {
183
+ this.removeLoadingStates();
184
+ reject(response);
185
+ }
186
+ });
187
+ }).catch((error) => {
188
+ this.removeLoadingStates();
189
+ reject(error);
190
+ });
191
+ });
192
+ });
193
+ }
194
+ login() {
195
+ return new Promise((resolve, reject) => {
196
+ if (helpers.cookieGet("access_token") !== null) {
197
+ this.addLoadingState("logging-in");
198
+ fetch(this.config.auth_server + "/check", {
199
+ method: "POST",
200
+ body: JSON.stringify({ access_token: helpers.cookieGet("access_token") }),
201
+ headers: { "content-type": "application/json" },
202
+ cache: "no-cache"
203
+ }).then((res) => res.json()).catch((err) => err).then((response) => {
204
+ if (response.success === true) this.setCookies(helpers.cookieGet("access_token")).then(() => {
205
+ this.removeLoadingStates();
206
+ resolve();
207
+ }).catch((error) => {
208
+ reject(error);
209
+ });
210
+ else fetch(this.config.auth_server + "/refresh", {
211
+ method: "POST",
212
+ headers: {
213
+ "content-type": "application/json",
214
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
215
+ },
216
+ cache: "no-cache"
217
+ }).then((res) => res.json()).catch((err) => err).then((response) => {
218
+ if (response.success === true) this.setCookies(response.data.access_token).then(() => {
219
+ this.removeLoadingStates();
220
+ resolve();
221
+ }).catch((error) => {
222
+ reject(error);
223
+ });
224
+ else this.renderLoginFormWithPromise().then(() => {
225
+ resolve();
226
+ });
227
+ });
228
+ });
229
+ } else this.renderLoginFormWithPromise().then(() => {
230
+ resolve();
231
+ }).catch(() => {
232
+ reject();
233
+ });
234
+ });
235
+ }
236
+ fetch(url, args = {}) {
237
+ return new Promise((resolve, reject) => {
238
+ if (!("headers" in args)) args.headers = {};
239
+ if (!("tries" in args)) args.tries = 0;
240
+ args.tries++;
241
+ if (args.tries > 3) {
242
+ reject(null);
243
+ return;
244
+ } else if (this.isLoggedIn() === false) this.login().then(() => {
245
+ this.fetch(url, args).then((response) => {
246
+ resolve(response);
247
+ }).catch((error) => {
248
+ reject(error);
249
+ });
250
+ }).catch((error) => {
251
+ reject(error);
252
+ });
253
+ else {
254
+ this.addLoadingState("fetching");
255
+ args.headers.Authorization = "Bearer " + helpers.cookieGet("access_token");
256
+ fetch(url, args).then((v) => v).catch((v) => v).then((response) => {
257
+ this.removeLoadingStates();
258
+ if (response.status === 401) {
259
+ this.addLoadingState("logging-in");
260
+ fetch(this.config.auth_server + "/refresh", {
261
+ method: "POST",
262
+ headers: {
263
+ "content-type": "application/json",
264
+ Authorization: "Bearer " + helpers.cookieGet("access_token")
265
+ },
266
+ cache: "no-cache"
267
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
268
+ if (response.success === true) this.setCookies(response.data.access_token).then(() => {
269
+ this.removeLoadingStates();
270
+ this.fetch(url, args).then((response) => {
271
+ resolve(response);
272
+ }).catch((error) => {
273
+ reject(error);
274
+ });
275
+ }).catch((error) => {
276
+ this.removeLoadingStates();
277
+ reject(error);
278
+ });
279
+ else {
280
+ this.removeLoadingStates();
281
+ this.renderLoginFormWithPromise().then(() => {
282
+ this.fetch(url, args).then((response) => {
283
+ resolve(response);
284
+ }).catch((error) => {
285
+ reject(error);
286
+ });
287
+ }).catch((error) => {
288
+ reject(error);
289
+ });
290
+ }
291
+ });
292
+ } else resolve(response);
293
+ });
294
+ }
295
+ });
296
+ }
297
+ setCookies(access_token = null) {
298
+ return new Promise((resolve, reject) => {
299
+ if (this.setCookieLoading === void 0) this.setCookieLoading;
300
+ if (this.setCookieLoading === true) {
301
+ resolve();
302
+ return;
303
+ }
304
+ this.setCookieLoading = true;
305
+ if (access_token !== null) helpers.cookieSet("access_token", access_token, 28);
306
+ else helpers.cookieDelete("access_token");
307
+ if (this.config.sso === void 0 || this.config.sso.length === 1 && this.config.sso[0] === window.location.protocol + "//" + window.location.host) {
308
+ this.setCookieLoading = false;
309
+ resolve();
310
+ return;
311
+ }
312
+ helpers.remove(document.querySelector(".iframe_wrapper"));
313
+ let iframe_wrapper = document.createElement("div");
314
+ iframe_wrapper.setAttribute("class", "iframe_wrapper");
315
+ iframe_wrapper.style.position = "absolute";
316
+ iframe_wrapper.style.opacity = "0";
317
+ document.body.appendChild(iframe_wrapper);
318
+ let timeout = null;
319
+ let _this = this, todo = this.config.sso.length - 1, waitForPostMessage = function(e) {
320
+ if (_this.config.sso.indexOf(e.origin) === -1) return;
321
+ if (e.data !== void 0 && e.data !== null && "success" in e.data && e.data.success === true) todo--;
322
+ if (todo <= 0) {
323
+ if (timeout) clearTimeout(timeout);
324
+ window.removeEventListener("message", waitForPostMessage, false);
325
+ helpers.remove(document.querySelector(".iframe_wrapper"));
326
+ _this.setCookieLoading = false;
327
+ resolve();
328
+ }
329
+ };
330
+ window.addEventListener("message", waitForPostMessage, false);
331
+ timeout = setTimeout(() => {
332
+ if (this.setCookieLoading === true) {
333
+ window.removeEventListener("message", waitForPostMessage, false);
334
+ helpers.remove(document.querySelector(".iframe_wrapper"));
335
+ this.setCookieLoading = false;
336
+ resolve();
337
+ }
338
+ }, 2e4);
339
+ this.config.sso.forEach((sso__value) => {
340
+ if (sso__value === window.location.protocol + "//" + window.location.host) return;
341
+ let iframe = document.createElement("iframe");
342
+ iframe.setAttribute("src", sso__value + "/sso.html");
343
+ iframe.style.width = "1px";
344
+ iframe.style.height = "1px";
345
+ iframe.addEventListener("load", (e) => {
346
+ iframe.contentWindow.postMessage({ access_token }, sso__value);
347
+ });
348
+ document.querySelector(".iframe_wrapper").appendChild(iframe);
349
+ });
350
+ });
351
+ }
352
+ renderLoginFormWithPromise() {
353
+ return new Promise((resolve, reject) => {
354
+ this.buildUpLoginFormHtml();
355
+ this.captchaRender().then(() => {
356
+ this.bindLoginFormSubmit().then(() => {
357
+ resolve();
358
+ }).catch((error) => {
359
+ reject(error);
360
+ });
361
+ this.triggerLoginFormRenderedEvent();
362
+ }).catch((error) => {
363
+ reject(error);
364
+ });
365
+ });
366
+ }
367
+ buildUpLoginFormHtml() {
368
+ if (!("login_form" in this.config) || this.config.login_form == "") this.config.login_form = `<div class="login-form">
369
+ <div class="login-form__inner">
370
+ <form class="login-form__form">
371
+ <ul class="login-form__items">
372
+ <li class="login-form__item">
373
+ <label class="login-form__label login-form__label--${this.config.auth_login}" for="login-form__label--${this.config.auth_login}">${this.config.auth_login === "email" ? "E-Mail-Adresse" : this.config.auth_login === "username" ? "Benutzername" : this.config.auth_login}</label>
374
+ <input class="login-form__input login-form__input--${this.config.auth_login}" id="login-form__label--${this.config.auth_login}" type="text" required="required" autocomplete="${this.config.auth_login}" name="${this.config.auth_login}" />
375
+ </li>
376
+ <li class="login-form__item">
377
+ <label class="login-form__label login-form__label--password" for="login-form__label--password">Passwort</label>
378
+ <input class="login-form__input login-form__input--password" id="login-form__label--password" type="password" required="required" autocomplete="current-password" name="password" />
379
+ </li>
380
+ ${this.captchaEnabled() ? `<li class="login-form__item">
381
+ <div class="login-form__captcha"></div>
382
+ </li>` : ""}
383
+ <li class="login-form__item">
384
+ <input class="login-form__submit" type="submit" value="Anmelden" />
385
+ </li>
386
+ ${this.passkeyEnabled() ? `<li class="login-form__item">
387
+ <button class="login-form__passkey" type="button">Mit Passkey anmelden</button>
388
+ </li>` : ""}
389
+ </ul>
390
+ </form>
391
+ </div>
392
+ </div>`;
393
+ let dom = new DOMParser().parseFromString(this.config.login_form, "text/html").body.childNodes[0];
394
+ this.config.login_form_class = dom.getAttribute("class").split(" ")[0];
395
+ let submit = dom.querySelector("input[type=\"submit\"]");
396
+ let submitItem = submit !== null ? submit.closest("." + this.config.login_form_class + "__item") || submit.closest("li") || submit.parentElement : null;
397
+ let itemTag = submitItem !== null && submitItem.tagName.toLowerCase() === "li" ? "li" : "div";
398
+ if (this.captchaEnabled() && dom.querySelector("." + this.config.login_form_class + "__captcha") === null) {
399
+ if (submitItem !== null) submitItem.insertAdjacentHTML("beforebegin", "<" + itemTag + " class=\"" + this.config.login_form_class + "__item\"><div class=\"" + this.config.login_form_class + "__captcha\"></div></" + itemTag + ">");
400
+ }
401
+ if (this.passkeyEnabled() && dom.querySelector("." + this.config.login_form_class + "__passkey") === null) {
402
+ if (submitItem !== null) submitItem.insertAdjacentHTML("afterend", "<" + itemTag + " class=\"" + this.config.login_form_class + "__item\"><button class=\"" + this.config.login_form_class + "__passkey\" type=\"button\">Mit Passkey anmelden</button></" + itemTag + ">");
403
+ }
404
+ helpers.remove(document.querySelector("." + this.config.login_form_class));
405
+ this.addLoadingState("login-form-visible");
406
+ let parent = document.body;
407
+ if ("login_form_parent" in this.config && this.config.login_form_parent != "" && document.querySelector(this.config.login_form_parent) !== null) parent = document.querySelector(this.config.login_form_parent);
408
+ parent.appendChild(dom);
409
+ }
410
+ triggerLoginFormRenderedEvent() {
411
+ if ("login_form_rendered" in this.config && this.config.login_form_rendered != "" && typeof this.config.login_form_rendered === "function") this.config.login_form_rendered(document.querySelector("." + this.config.login_form_class));
412
+ }
413
+ captchaEnabled() {
414
+ return this.config.captcha !== false && typeof this.config.captcha === "object" && (!("provider" in this.config.captcha) || this.captchaProvider() in this.captchaVendors()) && "sitekey" in this.config.captcha && this.config.captcha.sitekey !== "";
415
+ }
416
+ captchaProvider() {
417
+ return this.config.captcha && this.config.captcha.provider || "hcaptcha";
418
+ }
419
+ captchaVendors() {
420
+ return {
421
+ hcaptcha: {
422
+ script: "https://js.hcaptcha.com/1/api.js?render=explicit",
423
+ langKey: "hl",
424
+ field: "h-captcha-response"
425
+ },
426
+ turnstile: {
427
+ script: "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",
428
+ langKey: "language",
429
+ field: "cf-turnstile-response"
430
+ }
431
+ };
432
+ }
433
+ captchaVendor() {
434
+ return this.captchaVendors()[this.captchaProvider()] || this.captchaVendors().hcaptcha;
435
+ }
436
+ captchaApi() {
437
+ return window[this.captchaProvider()];
438
+ }
439
+ passkeyEnabled() {
440
+ return this.config.passkeys !== false;
441
+ }
442
+ responseMessage(response) {
443
+ let messages = {
444
+ en: {
445
+ _default: "Not successful",
446
+ "unknown route": "Unknown route!",
447
+ "captcha not successful": "Captcha not successful",
448
+ "too many login attempts": "Too many login attempts. Please try again later.",
449
+ "auth successful": "Successfully logged in",
450
+ "auth not successful": "Not successful",
451
+ "invalid token": "Invalid token",
452
+ "logout successful": "Successfully logged out",
453
+ "logout not successful": "Logout not successful",
454
+ "valid token": "Valid token",
455
+ "passkey registration options created": "Passkey registration prepared",
456
+ "passkey registration options not created": "Passkey registration not prepared",
457
+ "passkey registered": "Passkey registered",
458
+ "passkey not registered": "Passkey not registered",
459
+ "passkey login options created": "Passkey login prepared",
460
+ "passkey login options not created": "Passkey login not prepared",
461
+ "passkey auth not successful": "Passkey not successful",
462
+ "passkey deleted": "Passkey deleted",
463
+ "passkey not deleted": "Passkey not deleted"
464
+ },
465
+ de: {
466
+ _default: "Nicht erfolgreich",
467
+ "unknown route": "Unbekannte Route!",
468
+ "captcha not successful": "Captcha nicht erfolgreich",
469
+ "too many login attempts": "Zu viele Loginversuche. Bitte später erneut versuchen.",
470
+ "auth successful": "Erfolgreich eingeloggt",
471
+ "auth not successful": "Nicht erfolgreich",
472
+ "invalid token": "Falsches Token",
473
+ "logout successful": "Erfolgreich ausgeloggt",
474
+ "logout not successful": "Nicht erfolgreich ausgeloggt",
475
+ "valid token": "Korrektes Token",
476
+ "passkey registration options created": "Passkey-Registrierung vorbereitet",
477
+ "passkey registration options not created": "Passkey-Registrierung nicht vorbereitet",
478
+ "passkey registered": "Passkey registriert",
479
+ "passkey not registered": "Passkey nicht registriert",
480
+ "passkey login options created": "Passkey-Login vorbereitet",
481
+ "passkey login options not created": "Passkey-Login nicht vorbereitet",
482
+ "passkey auth not successful": "Passkey nicht erfolgreich",
483
+ "passkey deleted": "Passkey gelöscht",
484
+ "passkey not deleted": "Passkey nicht gelöscht"
485
+ }
486
+ };
487
+ let language = this.config.language in messages ? this.config.language : "en";
488
+ if (response !== void 0 && response !== null && "message" in response && response.message in messages[language]) return messages[language][response.message];
489
+ if (response !== void 0 && response !== null && "public_message" in response) return response.public_message;
490
+ return messages[language]._default;
491
+ }
492
+ captchaRender() {
493
+ return new Promise((resolve, reject) => {
494
+ if (!this.captchaEnabled()) {
495
+ resolve();
496
+ return;
497
+ }
498
+ this.captchaLoad().then(() => {
499
+ let captcha = document.querySelector("." + this.config.login_form_class + "__captcha");
500
+ if (captcha === null) {
501
+ resolve();
502
+ return;
503
+ }
504
+ if (captcha.getAttribute("data-widget-id") !== null) {
505
+ resolve();
506
+ return;
507
+ }
508
+ let options = {
509
+ sitekey: this.config.captcha.sitekey,
510
+ theme: this.config.captcha.theme || "light"
511
+ };
512
+ if (this.config.language !== "") options[this.captchaVendor().langKey] = this.config.language;
513
+ captcha.setAttribute("data-widget-id", this.captchaApi().render(captcha, options));
514
+ resolve();
515
+ }).catch((error) => {
516
+ reject(error);
517
+ });
518
+ });
519
+ }
520
+ captchaLoad() {
521
+ return new Promise((resolve, reject) => {
522
+ if (!this.captchaEnabled() || this.captchaProvider() in window) {
523
+ resolve();
524
+ return;
525
+ }
526
+ let selector = "script[data-jwtbutler-captcha=\"" + this.captchaProvider() + "\"]";
527
+ if (document.querySelector(selector) !== null) {
528
+ document.querySelector(selector).addEventListener("load", () => resolve());
529
+ document.querySelector(selector).addEventListener("error", (error) => reject(error));
530
+ return;
531
+ }
532
+ let script = document.createElement("script");
533
+ script.setAttribute("src", this.captchaVendor().script);
534
+ script.setAttribute("async", "async");
535
+ script.setAttribute("defer", "defer");
536
+ script.setAttribute("data-jwtbutler-captcha", this.captchaProvider());
537
+ script.addEventListener("load", () => resolve());
538
+ script.addEventListener("error", (error) => reject(error));
539
+ document.head.appendChild(script);
540
+ });
541
+ }
542
+ captchaToken(form) {
543
+ return new Promise((resolve, reject) => {
544
+ if (!this.captchaEnabled()) {
545
+ resolve(null);
546
+ return;
547
+ }
548
+ let captcha = form.querySelector("." + this.config.login_form_class + "__captcha");
549
+ if (captcha === null || !(this.captchaProvider() in window)) {
550
+ reject({
551
+ message: "captcha not successful",
552
+ public_message: "Captcha not successful"
553
+ });
554
+ return;
555
+ }
556
+ let token = this.captchaApi().getResponse(captcha.getAttribute("data-widget-id"));
557
+ if (token === "") {
558
+ reject({
559
+ message: "captcha not successful",
560
+ public_message: "Captcha not successful"
561
+ });
562
+ return;
563
+ }
564
+ resolve(token);
565
+ });
566
+ }
567
+ captchaReset(form) {
568
+ if (!this.captchaEnabled() || !(this.captchaProvider() in window)) return;
569
+ let captcha = form.querySelector("." + this.config.login_form_class + "__captcha");
570
+ if (captcha === null || captcha.getAttribute("data-widget-id") === null) return;
571
+ this.captchaApi().reset(captcha.getAttribute("data-widget-id"));
572
+ }
573
+ bindLoginFormSubmit() {
574
+ return new Promise((resolve, reject) => {
575
+ let dom = document.querySelector("." + this.config.login_form_class), form = dom.querySelector("form");
576
+ form.addEventListener("submit", (e) => {
577
+ this.addLoadingState("logging-in");
578
+ if (form.querySelector("input[type=\"submit\"]") !== null) form.querySelector("input[type=\"submit\"]").disabled = true;
579
+ helpers.remove(dom.querySelector("." + this.config.login_form_class + "__error"));
580
+ let body = {
581
+ [this.config.auth_login]: form.querySelector("input[name=\"" + this.config.auth_login + "\"]").value,
582
+ password: form.querySelector("input[name=\"password\"]").value
583
+ };
584
+ this.captchaToken(form).then((captchaToken) => {
585
+ if (captchaToken !== null) body[this.captchaVendor().field] = captchaToken;
586
+ return fetch(this.config.auth_server + "/login", {
587
+ method: "POST",
588
+ body: JSON.stringify(body),
589
+ headers: { "content-type": "application/json" },
590
+ cache: "no-cache"
591
+ });
592
+ }).then((res) => res.json()).catch((error) => error).then((response) => {
593
+ if (form.querySelector("input[type=\"submit\"]") !== null) form.querySelector("input[type=\"submit\"]").disabled = false;
594
+ if (response !== void 0 && response !== null && "success" in response && response.success === true) {
595
+ helpers.remove(document.querySelector("." + this.config.login_form_class));
596
+ this.setCookies(response.data.access_token).then(() => {
597
+ this.removeLoadingStates();
598
+ resolve();
599
+ }).catch((error) => {
600
+ reject(error);
601
+ });
602
+ } else {
603
+ this.removeLoadingStates();
604
+ this.addLoadingState("login-form-visible");
605
+ this.captchaReset(form);
606
+ form.insertAdjacentHTML("afterbegin", "<div class=\"" + this.config.login_form_class + "__error\">" + this.responseMessage(response) + "</div>");
607
+ }
608
+ });
609
+ e.preventDefault();
610
+ }, false);
611
+ if (form.querySelector("." + this.config.login_form_class + "__passkey") !== null) form.querySelector("." + this.config.login_form_class + "__passkey").addEventListener("click", (e) => {
612
+ this.addLoadingState("logging-in");
613
+ helpers.remove(dom.querySelector("." + this.config.login_form_class + "__error"));
614
+ let login = null;
615
+ if (form.querySelector("input[name=\"" + this.config.auth_login + "\"]") !== null) login = form.querySelector("input[name=\"" + this.config.auth_login + "\"]").value;
616
+ this.passkeyLogin(login).then(() => {
617
+ helpers.remove(document.querySelector("." + this.config.login_form_class));
618
+ this.removeLoadingStates();
619
+ resolve();
620
+ }).catch((response) => {
621
+ this.removeLoadingStates();
622
+ this.addLoadingState("login-form-visible");
623
+ form.insertAdjacentHTML("afterbegin", "<div class=\"" + this.config.login_form_class + "__error\">" + this.responseMessage(response) + "</div>");
624
+ });
625
+ e.preventDefault();
626
+ }, false);
627
+ });
628
+ }
629
+ passkeyPublicKeyFromJson(publicKey) {
630
+ publicKey.challenge = this.passkeyBase64UrlToBuffer(publicKey.challenge);
631
+ if ("user" in publicKey && "id" in publicKey.user) publicKey.user.id = this.passkeyBase64UrlToBuffer(publicKey.user.id);
632
+ if ("excludeCredentials" in publicKey) publicKey.excludeCredentials.forEach((credential) => {
633
+ credential.id = this.passkeyBase64UrlToBuffer(credential.id);
634
+ });
635
+ if ("allowCredentials" in publicKey) publicKey.allowCredentials.forEach((credential) => {
636
+ credential.id = this.passkeyBase64UrlToBuffer(credential.id);
637
+ });
638
+ return publicKey;
639
+ }
640
+ passkeyCredentialToJson(credential) {
641
+ let response = { clientDataJSON: this.passkeyBufferToBase64Url(credential.response.clientDataJSON) };
642
+ if ("attestationObject" in credential.response) {
643
+ response.attestationObject = this.passkeyBufferToBase64Url(credential.response.attestationObject);
644
+ if (typeof credential.response.getTransports === "function") response.transports = credential.response.getTransports();
645
+ }
646
+ if ("authenticatorData" in credential.response) {
647
+ response.authenticatorData = this.passkeyBufferToBase64Url(credential.response.authenticatorData);
648
+ response.signature = this.passkeyBufferToBase64Url(credential.response.signature);
649
+ response.userHandle = !("userHandle" in credential.response) || credential.response.userHandle === null ? null : this.passkeyBufferToBase64Url(credential.response.userHandle);
650
+ }
651
+ return {
652
+ id: credential.id,
653
+ rawId: this.passkeyBufferToBase64Url(credential.rawId),
654
+ type: credential.type,
655
+ response,
656
+ clientExtensionResults: credential.getClientExtensionResults()
657
+ };
658
+ }
659
+ passkeyBase64UrlToBuffer(value) {
660
+ let base64 = value.replace(/-/g, "+").replace(/_/g, "/");
661
+ while (base64.length % 4 !== 0) base64 += "=";
662
+ let binary = atob(base64), bytes = new Uint8Array(binary.length);
663
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
664
+ return bytes.buffer;
665
+ }
666
+ passkeyBufferToBase64Url(buffer) {
667
+ let binary = "", bytes = new Uint8Array(buffer);
668
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
669
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
670
+ }
671
+ addLoadingState(state) {
672
+ document.documentElement.classList.add("jwtbutler-" + state);
673
+ if (state === "logging-in" || state === "logging-out") document.documentElement.classList.add("jwtbutler-loading");
674
+ }
675
+ removeLoadingStates() {
676
+ document.documentElement.classList.remove("jwtbutler-logging-in");
677
+ document.documentElement.classList.remove("jwtbutler-logging-out");
678
+ document.documentElement.classList.remove("jwtbutler-loading");
679
+ document.documentElement.classList.remove("jwtbutler-fetching");
680
+ document.documentElement.classList.remove("jwtbutler-login-form-visible");
681
+ }
682
+ };
683
+ if (typeof window !== "undefined") window.jwtbutler = jwtbutler;
684
+ //#endregion
685
+ module.exports = jwtbutler;