@svadmin/auth0 0.0.0-stage → 0.2.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/package.json CHANGED
@@ -1,6 +1,49 @@
1
1
  {
2
2
  "name": "@svadmin/auth0",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.2",
4
+ "description": "Auth0 AuthProvider adapter for svadmin",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "src/**/*.ts",
9
+ "!src/**/*.test.*",
10
+ "!src/**/*.spec.*"
11
+ ],
12
+ "main": "src/index.ts",
13
+ "types": "src/index.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./src/index.ts",
17
+ "default": "./src/index.ts"
18
+ }
19
+ },
20
+ "peerDependencies": {
21
+ "@svadmin/core": "^0.79.0"
22
+ },
23
+ "keywords": [
24
+ "svadmin",
25
+ "auth0",
26
+ "auth",
27
+ "auth-provider",
28
+ "identity"
29
+ ],
30
+ "license": "MIT",
31
+ "author": "zuohuadong",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/vibeunion/svadmin.git",
35
+ "directory": "packages/auth0"
36
+ },
37
+ "homepage": "https://github.com/vibeunion/svadmin#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/vibeunion/svadmin/issues"
40
+ },
41
+ "svadmin": {
42
+ "capabilities": [
43
+ "auth"
44
+ ],
45
+ "core": "^0.55.0",
46
+ "stability": "stable",
47
+ "migration": null
48
+ }
49
+ }
@@ -0,0 +1,154 @@
1
+ import type { AuthProvider, AuthActionResult, CheckResult, Identity, AuthErrorResult } from '@svadmin/core';
2
+
3
+ export interface Auth0UserLike {
4
+ sub?: string;
5
+ name?: string | null;
6
+ nickname?: string | null;
7
+ given_name?: string | null;
8
+ family_name?: string | null;
9
+ email?: string | null;
10
+ picture?: string | null;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ export interface Auth0ClientLike {
15
+ isAuthenticated: () => Promise<boolean>;
16
+ getUser: () => Promise<Auth0UserLike | undefined>;
17
+ getIdTokenClaims?: () => Promise<Record<string, unknown> | undefined>;
18
+ loginWithRedirect?: (options?: Record<string, unknown>) => Promise<void>;
19
+ loginWithPopup?: (options?: Record<string, unknown>) => Promise<void>;
20
+ logout?: (options?: Record<string, unknown>) => Promise<void> | void;
21
+ getTokenSilently?: (options?: Record<string, unknown>) => Promise<string>;
22
+ }
23
+
24
+ export interface Auth0AuthOptions {
25
+ /** Custom claims key for roles, e.g. 'https://svadmin.dev/roles' or 'roles'. Defaults to 'roles' */
26
+ rolesKey?: string;
27
+ /** Custom claims key for permissions. Defaults to 'permissions' */
28
+ permissionsKey?: string;
29
+ /** Fallback default role when no role claim exists. Defaults to 'user' */
30
+ defaultRole?: string;
31
+ /** Return URI after logout. Defaults to '/' */
32
+ logoutRedirectUri?: string;
33
+ /** Fallback login redirect path or URL */
34
+ loginUrl?: string;
35
+ /** Login invocation mode: 'redirect' or 'popup'. Defaults to 'redirect' */
36
+ loginMode?: 'redirect' | 'popup';
37
+ }
38
+
39
+ function asInternalRedirect(value: string | undefined): string | undefined {
40
+ if (!value || !value.startsWith('/') || value.startsWith('//') || value.includes('\\')) return undefined;
41
+ for (const character of value) {
42
+ const code = character.charCodeAt(0);
43
+ if (code <= 0x20 || code === 0x7f) return undefined;
44
+ }
45
+ return value;
46
+ }
47
+
48
+ export function createAuth0AuthProvider(
49
+ auth0: Auth0ClientLike,
50
+ options: Auth0AuthOptions = {},
51
+ ): AuthProvider {
52
+ const rolesKey = options.rolesKey ?? 'roles';
53
+ const permissionsKey = options.permissionsKey ?? 'permissions';
54
+ const defaultRole = options.defaultRole ?? 'user';
55
+ const loginMode = options.loginMode ?? 'redirect';
56
+
57
+ return {
58
+ async login(params: Record<string, unknown> = {}): Promise<AuthActionResult> {
59
+ try {
60
+ if (loginMode === 'popup' && auth0.loginWithPopup) {
61
+ await auth0.loginWithPopup(params);
62
+ return { success: true };
63
+ }
64
+ if (auth0.loginWithRedirect) {
65
+ await auth0.loginWithRedirect(params);
66
+ return { success: true };
67
+ }
68
+ if (options.loginUrl && typeof window !== 'undefined') {
69
+ window.location.href = options.loginUrl;
70
+ return { success: true };
71
+ }
72
+ return {
73
+ success: false,
74
+ error: {
75
+ name: 'AUTH0_NOT_CONFIGURED',
76
+ message: 'Auth0 client login method is not available.',
77
+ },
78
+ };
79
+ } catch (err: unknown) {
80
+ const error = err instanceof Error ? err : new Error(String(err));
81
+ return {
82
+ success: false,
83
+ error: {
84
+ name: error.name || 'AUTH0_LOGIN_ERROR',
85
+ message: error.message,
86
+ },
87
+ };
88
+ }
89
+ },
90
+
91
+ async logout(params?: Record<string, unknown>): Promise<AuthActionResult> {
92
+ const returnTo = options.logoutRedirectUri ?? (typeof window !== 'undefined' ? window.location.origin : '/');
93
+ if (auth0.logout) {
94
+ await auth0.logout({
95
+ logoutParams: { returnTo },
96
+ ...params,
97
+ });
98
+ }
99
+ const redirectTo = asInternalRedirect(returnTo);
100
+ return redirectTo ? { success: true, redirectTo } : { success: true };
101
+ },
102
+
103
+ async check(): Promise<CheckResult> {
104
+ try {
105
+ const authenticated = await auth0.isAuthenticated();
106
+ if (authenticated) {
107
+ return { authenticated: true };
108
+ }
109
+ const redirectTo = asInternalRedirect(options.loginUrl);
110
+ return { authenticated: false, ...(redirectTo ? { redirectTo } : {}) };
111
+ } catch {
112
+ const redirectTo = asInternalRedirect(options.loginUrl);
113
+ return { authenticated: false, ...(redirectTo ? { redirectTo } : {}) };
114
+ }
115
+ },
116
+
117
+ async getIdentity(): Promise<Identity | null> {
118
+ const user = await auth0.getUser();
119
+ if (!user) return null;
120
+
121
+ const joinedName = [user.given_name, user.family_name].filter(Boolean).join(' ');
122
+ const computedName =
123
+ user.name ??
124
+ user.nickname ??
125
+ (joinedName.length > 0 ? joinedName : 'Anonymous User');
126
+
127
+ return {
128
+ id: user.sub ?? 'unknown',
129
+ name: computedName,
130
+ ...(user.email ? { email: user.email } : {}),
131
+ ...(user.picture ? { avatar: user.picture } : {}),
132
+ };
133
+ },
134
+
135
+ async getPermissions(): Promise<unknown> {
136
+ const claims = (await auth0.getIdTokenClaims?.()) ?? (await auth0.getUser()) ?? {};
137
+ const roles = claims[rolesKey] ?? claims[permissionsKey] ?? defaultRole;
138
+ return roles;
139
+ },
140
+
141
+ async onError(error: unknown): Promise<AuthErrorResult> {
142
+ const status = (error as { status?: number; statusCode?: number })?.status ??
143
+ (error as { status?: number; statusCode?: number })?.statusCode;
144
+ if (status === 401 || status === 403) {
145
+ const redirectTo = asInternalRedirect(options.loginUrl);
146
+ return {
147
+ logout: true,
148
+ ...(redirectTo ? { redirectTo } : { redirectTo: '/login' }),
149
+ };
150
+ }
151
+ return {};
152
+ },
153
+ };
154
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { createAuth0AuthProvider } from './auth0-auth-provider.js';
2
+ export type {
3
+ Auth0UserLike,
4
+ Auth0ClientLike,
5
+ Auth0AuthOptions,
6
+ } from './auth0-auth-provider.js';
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.