@wral/studio.mods.auth 0.3.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -47
- package/bitbucket-pipelines.yml +25 -1
- package/dist/auth.cjs.js +326 -1467
- package/dist/auth.es.js +1081 -3093
- package/dist/lib.cjs.js +1 -1
- package/dist/lib.es.js +13 -7
- package/eslint.config.mjs +41 -34
- package/index.html +83 -18
- package/jest.config.mjs +24 -0
- package/jest.setup.mjs +5 -0
- package/package.json +15 -28
- package/src/auth.mjs +204 -69
- package/src/auth.test.mjs +97 -0
- package/src/components/auth-app.mjs +26 -0
- package/src/components/forgot-password-form.mjs +217 -0
- package/src/components/login-form.mjs +288 -0
- package/src/config.mjs +27 -0
- package/src/helper.mjs +31 -0
- package/src/helper.test.mjs +44 -0
- package/src/index.mjs +17 -0
- package/src/login-layout.mjs +32 -0
- package/src/login.mjs +20 -0
- package/src/routes/change-password.mjs +158 -0
- package/src/routes/dashboard.mjs +17 -0
- package/src/routes/index.mjs +15 -0
- package/src/state.mjs +61 -0
- package/src/state.test.mjs +58 -0
- package/src/styles.mjs +9 -0
- package/src/token.mjs +40 -0
- package/src/utils.mjs +3 -0
- package/vellum-fixture.mjs +86 -0
- package/vite.config.mjs +12 -0
- package/components.html +0 -43
- package/development.md +0 -41
- package/src/components/mod-auth-login-form.mjs +0 -133
- package/src/components/studio-change-password.mjs +0 -84
- package/src/components/studio-login.mjs +0 -94
- package/src/components/studio-profile-view.mjs +0 -56
- package/src/components/studio-reset-password.mjs +0 -110
- package/src/lib.mjs +0 -16
- package/src/tool-dummy.mjs +0 -84
- package/src/util.mjs +0 -194
- package/src/util.test.mjs +0 -171
- package/vite.config.js +0 -12
- package/web-test-runner.config.mjs +0 -28
package/dist/lib.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(e){return new Promise((t,n)=>{(!e||!e.dispatchEvent)&&n(new Error("getToken must be called with a DOM element")),e.dispatchEvent(new CustomEvent("harness:action",{detail:{type:"auth:requestToken",detail:{callback:t}},bubbles:!0,composed:!0,cancelable:!0}))})}exports.getToken=o;
|
package/dist/lib.es.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
function a(
|
|
2
|
-
return new Promise((
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
function a(e) {
|
|
2
|
+
return new Promise((t, n) => {
|
|
3
|
+
(!e || !e.dispatchEvent) && n(new Error("getToken must be called with a DOM element")), e.dispatchEvent(new CustomEvent("harness:action", {
|
|
4
|
+
detail: {
|
|
5
|
+
type: "auth:requestToken",
|
|
6
|
+
detail: {
|
|
7
|
+
callback: t
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
bubbles: !0,
|
|
11
|
+
composed: !0,
|
|
12
|
+
cancelable: !0
|
|
13
|
+
}));
|
|
8
14
|
});
|
|
9
15
|
}
|
|
10
16
|
export {
|
package/eslint.config.mjs
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import globals from 'globals';
|
|
3
|
+
import jest from 'eslint-plugin-jest';
|
|
4
|
+
|
|
1
5
|
export default [
|
|
6
|
+
|
|
7
|
+
// Base configuration
|
|
8
|
+
js.configs.recommended,
|
|
9
|
+
|
|
10
|
+
// Language options and globals
|
|
2
11
|
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
],
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
ecmaVersion: 2021, // Latest ECMAScript features
|
|
14
|
+
sourceType: 'module', // Enable ES6 modules
|
|
15
|
+
globals: {
|
|
16
|
+
...globals.browser, // Browser globals
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
files: ['src/**/*.mjs'],
|
|
23
|
+
ignores: ['dist/**', 'node_modules/**','coverage/**'],
|
|
24
|
+
rules: {
|
|
25
|
+
indent: ['error', 'tab'], // Use tabs for indentation
|
|
26
|
+
'linebreak-style': ['error', 'unix'], // Enforce Unix-style linebreaks
|
|
27
|
+
quotes: ['error', 'single'], // Prefer single quotes
|
|
28
|
+
semi: ['error', 'always'], // Enforce semicolons
|
|
29
|
+
'max-len': ['error', { code: 90, tabWidth: 2 }], // Limit line length
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Jest-specific configuration for test files
|
|
34
|
+
{
|
|
35
|
+
files: ['**/*.test.mjs'], // Target Jest test files
|
|
36
|
+
plugins: {
|
|
37
|
+
jest, // Use the Jest plugin
|
|
38
|
+
},
|
|
39
|
+
rules: jest.configs.recommended.rules, // Apply recommended Jest rules
|
|
40
|
+
languageOptions: {
|
|
41
|
+
globals: {
|
|
42
|
+
...globals.jest, // Jest-specific globals
|
|
43
|
+
},
|
|
37
44
|
},
|
|
38
45
|
},
|
|
39
46
|
];
|
package/index.html
CHANGED
|
@@ -1,20 +1,85 @@
|
|
|
1
|
+
<!-- src/index.html -->
|
|
1
2
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>WRAL.studio</title>
|
|
8
|
+
<script type="module" src="/vellum-fixture.mjs"></script>
|
|
9
|
+
<meta name="robots" content="noindex, nofollow">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<style>
|
|
13
|
+
/*
|
|
14
|
+
TODO: move these styles into a theme
|
|
15
|
+
*/
|
|
16
|
+
html {
|
|
17
|
+
--spacing-sm: 0.5rem;
|
|
18
|
+
--spacing-md: 1rem;
|
|
19
|
+
--spacing-lg: 2rem;
|
|
20
|
+
--spacing-xl: 3rem;
|
|
21
|
+
|
|
22
|
+
--radius-sm: 5px;
|
|
23
|
+
--radius-md: 8px;
|
|
24
|
+
--radius-lg: 10px;
|
|
25
|
+
--radius-full: 50%;
|
|
26
|
+
--radius-dynamic: clamp(var(--radius-sm), 1vw, var(--radius-lg));
|
|
27
|
+
|
|
28
|
+
--color-primary: #001D68;
|
|
29
|
+
--color-primary-light: #193377;
|
|
30
|
+
--color-primary-dark: #00144A;
|
|
31
|
+
--color-secondary: #2594E3;
|
|
32
|
+
--color-secondary-light: #46A4E7;
|
|
33
|
+
--color-secondary-light-1: #c3d6e3;
|
|
34
|
+
--color-secondary-dark: #1D76B5;
|
|
35
|
+
--color-error: var(--color-red-dark);
|
|
36
|
+
--color-red: #D1232A;
|
|
37
|
+
--color-red-light: #FF2B32;
|
|
38
|
+
--color-red-dark: #D1232A;
|
|
39
|
+
--color-yellow: #FFEC19;
|
|
40
|
+
--color-yellow-light: #FFF15E;
|
|
41
|
+
--color-yellow-dark: #E5D416;
|
|
42
|
+
--color-green: #72B509;
|
|
43
|
+
--color-green-light: #8EC33A;
|
|
44
|
+
--color-green-dark: #66A208;
|
|
45
|
+
--color-purple: #33109C;
|
|
46
|
+
--color-purple-light: #4727A5;
|
|
47
|
+
--color-purple-dark: #280C7C;
|
|
48
|
+
--color-orange: #FF9505;
|
|
49
|
+
--color-orange-light: #FFA21F;
|
|
50
|
+
--color-orange-dark: #F58F00;
|
|
51
|
+
--color-white: #FFFFFF;
|
|
52
|
+
--color-black: #030711;
|
|
53
|
+
--color-gray-1: #F7F5F4;
|
|
54
|
+
--color-gray-2: #E7E5E4;
|
|
55
|
+
--color-gray-3: #D7D4D2;
|
|
56
|
+
--color-gray-4: #7C7C7C;
|
|
57
|
+
--color-gray-5: #6C6C6C;
|
|
58
|
+
--color-gray-6: #565454;
|
|
59
|
+
--color-gray-7: #424242;
|
|
60
|
+
--color-gray-8: #263238;
|
|
61
|
+
--color-gray-9: #1C2321;
|
|
62
|
+
|
|
63
|
+
--font-heading: Inter, sans-serif;
|
|
64
|
+
--font-body: Arial, sans-serif;
|
|
65
|
+
--font-size-base: 16px;
|
|
66
|
+
--color-text: var(--color-gray-9);
|
|
67
|
+
|
|
68
|
+
--drop-shadow-md: 0 0 10px var(--color-gray-3);
|
|
69
|
+
}
|
|
70
|
+
html, body {
|
|
71
|
+
margin: 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
box-sizing: border-box;
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
76
|
+
<studio-app>
|
|
77
|
+
<vellum-mod name="setup" src="/vellum-fixture.mjs"></vellum-mod>
|
|
78
|
+
|
|
79
|
+
<vellum-mod name="auth" src="/src/index.mjs"
|
|
80
|
+
api="https://api.wral.com/auth"
|
|
81
|
+
force-login></vellum-mod>
|
|
82
|
+
|
|
83
|
+
</studio-app>
|
|
84
|
+
</body>
|
|
20
85
|
</html>
|
package/jest.config.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
|
|
3
|
+
setupFiles: ['<rootDir>/jest.setup.mjs'],
|
|
4
|
+
|
|
5
|
+
// Use jsdom for a browser-like testing environment
|
|
6
|
+
testEnvironment: 'jsdom',
|
|
7
|
+
|
|
8
|
+
// Look for tests ending with .test.mjs
|
|
9
|
+
testMatch: ['**/*.test.mjs'],
|
|
10
|
+
|
|
11
|
+
// Collect code coverage from files in /src
|
|
12
|
+
collectCoverage: true,
|
|
13
|
+
collectCoverageFrom: ['src/**/*.{js,mjs}'],
|
|
14
|
+
|
|
15
|
+
// Set coverage thresholds to 0 (TODO: bring them up to 100%)
|
|
16
|
+
coverageThreshold: {
|
|
17
|
+
global: {
|
|
18
|
+
branches: 0, // TODO: bring up to 100%
|
|
19
|
+
functions: 0, // TODO: bring up to 100%
|
|
20
|
+
lines: 0, // TODO: bring up to 100%
|
|
21
|
+
statements: 0, // TODO: bring up to 100%
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
package/jest.setup.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wral/studio.mods.auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Auth mod for Studio",
|
|
5
|
-
"main": "dist/
|
|
6
|
-
"module": "dist/
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Auth mod for Studio/Vellum",
|
|
5
|
+
"main": "dist/auth.cjs.js",
|
|
6
|
+
"module": "dist/auth.es.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"prepare": "husky",
|
|
9
|
-
"lint": "eslint . --config eslint.config.mjs",
|
|
10
|
-
"update:modules": "npx npm-check-updates --interactive",
|
|
11
|
-
"build": "vite build",
|
|
12
8
|
"dev": "vite",
|
|
13
|
-
"test": "
|
|
9
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"build": "vite build"
|
|
14
12
|
},
|
|
15
13
|
"repository": {
|
|
16
14
|
"type": "git",
|
|
17
|
-
"url": "git+https://bitbucket.org/cbcnm/studio.mods
|
|
15
|
+
"url": "git+https://bitbucket.org/cbcnm/studio.mods/auth.git"
|
|
18
16
|
},
|
|
19
17
|
"author": "Kenneth Barbour <kbarbour@wral.com>",
|
|
20
18
|
"license": "UNLICENSED",
|
|
21
|
-
"bugs": {
|
|
22
|
-
"url": "https://bitbucket.org/cbcnm/studio.mods.auth/issues"
|
|
23
|
-
},
|
|
24
|
-
"homepage": "https://bitbucket.org/cbcnm/studio.mods.auth#readme",
|
|
25
19
|
"dependencies": {
|
|
26
|
-
"@shoelace-style/shoelace": "^2.16.0",
|
|
27
20
|
"@wral/sdk-auth": "^0.2.1",
|
|
28
|
-
"
|
|
29
|
-
"@wral/studio-ui": "^2.4.8",
|
|
30
|
-
"lit": "^3.2.0"
|
|
21
|
+
"lit": "^3.2.1"
|
|
31
22
|
},
|
|
32
23
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"husky": "^9.1.5",
|
|
40
|
-
"playwright": "^1.47.0",
|
|
41
|
-
"sinon": "^18.0.1",
|
|
42
|
-
"vite": "^5.4.4"
|
|
24
|
+
"@thefarce/vellum": "^0.4.2",
|
|
25
|
+
"eslint": "^9.22.0",
|
|
26
|
+
"eslint-plugin-jest": "^28.11.0",
|
|
27
|
+
"jest": "^29.7.0",
|
|
28
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
29
|
+
"vite": "^6.2.1"
|
|
43
30
|
}
|
|
44
31
|
}
|
package/src/auth.mjs
CHANGED
|
@@ -1,73 +1,208 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { imply } from './utils.mjs';
|
|
2
|
+
import { loginLayout } from './login-layout.mjs';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import { shouldRefreshToken } from './token.mjs';
|
|
5
|
+
import { pathnameToState } from './state.mjs';
|
|
6
|
+
import { createClient } from '@wral/sdk-auth';
|
|
7
|
+
import './components/auth-app.mjs';
|
|
8
|
+
|
|
9
|
+
export function makeAuth(config) {
|
|
10
|
+
const auth = {
|
|
11
|
+
config,
|
|
12
|
+
log: (...args) => console.log('[auth]', ...args),
|
|
13
|
+
};
|
|
14
|
+
auth.mount = imply(mount, auth);
|
|
15
|
+
auth.handleTokenRequest = tokenRequestHandler(auth);
|
|
16
|
+
auth.handleDestroyAuth = loginDestroyHandler(auth);
|
|
17
|
+
auth.presentLoginForm = imply(presentLoginForm, auth);
|
|
18
|
+
auth.getToken = imply(getToken, auth);
|
|
19
|
+
auth.saveToken = imply(saveToken, auth);
|
|
20
|
+
auth.getFreshToken = imply(getFreshToken, auth);
|
|
21
|
+
auth.requestRender = (state) => renderApp(auth, state);
|
|
22
|
+
|
|
23
|
+
return auth;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Call mount when initializing the mod for the first time.
|
|
28
|
+
* It registers handlers with the vellum toolkit.
|
|
29
|
+
* @param {Object} auth
|
|
30
|
+
*/
|
|
31
|
+
export function mount(auth) {
|
|
32
|
+
const windowElem = auth.config.toolkit.element.ownerDocument.defaultView;
|
|
33
|
+
[{
|
|
34
|
+
type: 'action:register',
|
|
35
|
+
detail: {
|
|
36
|
+
actionType: 'auth:requestToken',
|
|
37
|
+
handler: auth.handleTokenRequest,
|
|
38
|
+
modName: 'auth',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'action:register',
|
|
43
|
+
detail: {
|
|
44
|
+
actionType: 'auth:destroy',
|
|
45
|
+
handler: auth.handleDestroyAuth,
|
|
46
|
+
modName: 'auth',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'layout:register',
|
|
51
|
+
detail: {
|
|
52
|
+
name: 'auth:login',
|
|
53
|
+
slots: ['main'],
|
|
54
|
+
templateFn: loginLayout(auth),
|
|
55
|
+
},
|
|
56
|
+
}].forEach(action => auth.config.toolkit.dispatchAction(action));
|
|
57
|
+
windowElem.addEventListener('popstate', historyStateHandler(auth));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function renderApp(auth, state) {
|
|
61
|
+
const windowElem = auth.config.toolkit.element.ownerDocument.defaultView;
|
|
62
|
+
let appState = state;
|
|
63
|
+
if (!appState) {
|
|
64
|
+
appState = pathnameToState(auth, windowElem.location.pathname);
|
|
65
|
+
}
|
|
66
|
+
if (!appState) {
|
|
67
|
+
return; // nothing to do
|
|
68
|
+
}
|
|
69
|
+
auth.config.toolkit.dispatchAction({
|
|
70
|
+
type: 'layout:slot:replace',
|
|
71
|
+
detail: {
|
|
72
|
+
layout: 'menu-main-task-layout',
|
|
73
|
+
slot: 'main',
|
|
74
|
+
content: html`<auth-app .state=${appState} .auth=${auth}></auth-app>`,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns an event handler for window.onpopstate.
|
|
81
|
+
* @param {Object} auth
|
|
82
|
+
* @returns {Function}
|
|
83
|
+
*/
|
|
84
|
+
export function historyStateHandler(auth) {
|
|
85
|
+
return (event) => {
|
|
86
|
+
auth.log('onpopstate', event);
|
|
87
|
+
renderApp(auth);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
2
90
|
|
|
3
91
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
* module is registered.
|
|
11
|
-
* @param {Object} config - Configuration object for the initialization
|
|
12
|
-
* (currently not used in the function).
|
|
92
|
+
* Generate a handler for token requests.
|
|
93
|
+
* This handler should attempt to resolve to a fresh token, or reject.
|
|
94
|
+
* If the currently held token is not fresh, the handler should present the
|
|
95
|
+
* login form to the user.
|
|
96
|
+
* @param {Object} auth
|
|
97
|
+
* @returns {Function}
|
|
13
98
|
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
export function tokenRequestHandler(auth) {
|
|
100
|
+
let callbacks = [];
|
|
101
|
+
let isLoginPresent = false;
|
|
102
|
+
|
|
103
|
+
const onLogin = token => {
|
|
104
|
+
callbacks.forEach(callback => {
|
|
105
|
+
callback(token);
|
|
106
|
+
});
|
|
107
|
+
callbacks = [];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return async ({ callback }) => {
|
|
111
|
+
let token;
|
|
112
|
+
try {
|
|
113
|
+
token = await auth.getFreshToken();
|
|
114
|
+
callback(token);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
callbacks.push(callback);
|
|
117
|
+
if (!isLoginPresent) {
|
|
118
|
+
auth.presentLoginForm({ onLogin });
|
|
119
|
+
isLoginPresent = true;
|
|
120
|
+
}
|
|
121
|
+
auth.log(err);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function loginDestroyHandler(auth) {
|
|
127
|
+
return () => {
|
|
128
|
+
const storageKey = getTokenStorageKey(auth);
|
|
129
|
+
window.localStorage.removeItem(storageKey);
|
|
130
|
+
auth.config.toolkit.dispatchAction({
|
|
131
|
+
type: 'layout:pop',
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Present the login form to the user.
|
|
138
|
+
* This also handles the dismissal of the login form.
|
|
139
|
+
* @param {Object} auth
|
|
140
|
+
* @param {Function} onLogin
|
|
141
|
+
*/
|
|
142
|
+
export function presentLoginForm(auth, { onLogin }) {
|
|
143
|
+
const { toolkit } = auth.config;
|
|
144
|
+
const urlParams = new URLSearchParams(
|
|
145
|
+
toolkit.element.ownerDocument.defaultView.location.search);
|
|
146
|
+
const passwordResetConfirmation = urlParams.get('password_reset_confirmation');
|
|
147
|
+
|
|
148
|
+
// After the login form is dismissed, restore the previous layout
|
|
149
|
+
const dismiss = (event) => {
|
|
150
|
+
auth.log('login success', { event });
|
|
151
|
+
const { token } = event.detail;
|
|
152
|
+
auth.log('Login form dismissed', { args: [token] });
|
|
153
|
+
toolkit.dispatchAction({
|
|
154
|
+
type: 'layout:pop',
|
|
155
|
+
});
|
|
156
|
+
auth.saveToken({token});
|
|
157
|
+
onLogin({ token });
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
toolkit.dispatchAction({
|
|
161
|
+
type: 'layout:push',
|
|
162
|
+
detail: {
|
|
163
|
+
layout: 'auth:login',
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
toolkit.dispatchAction({
|
|
167
|
+
type: 'layout:slot:push',
|
|
168
|
+
detail: {
|
|
169
|
+
layout: 'auth:login',
|
|
170
|
+
slot: 'main',
|
|
171
|
+
content: html`<auth-login-form
|
|
172
|
+
api=${auth.config.api}
|
|
173
|
+
confirmation="${passwordResetConfirmation}"
|
|
174
|
+
@login-success=${dismiss}>
|
|
175
|
+
</auth-login-form>`,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function saveToken(auth, { token }) {
|
|
181
|
+
auth.log('saving token', { args: [token] });
|
|
182
|
+
const key = getTokenStorageKey(auth);
|
|
183
|
+
window.localStorage.setItem(key, token);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function getToken(auth) {
|
|
187
|
+
const key = getTokenStorageKey(auth);
|
|
188
|
+
return window.localStorage.getItem(key);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function getFreshToken(auth) {
|
|
192
|
+
const token = getToken(auth);
|
|
193
|
+
if (!token || token.length < 1) {
|
|
194
|
+
throw new Error('No token found');
|
|
195
|
+
}
|
|
196
|
+
if (shouldRefreshToken(token)) {
|
|
197
|
+
auth.log('refreshing token', { args: [token] });
|
|
198
|
+
const client = createClient({
|
|
199
|
+
baseUrl: auth.config.api,
|
|
200
|
+
});
|
|
201
|
+
return await client.refreshToken({ token }).then(({ token }) => token);
|
|
202
|
+
}
|
|
203
|
+
return token;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function getTokenStorageKey({ storageKey='token' }) {
|
|
207
|
+
return storageKey;
|
|
73
208
|
}
|