@shokirovr16/frontend-library 0.1.2
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/LICENSE +21 -0
- package/README.md +98 -0
- package/bin/cmfrt.js +69 -0
- package/package.json +47 -0
- package/src/auth/README.md +193 -0
- package/src/auth/core/AuthEngine.js +623 -0
- package/src/auth/core/OidcClient.js +79 -0
- package/src/auth/core/OidcDiscovery.js +17 -0
- package/src/auth/core/Pkce.js +18 -0
- package/src/auth/events/AuthEventBus.js +22 -0
- package/src/auth/http/authFetch.js +32 -0
- package/src/auth/http/createAuthHttpClient.js +42 -0
- package/src/auth/index.js +90 -0
- package/src/auth/permissions/ClaimsNormalizer.js +69 -0
- package/src/auth/permissions/permissions.js +26 -0
- package/src/auth/react/AuthProvider.js +34 -0
- package/src/auth/react/guards/RequireAuth.js +35 -0
- package/src/auth/react/guards/RequirePermission.js +16 -0
- package/src/auth/react/guards/withAuthGuard.js +12 -0
- package/src/auth/react/hooks/useRequireAuth.js +24 -0
- package/src/auth/react/index.js +6 -0
- package/src/auth/react/useAuth.js +29 -0
- package/src/auth/silent/silentCallback.js +42 -0
- package/src/auth/singleton.js +22 -0
- package/src/auth/storage/InMemoryTokenStore.js +56 -0
- package/src/auth/storage/TransactionStore.js +51 -0
- package/src/auth/sync/BroadcastChannelSync.js +29 -0
- package/src/auth/tenancy/TenantResolver.js +39 -0
- package/src/auth/types.js +113 -0
- package/src/auth/utils/base64url.js +15 -0
- package/src/auth/utils/jwt.js +26 -0
- package/src/auth/utils/random.js +13 -0
- package/src/auth/utils/url.js +27 -0
- package/src/commands/add.js +80 -0
- package/src/commands/init.js +113 -0
- package/src/commands/list.js +92 -0
- package/src/commands/remove.js +150 -0
- package/src/commands/status.js +96 -0
- package/src/commands/theme.js +47 -0
- package/src/commands/uninstall.js +198 -0
- package/src/commands/update.js +151 -0
- package/src/lib/config.js +55 -0
- package/src/lib/fs.js +13 -0
- package/src/lib/packageManager.js +30 -0
- package/src/lib/paths.js +14 -0
- package/src/lib/registry.js +11 -0
- package/src/lib/styles.js +223 -0
- package/src/lib/targets.js +15 -0
- package/src/lib/theme.js +102 -0
- package/templates/docs/cmfrt-doc.md +82 -0
- package/templates/lib/utils.js +6 -0
- package/templates/registry.json +42 -0
- package/templates/styles/theme.cjs +832 -0
- package/templates/styles/type-utilities.css +136 -0
- package/templates/styles/type-utility-classes.css +138 -0
- package/templates/styles/variables.css +1560 -0
- package/templates/styles/variables.json +6870 -0
- package/templates/ui/button.jsx +117 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mirjalolbek Qosimov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# @shokirovr16/frontend-library
|
|
2
|
+
|
|
3
|
+
Shared frontend package for:
|
|
4
|
+
|
|
5
|
+
- browser auth helpers used by the support/operator frontends
|
|
6
|
+
- CLI tooling for Comfort UI component bootstrap and maintenance
|
|
7
|
+
|
|
8
|
+
## Package Identity
|
|
9
|
+
|
|
10
|
+
- npm package name: `@shokirovr16/frontend-library`
|
|
11
|
+
- auth exports: `@shokirovr16/frontend-library/auth`, `@shokirovr16/frontend-library/auth/react`
|
|
12
|
+
- CLI aliases: `frontend-library`, `cmfrt`
|
|
13
|
+
- generated config/doc filenames still remain `cmfrt.json` and `cmfrt-doc.md`
|
|
14
|
+
|
|
15
|
+
Important:
|
|
16
|
+
|
|
17
|
+
- this repository is now published and consumed as `@shokirovr16/frontend-library`
|
|
18
|
+
- the old public `cmfrt` package is not the source of truth for this repository anymore
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @shokirovr16/frontend-library
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Peer dependencies:
|
|
27
|
+
|
|
28
|
+
- `react >= 18`
|
|
29
|
+
- `react-router-dom >= 6`
|
|
30
|
+
- `axios >= 1` for the axios helper only
|
|
31
|
+
|
|
32
|
+
## Auth Usage
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { initializeAuth } from '@shokirovr16/frontend-library/auth';
|
|
36
|
+
import { AuthProvider } from '@shokirovr16/frontend-library/auth/react';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See [src/auth/README.md](D:/MIO%20BEAUTY%20ERP%202.0.0/frontend-library/src/auth/README.md) for the auth runtime contract and examples.
|
|
40
|
+
|
|
41
|
+
## CLI Usage
|
|
42
|
+
|
|
43
|
+
Both commands are valid after installation:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx frontend-library init
|
|
47
|
+
npx cmfrt init
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The CLI still manages the same generated files:
|
|
51
|
+
|
|
52
|
+
- `cmfrt.json`
|
|
53
|
+
- `cmfrt-doc.md`
|
|
54
|
+
|
|
55
|
+
## Local Workspace Development
|
|
56
|
+
|
|
57
|
+
Current workspace rule:
|
|
58
|
+
|
|
59
|
+
- [cx-admin-frontend/package.json](D:/MIO%20BEAUTY%20ERP%202.0.0/cx-admin-frontend/package.json) uses `"@shokirovr16/frontend-library": "file:../frontend-library"` during active auth-library work
|
|
60
|
+
- after changing this package, run `npm install` in both `frontend-library` and `cx-admin-frontend`
|
|
61
|
+
- `cx-admin-frontend` dev mode uses `vite --force` to avoid stale optimized deps while the local file dependency changes
|
|
62
|
+
|
|
63
|
+
Recommended local loop:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd frontend-library
|
|
67
|
+
npm install
|
|
68
|
+
|
|
69
|
+
cd ../cx-admin-frontend
|
|
70
|
+
npm install
|
|
71
|
+
npm run build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Publishing
|
|
75
|
+
|
|
76
|
+
Publish this repository under the package name `@shokirovr16/frontend-library`.
|
|
77
|
+
|
|
78
|
+
Recommended release flow:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd frontend-library
|
|
82
|
+
npm install
|
|
83
|
+
npm version patch
|
|
84
|
+
npm publish
|
|
85
|
+
git push origin main --tags
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Before publishing:
|
|
89
|
+
|
|
90
|
+
- make sure `git status` is clean
|
|
91
|
+
- verify `npm pack --json` produces `shokirovr16-frontend-library-<version>.tgz`
|
|
92
|
+
- verify `cx-admin-frontend` still builds against either the local file dependency or the published version
|
|
93
|
+
|
|
94
|
+
After publishing:
|
|
95
|
+
|
|
96
|
+
1. Replace the local dependency in [cx-admin-frontend/package.json](D:/MIO%20BEAUTY%20ERP%202.0.0/cx-admin-frontend/package.json) with a semver range such as `"@shokirovr16/frontend-library": "^0.1.2"`.
|
|
97
|
+
2. Run `npm install` in `cx-admin-frontend`.
|
|
98
|
+
3. Run `npm run build` in `cx-admin-frontend`.
|
package/bin/cmfrt.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import initCommand from '../src/commands/init.js';
|
|
4
|
+
import addCommand from '../src/commands/add.js';
|
|
5
|
+
import updateCommand from '../src/commands/update.js';
|
|
6
|
+
import statusCommand from '../src/commands/status.js';
|
|
7
|
+
import listCommand from '../src/commands/list.js';
|
|
8
|
+
import uninstallCommand from '../src/commands/uninstall.js';
|
|
9
|
+
import themeCommand from '../src/commands/theme.js';
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name('cmfrt')
|
|
15
|
+
.description('Comfort UI komponentlarini loyiha ichiga qo‘shish uchun CLI')
|
|
16
|
+
.version('0.0.1');
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command('init')
|
|
20
|
+
.description('Vite + React + Tailwind loyihasi uchun boshlang‘ich sozlash')
|
|
21
|
+
.action(() => initCommand());
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('add <component>')
|
|
25
|
+
.description('Ro‘yxatdagi komponentni loyihaga qo‘shish')
|
|
26
|
+
.action((component) => addCommand(component));
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('update [component]')
|
|
30
|
+
.description('Komponentlarni yangilash')
|
|
31
|
+
.option('--all', 'Barcha o‘rnatilgan komponentlarni yangilash')
|
|
32
|
+
.action((component, options) => updateCommand(component, options));
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command('status')
|
|
36
|
+
.description('O‘rnatilgan komponentlar holatini ko‘rsatish')
|
|
37
|
+
.option('--json', 'Natijani JSON ko‘rinishida chiqarish')
|
|
38
|
+
.option('--only-updates', 'Faqat yangilanishi borlarini ko‘rsatish')
|
|
39
|
+
.action((options) => statusCommand(options));
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command('list')
|
|
43
|
+
.description('Registrydagi barcha komponentlarni ko‘rsatish')
|
|
44
|
+
.option('-c, --category <name>', 'Kategoriya bo‘yicha filtrlash')
|
|
45
|
+
.option('--search <text>', 'Nom yoki tavsif bo‘yicha qidirish')
|
|
46
|
+
.option('--json', 'Natijani JSON ko‘rinishida chiqarish')
|
|
47
|
+
.action((options) => listCommand(options));
|
|
48
|
+
|
|
49
|
+
program
|
|
50
|
+
.command('theme')
|
|
51
|
+
.description('Theme mode bilan ishlash')
|
|
52
|
+
.option('--list', 'Mavjud mode ro‘yxatini ko‘rsatish')
|
|
53
|
+
.option('--set <mode>', 'Mode tanlash va qo‘llash')
|
|
54
|
+
.action((options) => themeCommand(options));
|
|
55
|
+
|
|
56
|
+
program
|
|
57
|
+
.command('uninstall [name]')
|
|
58
|
+
.description('O‘rnatilgan komponentni o‘chirish')
|
|
59
|
+
.option('--all', 'Barcha o‘rnatilgan komponentlarni o‘chirish')
|
|
60
|
+
.option('--prune', 'Foydalanilmaydigan dependencylarni o‘chirish')
|
|
61
|
+
.option('--yes', 'Tasdiqlashsiz o‘chirish')
|
|
62
|
+
.action((name, options) => uninstallCommand(name, options));
|
|
63
|
+
|
|
64
|
+
program.addHelpText(
|
|
65
|
+
'after',
|
|
66
|
+
`\nMisollar:\n cmfrt init\n cmfrt add button\n cmfrt update --all\n cmfrt status --only-updates\n cmfrt list -c forms\n cmfrt theme --list\n cmfrt theme --set "Client light"\n cmfrt uninstall button --prune\n cmfrt uninstall --all\n`
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
await program.parseAsync(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shokirovr16/frontend-library",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CLI for bootstrapping comfort UI components into Vite + React + Tailwind projects.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"frontend-library": "./bin/cmfrt.js",
|
|
12
|
+
"cmfrt": "./bin/cmfrt.js"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
"./auth": "./src/auth/index.js",
|
|
16
|
+
"./auth/react": "./src/auth/react/index.js",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"src",
|
|
22
|
+
"templates",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"commander": "^10.0.0",
|
|
31
|
+
"execa": "^7.1.1",
|
|
32
|
+
"fs-extra": "^11.1.1",
|
|
33
|
+
"picocolors": "^1.1.1",
|
|
34
|
+
"prompts": "^2.4.2",
|
|
35
|
+
"semver": "^7.7.4"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18",
|
|
39
|
+
"react-router-dom": ">=6",
|
|
40
|
+
"axios": ">=1"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"axios": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @shokirovr16/frontend-library/auth
|
|
2
|
+
|
|
3
|
+
Shared React SPA auth layer for Keycloak / generic OIDC Authorization Code + PKCE flows.
|
|
4
|
+
|
|
5
|
+
Package identity:
|
|
6
|
+
|
|
7
|
+
- npm package: `@shokirovr16/frontend-library`
|
|
8
|
+
- core import: `@shokirovr16/frontend-library/auth`
|
|
9
|
+
- React helpers: `@shokirovr16/frontend-library/auth/react`
|
|
10
|
+
|
|
11
|
+
## What It Provides
|
|
12
|
+
|
|
13
|
+
- OIDC discovery from `issuer`
|
|
14
|
+
- Authorization Code Flow + PKCE
|
|
15
|
+
- in-memory access/refresh token handling
|
|
16
|
+
- silent SSO bootstrap through an iframe callback page
|
|
17
|
+
- fetch and axios helpers
|
|
18
|
+
- React provider, hooks, and route guards
|
|
19
|
+
- same-origin session sync through `BroadcastChannel`
|
|
20
|
+
|
|
21
|
+
## Peer Dependencies
|
|
22
|
+
|
|
23
|
+
- `react >= 18`
|
|
24
|
+
- `react-router-dom >= 6`
|
|
25
|
+
- `axios >= 1` only if you use the axios helper
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
Initialize before rendering protected UI:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { initializeAuth } from '@shokirovr16/frontend-library/auth';
|
|
33
|
+
|
|
34
|
+
initializeAuth({
|
|
35
|
+
runtimeConfig: async () => {
|
|
36
|
+
const res = await fetch('/runtime/auth-config', {
|
|
37
|
+
headers: { accept: 'application/json' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
throw new Error('runtime config fetch failed');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return res.json();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Wrap the app:
|
|
50
|
+
|
|
51
|
+
```jsx
|
|
52
|
+
import { AuthProvider } from '@shokirovr16/frontend-library/auth/react';
|
|
53
|
+
|
|
54
|
+
root.render(
|
|
55
|
+
<AuthProvider loading={null}>
|
|
56
|
+
<App />
|
|
57
|
+
</AuthProvider>
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Read auth state:
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
import { useAuth } from '@shokirovr16/frontend-library/auth/react';
|
|
65
|
+
|
|
66
|
+
function Header() {
|
|
67
|
+
const auth = useAuth();
|
|
68
|
+
|
|
69
|
+
return auth.isAuthenticated ? (
|
|
70
|
+
<button onClick={() => auth.logout()}>Logout</button>
|
|
71
|
+
) : (
|
|
72
|
+
<button onClick={() => auth.login()}>Login</button>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Callback Route
|
|
78
|
+
|
|
79
|
+
Your `redirectUri` route should call `handleCallback()`:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import { getAuth } from '@shokirovr16/frontend-library/auth';
|
|
83
|
+
|
|
84
|
+
async function onCallbackPage(navigate) {
|
|
85
|
+
const engine = getAuth();
|
|
86
|
+
const { returnTo } = await engine.handleCallback();
|
|
87
|
+
navigate(returnTo || '/');
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Silent SSO
|
|
92
|
+
|
|
93
|
+
If you use `silentCheckSsoRedirectUri`, create a page that runs:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import { createSilentCheckSsoCallback } from '@shokirovr16/frontend-library/auth';
|
|
97
|
+
|
|
98
|
+
createSilentCheckSsoCallback();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## HTTP Helpers
|
|
102
|
+
|
|
103
|
+
`fetch` helper:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
import { authFetch } from '@shokirovr16/frontend-library/auth';
|
|
107
|
+
|
|
108
|
+
const res = await authFetch('/api/me', { method: 'GET' });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Axios helper:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import axios from 'axios';
|
|
115
|
+
import { getAuth, createAuthHttpClient } from '@shokirovr16/frontend-library/auth';
|
|
116
|
+
|
|
117
|
+
const api = createAuthHttpClient({
|
|
118
|
+
axios,
|
|
119
|
+
engine: getAuth(),
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Guards
|
|
124
|
+
|
|
125
|
+
```jsx
|
|
126
|
+
import { RequireAuth, RequirePermission } from '@shokirovr16/frontend-library/auth/react';
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Examples:
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
<RequireAuth fallback={null}>
|
|
133
|
+
<ProtectedPage />
|
|
134
|
+
</RequireAuth>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```jsx
|
|
138
|
+
<RequirePermission permission="billing:read" fallback={null}>
|
|
139
|
+
<Billing />
|
|
140
|
+
</RequirePermission>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Runtime Config Contract
|
|
144
|
+
|
|
145
|
+
Minimum required fields:
|
|
146
|
+
|
|
147
|
+
- `tenantId`
|
|
148
|
+
- `issuer`
|
|
149
|
+
- `clientId`
|
|
150
|
+
- `redirectUri`
|
|
151
|
+
|
|
152
|
+
Recommended example:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"tenantId": "acme",
|
|
157
|
+
"tenantSlug": "acme",
|
|
158
|
+
"mode": "spa-direct",
|
|
159
|
+
"issuer": "https://sso.example.com/realms/acme",
|
|
160
|
+
"clientId": "spa-web",
|
|
161
|
+
"apiAudience": "api",
|
|
162
|
+
"scopes": ["openid", "profile", "email"],
|
|
163
|
+
"redirectUri": "https://app.example.com/auth/callback",
|
|
164
|
+
"postLogoutRedirectUri": "https://app.example.com/",
|
|
165
|
+
"silentCheckSsoRedirectUri": "https://app.example.com/auth/silent-callback",
|
|
166
|
+
"checkSsoPolicy": "onLoad",
|
|
167
|
+
"tokenPolicy": {
|
|
168
|
+
"minValiditySec": 30,
|
|
169
|
+
"refreshLeewaySec": 5
|
|
170
|
+
},
|
|
171
|
+
"logoutPolicy": {
|
|
172
|
+
"frontChannel": true,
|
|
173
|
+
"broadcast": true
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Events
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
import { subscribeAuthEvents } from '@shokirovr16/frontend-library/auth';
|
|
182
|
+
|
|
183
|
+
const unsub = subscribeAuthEvents((event) => {
|
|
184
|
+
console.log(event.type, event);
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Security Notes
|
|
189
|
+
|
|
190
|
+
- refresh tokens stay in memory by default
|
|
191
|
+
- `sessionStorage` is used only for redirect transaction state such as `state`, `code_verifier`, and `returnTo`
|
|
192
|
+
- retry on `401` is intentionally limited
|
|
193
|
+
- token sync is same-origin only
|