@auditauth/web 0.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # @auditauth/web
2
+
3
+ `@auditauth/web` is the framework-agnostic AuditAuth SDK for browser
4
+ applications. It handles login redirects, callback processing, session state,
5
+ token refresh, and request and navigation metrics.
6
+
7
+ ## Install
8
+
9
+ Install the package in your app.
10
+
11
+ ```bash
12
+ npm install @auditauth/web
13
+ ```
14
+
15
+ ## Create an SDK instance
16
+
17
+ Create one `AuditAuthWeb` instance with your AuditAuth config and a storage
18
+ adapter. In browser apps, local storage is the common default.
19
+
20
+ ```ts
21
+ import { AuditAuthWeb } from '@auditauth/web'
22
+
23
+ export const auditauth = new AuditAuthWeb(
24
+ {
25
+ apiKey: process.env.VITE_AUDITAUTH_API_KEY!,
26
+ appId: process.env.VITE_AUDITAUTH_APP_ID!,
27
+ baseUrl: 'http://localhost:5173',
28
+ redirectUrl: 'http://localhost:5173/private',
29
+ },
30
+ {
31
+ get: (name) => localStorage.getItem(name),
32
+ set: (name, value) => localStorage.setItem(name, value),
33
+ remove: (name) => localStorage.removeItem(name),
34
+ }
35
+ )
36
+ ```
37
+
38
+ ## Initialize on app startup
39
+
40
+ Handle callback redirects before rendering protected pages, then enable
41
+ navigation tracking.
42
+
43
+ ```ts
44
+ await auditauth.handleRedirect()
45
+ auditauth.initNavigationTracking()
46
+ ```
47
+
48
+ ## Protect private pages
49
+
50
+ Before rendering private views, check if the user has a session. If not,
51
+ redirect to login.
52
+
53
+ ```ts
54
+ if (!auditauth.isAuthenticated()) {
55
+ await auditauth.login()
56
+ }
57
+ ```
58
+
59
+ You can access the current user at any time with `auditauth.getSessionUser()`.
60
+
61
+ ## Make authenticated API calls
62
+
63
+ Use `auditauth.fetch()` instead of `window.fetch()` for authenticated requests.
64
+ The SDK attaches the access token and automatically attempts refresh when a
65
+ request returns `401`.
66
+
67
+ ```ts
68
+ const response = await auditauth.fetch('https://api.example.com/private')
69
+ ```
70
+
71
+ ## Use session actions
72
+
73
+ You can trigger user session actions from your UI.
74
+
75
+ - `auditauth.login()` starts the login flow
76
+ - `auditauth.logout()` revokes and clears the session
77
+ - `auditauth.goToPortal()` redirects to the AuditAuth portal
78
+
79
+ ## API reference
80
+
81
+ These methods are available in `AuditAuthWeb`.
82
+
83
+ - `handleRedirect(): Promise<void | null>`
84
+ - `isAuthenticated(): boolean`
85
+ - `getSessionUser(): SessionUser | null`
86
+ - `login(): Promise<void>`
87
+ - `logout(): Promise<void>`
88
+ - `goToPortal(): Promise<void>`
89
+ - `fetch(input, init?): Promise<Response>`
90
+ - `trackNavigationPath(path: string): void`
91
+ - `initNavigationTracking(): void`
92
+
93
+ ## Compatibility
94
+
95
+ This package requires Node.js `>=18.18.0` for tooling and build environments.
96
+
97
+ ## Resources
98
+
99
+ - Repository: https://github.com/nimibyte/auditauth-sdk
100
+ - Documentation: https://docs.auditauth.com
101
+
102
+ ## Example
103
+
104
+ See the complete integration in `examples/vanilla/src`.
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,2 @@
1
+ export * from './sdk';
2
+ export type * from './types';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './sdk';
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { AuditAuthConfig } from "@auditauth/core";
2
+ import { StorageAdapter } from "./types";
3
+ declare class AuditAuthWeb {
4
+ private config;
5
+ private storage;
6
+ constructor(config: AuditAuthConfig, storage: StorageAdapter);
7
+ private getWithExpiry;
8
+ private setWithExpiry;
9
+ private getSessionId;
10
+ private pushMetric;
11
+ trackNavigationPath(path: string): void;
12
+ initNavigationTracking(): void;
13
+ fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
14
+ isAuthenticated(): boolean;
15
+ getSessionUser(): any;
16
+ goToPortal(): Promise<void>;
17
+ logout(): Promise<void>;
18
+ login(): Promise<void>;
19
+ handleRedirect(): Promise<null | undefined>;
20
+ }
21
+ export { AuditAuthWeb };
package/dist/sdk.js ADDED
@@ -0,0 +1,164 @@
1
+ import { authorizeCode, buildAuthUrl, buildPortalUrl, CORE_SETTINGS, refreshTokens, revokeSession } from "@auditauth/core";
2
+ class AuditAuthWeb {
3
+ constructor(config, storage) {
4
+ this.config = config;
5
+ this.storage = storage;
6
+ }
7
+ getWithExpiry(key) {
8
+ const itemStr = this.storage.get(key);
9
+ if (!itemStr)
10
+ return null;
11
+ const item = JSON.parse(itemStr);
12
+ if (Date.now() > item.expiresAt) {
13
+ this.storage.remove(key);
14
+ return null;
15
+ }
16
+ return item.value;
17
+ }
18
+ setWithExpiry(key, value, ttlSeconds) {
19
+ const now = Date.now();
20
+ const item = {
21
+ value,
22
+ expiresAt: now + ttlSeconds * 1000,
23
+ };
24
+ this.storage.set(key, JSON.stringify(item));
25
+ }
26
+ getSessionId() {
27
+ let session_id = this.getWithExpiry(CORE_SETTINGS.storage_keys.session_id);
28
+ if (!session_id) {
29
+ session_id = crypto.randomUUID();
30
+ this.setWithExpiry(CORE_SETTINGS.storage_keys.session_id, session_id, 60 * 5);
31
+ }
32
+ return session_id;
33
+ }
34
+ pushMetric(metric) {
35
+ const session_id = this.getSessionId();
36
+ const body = JSON.stringify({
37
+ appId: this.config.appId,
38
+ apiKey: this.config.apiKey,
39
+ session_id,
40
+ ...metric,
41
+ });
42
+ const url = `${CORE_SETTINGS.domains.api}/metrics`;
43
+ if (navigator.sendBeacon) {
44
+ const blob = new Blob([body], { type: 'application/json' });
45
+ navigator.sendBeacon(url, blob);
46
+ return;
47
+ }
48
+ fetch(url, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body,
52
+ keepalive: true,
53
+ }).catch(() => { });
54
+ }
55
+ trackNavigationPath(path) {
56
+ this.pushMetric({
57
+ event_type: 'navigation',
58
+ runtime: 'browser',
59
+ target: {
60
+ type: 'page',
61
+ path: path,
62
+ },
63
+ });
64
+ }
65
+ initNavigationTracking() {
66
+ const track = () => {
67
+ this.pushMetric({
68
+ event_type: 'navigation',
69
+ runtime: 'browser',
70
+ target: {
71
+ type: 'page',
72
+ path: window.location.pathname,
73
+ },
74
+ });
75
+ };
76
+ window.addEventListener('popstate', track);
77
+ const originalPushState = history.pushState;
78
+ history.pushState = (...args) => {
79
+ originalPushState.apply(history, args);
80
+ track();
81
+ };
82
+ }
83
+ async fetch(input, init = {}) {
84
+ const start = performance.now();
85
+ let access = this.getWithExpiry(CORE_SETTINGS.storage_keys.access);
86
+ const doFetch = (token) => fetch(input, {
87
+ ...init,
88
+ credentials: 'include',
89
+ headers: {
90
+ ...init.headers,
91
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
92
+ },
93
+ });
94
+ let res = await doFetch(access || undefined);
95
+ if (res.status === 401) {
96
+ const refreshData = await refreshTokens({ client_type: 'browser' });
97
+ if (!refreshData) {
98
+ await this.logout();
99
+ return res;
100
+ }
101
+ this.setWithExpiry(CORE_SETTINGS.storage_keys.access, refreshData.access_token, refreshData.access_expires_seconds);
102
+ res = await doFetch(refreshData.access_token);
103
+ }
104
+ this.pushMetric({
105
+ event_type: 'request',
106
+ runtime: 'browser',
107
+ target: {
108
+ type: 'api',
109
+ method: init.method || 'GET',
110
+ path: typeof input === 'string'
111
+ ? input
112
+ : input instanceof Request
113
+ ? input.url
114
+ : '',
115
+ status: res.status,
116
+ duration_ms: Math.round(performance.now() - start),
117
+ },
118
+ });
119
+ return res;
120
+ }
121
+ isAuthenticated() {
122
+ return !!this.getWithExpiry(CORE_SETTINGS.storage_keys.session);
123
+ }
124
+ getSessionUser() {
125
+ const value = this.getWithExpiry(CORE_SETTINGS.storage_keys.session);
126
+ return value ? value?.user : null;
127
+ }
128
+ async goToPortal() {
129
+ const access_token = this.getWithExpiry(CORE_SETTINGS.storage_keys.access);
130
+ if (!access_token)
131
+ return this.login();
132
+ const url = await buildPortalUrl({ access_token, redirectUrl: this.config.redirectUrl });
133
+ window.location.href = url.href;
134
+ }
135
+ async logout() {
136
+ const access_token = this.getWithExpiry(CORE_SETTINGS.storage_keys.access);
137
+ await revokeSession({ access_token }).catch(() => { });
138
+ this.storage.remove(CORE_SETTINGS.storage_keys.access);
139
+ this.storage.remove(CORE_SETTINGS.storage_keys.session);
140
+ window.location.reload();
141
+ }
142
+ async login() {
143
+ const url = await buildAuthUrl({
144
+ apiKey: this.config.apiKey,
145
+ redirectUrl: this.config.redirectUrl,
146
+ });
147
+ window.location.href = url.href;
148
+ }
149
+ async handleRedirect() {
150
+ const url = new URL(window.location.href);
151
+ const code = url.searchParams.get('code');
152
+ try {
153
+ const { data } = await authorizeCode({ code, client_type: 'browser' });
154
+ this.setWithExpiry(CORE_SETTINGS.storage_keys.access, data.access_token, data.access_expires_seconds);
155
+ this.setWithExpiry(CORE_SETTINGS.storage_keys.session, { user: data.user }, data.access_expires_seconds);
156
+ url.searchParams.delete('code');
157
+ window.history.replaceState({}, document.title, url.pathname + url.search);
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ }
163
+ }
164
+ export { AuditAuthWeb };
@@ -0,0 +1,6 @@
1
+ type StorageAdapter = {
2
+ get: (name: string) => string | null;
3
+ set: (name: string, value: string) => void;
4
+ remove: (name: string) => void;
5
+ };
6
+ export type { StorageAdapter };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@auditauth/web",
3
+ "version": "0.2.0-beta.1",
4
+ "description": "AuditAuth Web SDK (framework-agnostic)",
5
+ "license": "MIT",
6
+ "author": "Nimibyte",
7
+ "engines": {
8
+ "node": ">=18.18.0"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/nimibyte/auditauth-sdk.git"
13
+ },
14
+ "homepage": "https://docs.auditauth.com",
15
+ "bugs": {
16
+ "url": "https://github.com/nimibyte/auditauth-sdk/issues"
17
+ },
18
+ "keywords": [
19
+ "authentication",
20
+ "auth",
21
+ "oauth",
22
+ "identity",
23
+ "jwt",
24
+ "security",
25
+ "auditauth"
26
+ ],
27
+ "module": "dist/index.js",
28
+ "type": "module",
29
+ "main": "dist/index.js",
30
+ "types": "dist/index.d.ts",
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "sideEffects": false,
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.build.json",
44
+ "dev": "tsc -p tsconfig.build.json --watch",
45
+ "clean": "rm -rf dist"
46
+ },
47
+ "dependencies": {
48
+ "@auditauth/core": "^0.2.0-beta.1"
49
+ },
50
+ "devDependencies": {
51
+ "typescript": "^5.3.3"
52
+ }
53
+ }