@ouim/logto-authkit 0.3.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.
@@ -0,0 +1,169 @@
1
+ import { type ClassValue } from 'clsx';
2
+ import type { LogtoUser, NavigationOptions } from './types.js';
3
+ import type { LogtoConfig } from '@logto/react';
4
+ /**
5
+ * Validates Logto configuration for required fields
6
+ * @param config - The LogtoConfig object to validate
7
+ * @throws {Error} If required configuration is missing or invalid
8
+ * @example
9
+ * try {
10
+ * validateLogtoConfig(config);
11
+ * } catch (error) {
12
+ * console.error('Invalid Logto config:', error.message);
13
+ * }
14
+ */
15
+ export declare const validateLogtoConfig: (config: LogtoConfig, options?: {
16
+ warnOnMissingResources?: boolean;
17
+ }) => void;
18
+ /**
19
+ * Transform Logto User Object
20
+ *
21
+ * Converts a Logto JWT claims object into a simplified LogtoUser format.
22
+ * Maps JWT standard claims (sub, name, email, picture) to user object properties,
23
+ * and preserves all additional claims from the original object.
24
+ *
25
+ * @param {any} logtoUser - Raw Logto user claims object from JWT token
26
+ * @returns {LogtoUser | null} Simplified user object or null if input is falsy
27
+ *
28
+ * @example
29
+ * const claims = { sub: 'user_123', name: 'John Doe', email: 'john@example.com', picture: 'http://...' };
30
+ * const user = transformUser(claims);
31
+ * // { id: 'user_123', name: 'John Doe', email: 'john@example.com', avatar: 'http://...' }
32
+ *
33
+ * @internal
34
+ */
35
+ export declare const transformUser: (logtoUser: any) => LogtoUser | null;
36
+ /**
37
+ * Navigate to a URL
38
+ *
39
+ * Browser fallback navigation function. AuthProvider-scoped custom navigation is handled
40
+ * via React context in `navigation.tsx`; this utility only performs History API /
41
+ * window.location navigation when no scoped router override is present.
42
+ *
43
+ * Supports:
44
+ * - Client-side History API for SPAs (pushState/replaceState)
45
+ * - Direct window.location for absolute URLs and fallback
46
+ *
47
+ * @param {string} url - URL to navigate to (relative path like '/dashboard' or absolute URL)
48
+ * @param {NavigationOptions} [options={}] - Navigation behavior options
49
+ * @param {boolean} [options.replace=false] - Use replaceState instead of pushState (replaces history entry)
50
+ * @param {boolean} [options.force=false] - Force navigation even if already on the same page
51
+ *
52
+ * @example
53
+ * // Navigate to a relative path
54
+ * navigateTo('/dashboard');
55
+ *
56
+ * @example
57
+ * // Replace history entry instead of adding new one
58
+ * navigateTo('/login', { replace: true });
59
+ *
60
+ * @example
61
+ * // Force navigation even if already on the page
62
+ * navigateTo('/dashboard', { force: true });
63
+ */
64
+ export declare const navigateTo: (url: string, options?: NavigationOptions) => void;
65
+ /**
66
+ * Get initials from name for avatar fallback
67
+ */
68
+ export declare const getInitials: (name?: string) => string;
69
+ /**
70
+ * Cookie management utilities
71
+ */
72
+ export declare const cookieUtils: {
73
+ /**
74
+ * Set a cookie with the given name, value, and options
75
+ */
76
+ setCookie: (name: string, value: string, options?: {
77
+ expires?: Date | number;
78
+ maxAge?: number;
79
+ domain?: string;
80
+ path?: string;
81
+ secure?: boolean;
82
+ sameSite?: "strict" | "lax" | "none";
83
+ httpOnly?: boolean;
84
+ }) => void;
85
+ /**
86
+ * Get a cookie value by name
87
+ */
88
+ getCookie: (name: string) => string | null;
89
+ /**
90
+ * Remove a cookie by name
91
+ */
92
+ removeCookie: (name: string, options?: {
93
+ domain?: string;
94
+ path?: string;
95
+ }) => void;
96
+ };
97
+ /**
98
+ * Specialized functions for JWT token management
99
+ */
100
+ export declare const jwtCookieUtils: {
101
+ /**
102
+ * Save JWT token to cookie.
103
+ *
104
+ * ⚠️ SECURITY NOTICE — XSS / non-httpOnly cookie
105
+ * ─────────────────────────────────────────────────────────────────────────
106
+ * This cookie is set from JavaScript, which means it cannot carry the
107
+ * `HttpOnly` flag. Any JavaScript that runs on the same origin — including
108
+ * injected scripts from an XSS vulnerability — can read the raw JWT value
109
+ * from `document.cookie` and exfiltrate it.
110
+ *
111
+ * The cookie is still protected by `Secure` (HTTPS-only) and
112
+ * `SameSite=Strict` (blocks cross-site request forgery), but those flags do
113
+ * NOT prevent access by same-origin JavaScript.
114
+ *
115
+ * MITIGATIONS:
116
+ * 1. Keep a strict Content-Security-Policy to reduce XSS attack surface.
117
+ * 2. If your deployment includes a Node.js backend that handles the
118
+ * callback redirect, use `buildAuthCookieHeader()` from
119
+ * `@ouim/logto-authkit/server` to set an `HttpOnly` version of the
120
+ * same cookie from the server side. The browser will then send it
121
+ * automatically and it will be invisible to JavaScript.
122
+ *
123
+ * See: https://owasp.org/www-community/HttpOnly
124
+ * ─────────────────────────────────────────────────────────────────────────
125
+ */
126
+ saveToken: (token: string) => void;
127
+ /**
128
+ * Get JWT token from cookie
129
+ */
130
+ getToken: () => string | null;
131
+ /**
132
+ * Remove JWT token from cookie
133
+ */
134
+ removeToken: () => void;
135
+ };
136
+ /**
137
+ * Client-side guest utilities
138
+ */
139
+ export declare const guestUtils: {
140
+ /**
141
+ * Generate a fingerprint-based guest ID
142
+ */
143
+ generateGuestId(): Promise<string>;
144
+ /**
145
+ * Get guest ID from cookie
146
+ */
147
+ getGuestId(): string | null;
148
+ /**
149
+ * Set guest ID cookie
150
+ *
151
+ * Uses cookieUtils.setCookie so that the same security flags applied to the
152
+ * auth token cookie (Secure, SameSite=strict) are also applied here, making
153
+ * both cookies consistent. The Secure flag is enforced regardless of the
154
+ * current protocol so that the guest ID is never sent over plain HTTP.
155
+ */
156
+ setGuestId(guestId?: string): Promise<string>;
157
+ /**
158
+ * Ensure guest ID exists, create if not
159
+ */
160
+ ensureGuestId(): Promise<string>;
161
+ /**
162
+ * Clear guest ID cookie
163
+ */
164
+ clearGuestId(): void;
165
+ };
166
+ /**
167
+ * Utility function to combine class names (for components)
168
+ */
169
+ export declare function cn(...inputs: ClassValue[]): string;
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@ouim/logto-authkit",
3
+ "version": "0.3.0",
4
+ "description": "A Logto auth kit with prebuilt React UI, hooks, server verification, and bundler helpers",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "engines": {
13
+ "node": "^18.18.0 || ^20.0.0 || ^22.0.0 || ^24.0.0"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js",
19
+ "require": "./dist/index.cjs"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/server/index.d.ts",
23
+ "import": "./dist/server/index.js",
24
+ "require": "./dist/server/index.cjs"
25
+ },
26
+ "./bundler-config": {
27
+ "types": "./dist/bundler-config.d.ts",
28
+ "import": "./dist/bundler-config.js",
29
+ "require": "./dist/bundler-config.cjs"
30
+ }
31
+ },
32
+ "browser": {
33
+ "./dist/index.js": "./dist/index.js"
34
+ },
35
+ "sideEffects": false,
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "vite build && tsc --project tsconfig.build.json --emitDeclarationOnly",
41
+ "build:tsc": "tsc",
42
+ "dev": "tsc --watch",
43
+ "dev:example": "npm --prefix example_app run dev",
44
+ "test": "vitest",
45
+ "test:package": "node ./scripts/run-package-audit.mjs",
46
+ "test:size": "node ./scripts/check-bundle-size.mjs",
47
+ "test:smoke": "node ./scripts/run-packed-smoke-tests.mjs",
48
+ "test:coverage": "vitest --coverage",
49
+ "lint": "eslint .",
50
+ "lint:fix": "eslint . --fix",
51
+ "clean": "rimraf dist",
52
+ "prepublishOnly": "npm run clean && npm run build",
53
+ "publish": "npm publish --access public --provenance"
54
+ },
55
+ "peerDependencies": {
56
+ "@logto/react": "^3.0.0 || ^4.0.0",
57
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
58
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
59
+ },
60
+ "dependencies": {
61
+ "@fingerprintjs/fingerprintjs": "^4.6.2",
62
+ "@radix-ui/react-avatar": "^1.0.4",
63
+ "@radix-ui/react-dialog": "^1.1.14",
64
+ "@radix-ui/react-dropdown-menu": "^2.0.6",
65
+ "@radix-ui/react-tooltip": "^1.2.7",
66
+ "class-variance-authority": "^0.7.0",
67
+ "clsx": "^2.1.0",
68
+ "cookie-parser": "^1.4.7",
69
+ "jose": "^6.0.11",
70
+ "lucide-react": "^0.575.0",
71
+ "tailwind-merge": "^2.2.1"
72
+ },
73
+ "devDependencies": {
74
+ "@eslint/js": "^8.56.0",
75
+ "@testing-library/dom": "^10.4.1",
76
+ "@testing-library/jest-dom": "^6.9.1",
77
+ "@testing-library/react": "^16.3.2",
78
+ "@types/cookie-parser": "^1.4.10",
79
+ "@types/express": "^5.0.6",
80
+ "@types/node": "^22.19.15",
81
+ "@types/react": "^18.0.37",
82
+ "@types/react-dom": "^18.0.11",
83
+ "@types/supertest": "^7.2.0",
84
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
85
+ "@typescript-eslint/parser": "^6.21.0",
86
+ "@vitest/coverage-v8": "^4.0.18",
87
+ "@vitest/ui": "^4.0.18",
88
+ "eslint": "^8.56.0",
89
+ "eslint-plugin-react": "^7.34.0",
90
+ "eslint-plugin-react-hooks": "^4.6.0",
91
+ "express": "^5.2.1",
92
+ "happy-dom": "^20.7.0",
93
+ "rimraf": "^5.0.10",
94
+ "supertest": "^7.2.2",
95
+ "typescript": "^5.0.4",
96
+ "vite": "^6.4.1",
97
+ "vitest": "^4.0.18"
98
+ },
99
+ "keywords": [
100
+ "logto",
101
+ "auth",
102
+ "authentication",
103
+ "react"
104
+ ],
105
+ "author": "thezem",
106
+ "license": "MIT",
107
+ "repository": {
108
+ "type": "git",
109
+ "url": "git+https://github.com/ouim-me/simple-logto.git"
110
+ }
111
+ }