@thunderid/nuxt 0.0.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.
Files changed (116) hide show
  1. package/LICENSE +201 -0
  2. package/dist/module.d.mts +46 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +216 -0
  5. package/dist/runtime/components/ThunderIDRoot.d.ts +52 -0
  6. package/dist/runtime/components/ThunderIDRoot.js +160 -0
  7. package/dist/runtime/components/actions/SignInButton.d.ts +37 -0
  8. package/dist/runtime/components/actions/SignInButton.js +51 -0
  9. package/dist/runtime/components/actions/SignOutButton.d.ts +34 -0
  10. package/dist/runtime/components/actions/SignOutButton.js +43 -0
  11. package/dist/runtime/components/actions/SignUpButton.d.ts +33 -0
  12. package/dist/runtime/components/actions/SignUpButton.js +48 -0
  13. package/dist/runtime/components/auth/Callback.d.ts +43 -0
  14. package/dist/runtime/components/auth/Callback.js +93 -0
  15. package/dist/runtime/components/auth/SignIn.d.ts +38 -0
  16. package/dist/runtime/components/auth/SignIn.js +60 -0
  17. package/dist/runtime/components/auth/SignUp.d.ts +40 -0
  18. package/dist/runtime/components/auth/SignUp.js +79 -0
  19. package/dist/runtime/components/control/Loading.d.ts +36 -0
  20. package/dist/runtime/components/control/Loading.js +17 -0
  21. package/dist/runtime/components/control/SignedIn.d.ts +38 -0
  22. package/dist/runtime/components/control/SignedIn.js +17 -0
  23. package/dist/runtime/components/control/SignedOut.d.ts +37 -0
  24. package/dist/runtime/components/control/SignedOut.js +17 -0
  25. package/dist/runtime/components/organization/CreateOrganization.d.ts +32 -0
  26. package/dist/runtime/components/organization/CreateOrganization.js +29 -0
  27. package/dist/runtime/components/organization/Organization.d.ts +39 -0
  28. package/dist/runtime/components/organization/Organization.js +17 -0
  29. package/dist/runtime/components/organization/OrganizationList.d.ts +34 -0
  30. package/dist/runtime/components/organization/OrganizationList.js +30 -0
  31. package/dist/runtime/components/organization/OrganizationProfile.d.ts +32 -0
  32. package/dist/runtime/components/organization/OrganizationProfile.js +32 -0
  33. package/dist/runtime/components/organization/OrganizationSwitcher.d.ts +36 -0
  34. package/dist/runtime/components/organization/OrganizationSwitcher.js +26 -0
  35. package/dist/runtime/components/user/User.d.ts +38 -0
  36. package/dist/runtime/components/user/User.js +17 -0
  37. package/dist/runtime/components/user/UserDropdown.d.ts +38 -0
  38. package/dist/runtime/components/user/UserDropdown.js +45 -0
  39. package/dist/runtime/components/user/UserProfile.d.ts +35 -0
  40. package/dist/runtime/components/user/UserProfile.js +35 -0
  41. package/dist/runtime/composables/useThunderID.d.ts +38 -0
  42. package/dist/runtime/composables/useThunderID.js +73 -0
  43. package/dist/runtime/errors/error-codes.d.ts +40 -0
  44. package/dist/runtime/errors/error-codes.js +19 -0
  45. package/dist/runtime/errors/index.d.ts +19 -0
  46. package/dist/runtime/errors/index.js +2 -0
  47. package/dist/runtime/errors/thunderid-error.d.ts +47 -0
  48. package/dist/runtime/errors/thunderid-error.js +15 -0
  49. package/dist/runtime/middleware/auth.d.ts +35 -0
  50. package/dist/runtime/middleware/auth.js +2 -0
  51. package/dist/runtime/middleware/defineThunderIDMiddleware.d.ts +53 -0
  52. package/dist/runtime/middleware/defineThunderIDMiddleware.js +24 -0
  53. package/dist/runtime/plugins/thunderid.d.ts +39 -0
  54. package/dist/runtime/plugins/thunderid.js +128 -0
  55. package/dist/runtime/server/ThunderIDNuxtClient.d.ts +186 -0
  56. package/dist/runtime/server/ThunderIDNuxtClient.js +384 -0
  57. package/dist/runtime/server/index.d.ts +33 -0
  58. package/dist/runtime/server/index.js +3 -0
  59. package/dist/runtime/server/plugins/thunderid-ssr.d.ts +40 -0
  60. package/dist/runtime/server/plugins/thunderid-ssr.js +135 -0
  61. package/dist/runtime/server/routes/auth/branding/branding.get.d.ts +31 -0
  62. package/dist/runtime/server/routes/auth/branding/branding.get.js +40 -0
  63. package/dist/runtime/server/routes/auth/organizations/current.get.d.ts +29 -0
  64. package/dist/runtime/server/routes/auth/organizations/current.get.js +24 -0
  65. package/dist/runtime/server/routes/auth/organizations/id.get.d.ts +28 -0
  66. package/dist/runtime/server/routes/auth/organizations/id.get.js +28 -0
  67. package/dist/runtime/server/routes/auth/organizations/index.get.d.ts +28 -0
  68. package/dist/runtime/server/routes/auth/organizations/index.get.js +24 -0
  69. package/dist/runtime/server/routes/auth/organizations/index.post.d.ts +30 -0
  70. package/dist/runtime/server/routes/auth/organizations/index.post.js +30 -0
  71. package/dist/runtime/server/routes/auth/organizations/me.get.d.ts +28 -0
  72. package/dist/runtime/server/routes/auth/organizations/me.get.js +24 -0
  73. package/dist/runtime/server/routes/auth/organizations/switch.post.d.ts +32 -0
  74. package/dist/runtime/server/routes/auth/organizations/switch.post.js +49 -0
  75. package/dist/runtime/server/routes/auth/session/callback.get.d.ts +27 -0
  76. package/dist/runtime/server/routes/auth/session/callback.get.js +91 -0
  77. package/dist/runtime/server/routes/auth/session/callback.post.d.ts +48 -0
  78. package/dist/runtime/server/routes/auth/session/callback.post.js +53 -0
  79. package/dist/runtime/server/routes/auth/session/session.get.d.ts +26 -0
  80. package/dist/runtime/server/routes/auth/session/session.get.js +22 -0
  81. package/dist/runtime/server/routes/auth/session/signin.get.d.ts +29 -0
  82. package/dist/runtime/server/routes/auth/session/signin.get.js +37 -0
  83. package/dist/runtime/server/routes/auth/session/signin.post.d.ts +37 -0
  84. package/dist/runtime/server/routes/auth/session/signin.post.js +102 -0
  85. package/dist/runtime/server/routes/auth/session/signout.post.d.ts +31 -0
  86. package/dist/runtime/server/routes/auth/session/signout.post.js +38 -0
  87. package/dist/runtime/server/routes/auth/session/signup.post.d.ts +36 -0
  88. package/dist/runtime/server/routes/auth/session/signup.post.js +30 -0
  89. package/dist/runtime/server/routes/auth/session/token.get.d.ts +29 -0
  90. package/dist/runtime/server/routes/auth/session/token.get.js +6 -0
  91. package/dist/runtime/server/routes/auth/user/profile.get.d.ts +29 -0
  92. package/dist/runtime/server/routes/auth/user/profile.get.js +24 -0
  93. package/dist/runtime/server/routes/auth/user/profile.patch.d.ts +35 -0
  94. package/dist/runtime/server/routes/auth/user/profile.patch.js +41 -0
  95. package/dist/runtime/server/routes/auth/user/user.get.d.ts +25 -0
  96. package/dist/runtime/server/routes/auth/user/user.get.js +21 -0
  97. package/dist/runtime/server/utils/event-context.d.ts +49 -0
  98. package/dist/runtime/server/utils/event-context.js +3 -0
  99. package/dist/runtime/server/utils/serverSession.d.ts +65 -0
  100. package/dist/runtime/server/utils/serverSession.js +44 -0
  101. package/dist/runtime/server/utils/session.d.ts +85 -0
  102. package/dist/runtime/server/utils/session.js +106 -0
  103. package/dist/runtime/server/utils/token-refresh.d.ts +42 -0
  104. package/dist/runtime/server/utils/token-refresh.js +65 -0
  105. package/dist/runtime/types.d.ts +161 -0
  106. package/dist/runtime/types.js +0 -0
  107. package/dist/runtime/utils/createRouteMatcher.d.ts +40 -0
  108. package/dist/runtime/utils/createRouteMatcher.js +7 -0
  109. package/dist/runtime/utils/index.d.ts +30 -0
  110. package/dist/runtime/utils/index.js +1 -0
  111. package/dist/runtime/utils/log.d.ts +44 -0
  112. package/dist/runtime/utils/log.js +25 -0
  113. package/dist/runtime/utils/url-validation.d.ts +49 -0
  114. package/dist/runtime/utils/url-validation.js +38 -0
  115. package/dist/types.d.mts +7 -0
  116. package/package.json +101 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
3
+ *
4
+ * WSO2 LLC. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+ import type { BrandingPreference, I18nPreferences, Organization, Platform, TokenEndpointAuthMethod, User, UserProfile } from '@thunderid/node';
19
+ import type { JWTPayload } from 'jose';
20
+ /**
21
+ * Configuration for the ThunderID Nuxt module.
22
+ */
23
+ export interface ThunderIDNuxtConfig {
24
+ /** URL to redirect to after sign-in (default: '/') */
25
+ afterSignInUrl?: string;
26
+ /** URL to redirect to after sign-out (default: '/') */
27
+ afterSignOutUrl?: string;
28
+ /**
29
+ * ThunderID application id (`spId`) — appended to the redirect-based sign-up
30
+ * URL when present. Mirrors `applicationId` in the React/Next.js SDKs.
31
+ */
32
+ applicationId?: string;
33
+ /** Base URL of the ThunderID org tenant (e.g. https://api.asgardeo.io/t/your_org) */
34
+ baseUrl?: string;
35
+ /** OAuth2 Client ID */
36
+ clientId?: string;
37
+ /** OAuth2 Client Secret (server-only, use ASGARDEO_CLIENT_SECRET env var) */
38
+ clientSecret?: string;
39
+ /**
40
+ * Identity platform variant. Set to `Platform.ThunderIDV2` when connecting to
41
+ * a Thunder (ThunderIDV2) instance. Forwarded to the underlying Node client so
42
+ * platform-specific behaviours (e.g. issuer resolution) apply correctly.
43
+ */
44
+ platform?: keyof typeof Platform;
45
+ /**
46
+ * Feature-gating preferences that control which server-side data fetches
47
+ * the Nitro plugin performs on every SSR request.
48
+ */
49
+ preferences?: {
50
+ /** i18n configuration forwarded to `I18nProvider`. */
51
+ i18n?: I18nPreferences;
52
+ theme?: {
53
+ /**
54
+ * When true (default), the Nitro plugin fetches the branding preference
55
+ * from ThunderID and passes it to `BrandingProvider` / `ThemeProvider`.
56
+ */
57
+ inheritFromBranding?: boolean;
58
+ /**
59
+ * Theme mode forwarded to the Vue SDK's `ThemeProvider`.
60
+ * - `'light'` (default) | `'dark'`: Fixed color scheme. Toggle at runtime with `useTheme().toggleTheme()`.
61
+ * - `'system'`: Follows the OS `prefers-color-scheme`.
62
+ * - `'class'`: Reads a CSS class on `<html>` (works well with Tailwind dark-mode).
63
+ * - `'branding'`: Follows the active theme from the tenant's branding preference.
64
+ */
65
+ mode?: 'light' | 'dark' | 'system' | 'class' | 'branding';
66
+ };
67
+ user?: {
68
+ /** Whether to fetch the user's organisations during SSR (default: true). */
69
+ fetchOrganizations?: boolean;
70
+ /** Whether to fetch the SCIM2 user profile during SSR (default: true). */
71
+ fetchUserProfile?: boolean;
72
+ };
73
+ };
74
+ /** OAuth2 scopes to request */
75
+ scopes?: string[];
76
+ /** Secret for signing session JWTs (use ASGARDEO_SESSION_SECRET env var) */
77
+ sessionSecret?: string;
78
+ /**
79
+ * Optional override for the redirect-based sign-in URL. Reserved for
80
+ * parity with the React/Next.js SDKs; not currently used by the redirect
81
+ * flow (which goes through `/api/auth/signin`).
82
+ */
83
+ signInUrl?: string;
84
+ /**
85
+ * Optional override for the redirect-based sign-up URL. When set,
86
+ * `<ThunderIDSignUpButton>` and `useThunderID().signUp()` (no-arg) navigate
87
+ * here instead of deriving the URL from `baseUrl`/`clientId`.
88
+ */
89
+ signUpUrl?: string;
90
+ /**
91
+ * Configuration for the token endpoint request.
92
+ */
93
+ tokenRequest?: {
94
+ /**
95
+ * OAuth 2.0 client authentication method used at the token endpoint.
96
+ * Defaults to `client_secret_basic` for ThunderIDV2 and `client_secret_post`
97
+ * for all other platforms when not specified.
98
+ */
99
+ authMethod?: TokenEndpointAuthMethod;
100
+ };
101
+ }
102
+ /**
103
+ * Payload stored in the session JWT cookie.
104
+ */
105
+ export interface ThunderIDSessionPayload extends JWTPayload {
106
+ accessToken: string;
107
+ /** Unix timestamp (seconds) when the access token expires. Used for proactive refresh. */
108
+ accessTokenExpiresAt?: number;
109
+ exp: number;
110
+ iat: number;
111
+ /** Raw ID token string (for userinfo derivation without in-memory store). */
112
+ idToken?: string;
113
+ organizationId?: string;
114
+ /** Refresh token for obtaining new access tokens without re-authentication. */
115
+ refreshToken?: string;
116
+ scopes: string;
117
+ sessionId: string;
118
+ sub: string;
119
+ }
120
+ /**
121
+ * Payload stored in the temporary session JWT cookie (during OAuth flow).
122
+ */
123
+ export interface ThunderIDTempSessionPayload extends JWTPayload {
124
+ /** URL to redirect to after successful sign-in */
125
+ returnTo?: string;
126
+ sessionId: string;
127
+ type: 'temp';
128
+ }
129
+ /**
130
+ * Full SSR payload resolved by the Nitro plugin on each page request.
131
+ * Written to `event.context.thunderid.ssr` and subsequently seeded into
132
+ * hydrated `useState` keys so the client never re-fetches on first render.
133
+ */
134
+ export interface ThunderIDSSRData {
135
+ /** Branding preference fetched from ThunderID (null when `preferences.theme.inheritFromBranding` is false). */
136
+ brandingPreference: BrandingPreference | null;
137
+ /** The organisation the user is currently acting within (null when not in an org). */
138
+ currentOrganization: Organization | null;
139
+ isSignedIn: boolean;
140
+ /** All organisations the user is a member of (empty array when `preferences.user.fetchOrganizations` is false). */
141
+ myOrganizations: Organization[];
142
+ /**
143
+ * The base URL actually used for this request.
144
+ * Equals `${baseUrl}/o` when the user is acting within an organisation
145
+ * (derived from the `user_org` claim in the ID token), otherwise equals
146
+ * the configured `baseUrl`.
147
+ */
148
+ resolvedBaseUrl: string | null;
149
+ session: ThunderIDSessionPayload | null;
150
+ user: User | null;
151
+ /** Flattened SCIM2 profile + raw profile + schemas (null when `preferences.user.fetchUserProfile` is false). */
152
+ userProfile: UserProfile | null;
153
+ }
154
+ /**
155
+ * Auth state hydrated from server to client via useState.
156
+ */
157
+ export interface ThunderIDAuthState {
158
+ isLoading: boolean;
159
+ isSignedIn: boolean;
160
+ user: User | null;
161
+ }
File without changes
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
3
+ *
4
+ * WSO2 LLC. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+ /**
19
+ * Create a route matcher function from an array of glob-like patterns.
20
+ *
21
+ * Patterns support:
22
+ * - Literal paths: `/dashboard`
23
+ * - Wildcard segments: `/admin/*` matches `/admin/users` but not `/admin/users/1`
24
+ * - Deep wildcards: `/admin/**` matches `/admin/users` and `/admin/users/1`
25
+ * - Explicit regex groups: `/api/(users|posts)` stays as-is
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const isProtectedRoute = createRouteMatcher(['/dashboard/**', '/admin/**']);
30
+ * const isPublicRoute = createRouteMatcher(['/', '/about', '/sign-in']);
31
+ *
32
+ * // In a global middleware:
33
+ * export default defineNuxtRouteMiddleware((to) => {
34
+ * if (isProtectedRoute(to.path) && !authState.value?.isSignedIn) {
35
+ * return navigateTo('/api/auth/signin', { external: true });
36
+ * }
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function createRouteMatcher(patterns: string[]): (path: string) => boolean;
@@ -0,0 +1,7 @@
1
+ export function createRouteMatcher(patterns) {
2
+ const regexes = patterns.map((pattern) => {
3
+ const regexStr = pattern.replace(/[.+^${}|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLE_STAR___").replace(/\*/g, "[^/]*").replace(/___DOUBLE_STAR___/g, ".*");
4
+ return new RegExp(`^${regexStr}$`);
5
+ });
6
+ return (path) => regexes.some((regex) => regex.test(path));
7
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
3
+ *
4
+ * WSO2 LLC. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+ /**
19
+ * @thunderid/nuxt/utils
20
+ *
21
+ * Public utilities barrel. Import from this subpath to access
22
+ * utilities such as createRouteMatcher without polluting the
23
+ * global auto-import namespace.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { createRouteMatcher } from '@thunderid/nuxt/utils';
28
+ * ```
29
+ */
30
+ export { createRouteMatcher } from './createRouteMatcher.js';
@@ -0,0 +1 @@
1
+ export { createRouteMatcher } from "./createRouteMatcher.js";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
3
+ *
4
+ * WSO2 LLC. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+ /**
19
+ * Mask a token so it is safe to include in logs and error messages.
20
+ * Shows the first 4 and last 4 characters, replacing the middle with "…".
21
+ *
22
+ * @example
23
+ * maskToken('eyJhbGciOiJIUzI1NiJ9.abc.xyz') // 'eyJh….xyz'
24
+ */
25
+ export declare function maskToken(token: string): string;
26
+ /**
27
+ * Create a namespaced logger for a specific SDK subsystem.
28
+ *
29
+ * Debug output is suppressed unless the `ASGARDEO_DEBUG` environment
30
+ * variable is set (any truthy value).
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const log = createLogger('session');
35
+ * log.info('Session created for', maskToken(accessToken));
36
+ * log.debug('Full payload', payload); // only logged when ASGARDEO_DEBUG=true
37
+ * ```
38
+ */
39
+ export declare function createLogger(subsystem: string): {
40
+ debug: (...args: unknown[]) => void;
41
+ error: (...args: unknown[]) => void;
42
+ info: (...args: unknown[]) => void;
43
+ warn: (...args: unknown[]) => void;
44
+ };
@@ -0,0 +1,25 @@
1
+ const PREFIX = "@thunderid/nuxt";
2
+ export function maskToken(token) {
3
+ if (!token) return "(empty)";
4
+ if (token.length <= 8) return "***";
5
+ return `${token.slice(0, 4)}\u2026${token.slice(-4)}`;
6
+ }
7
+ export function createLogger(subsystem) {
8
+ const tag = `[${PREFIX}:${subsystem}]`;
9
+ return {
10
+ debug: (...args) => {
11
+ if (process.env["ASGARDEO_DEBUG"]) {
12
+ console.log(tag, ...args);
13
+ }
14
+ },
15
+ error: (...args) => {
16
+ console.error(tag, ...args);
17
+ },
18
+ info: (...args) => {
19
+ console.log(tag, ...args);
20
+ },
21
+ warn: (...args) => {
22
+ console.warn(tag, ...args);
23
+ }
24
+ };
25
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
3
+ *
4
+ * WSO2 LLC. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+ /**
19
+ * Validate a `returnTo` / redirect URL supplied by the client.
20
+ *
21
+ * Rules (defence-in-depth against open-redirect attacks):
22
+ * 1. Must be a non-empty string.
23
+ * 2. Must start with `/` (relative path only — no protocol, no host).
24
+ * 3. Must NOT start with `//` (protocol-relative URL — resolves as absolute).
25
+ * 4. Must NOT contain a `\` after the leading `/` (browser normalises `\` to `/`).
26
+ * 5. Must NOT contain a `%2F` or `%5C` in the first two chars after `/`
27
+ * (encoded slashes/backslashes that bypass rule 3/4 after URL decoding).
28
+ *
29
+ * Returns the validated URL as-is on success, or throws `ThunderIDError`
30
+ * with `ErrorCode.OpenRedirectBlocked` on failure.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const safe = validateReturnUrl('/dashboard'); // '/dashboard'
35
+ * validateReturnUrl('//evil.com'); // throws
36
+ * validateReturnUrl('https://evil.com'); // throws
37
+ * validateReturnUrl('/\\evil.com'); // throws
38
+ * ```
39
+ */
40
+ export declare function validateReturnUrl(url: unknown): string;
41
+ /**
42
+ * Safe variant of `validateReturnUrl` that returns a fallback instead of throwing.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const url = safeReturnUrl(query.returnTo, '/dashboard');
47
+ * ```
48
+ */
49
+ export declare function safeReturnUrl(url: unknown, fallback?: string): string;
@@ -0,0 +1,38 @@
1
+ import { ThunderIDError } from "../errors/thunderid-error.js";
2
+ import { ErrorCode } from "../errors/error-codes.js";
3
+ export function validateReturnUrl(url) {
4
+ if (typeof url !== "string" || url.trim() === "") {
5
+ throw new ThunderIDError("returnTo must be a non-empty string.", ErrorCode.OpenRedirectBlocked, { statusCode: 400 });
6
+ }
7
+ const trimmed = url.trim();
8
+ if (!trimmed.startsWith("/") || trimmed.startsWith("//")) {
9
+ throw new ThunderIDError(
10
+ `Open redirect blocked: returnTo "${trimmed}" must be a relative path starting with a single "/".`,
11
+ ErrorCode.OpenRedirectBlocked,
12
+ { statusCode: 400 }
13
+ );
14
+ }
15
+ if (trimmed.length > 1 && trimmed[1] === "\\") {
16
+ throw new ThunderIDError(
17
+ `Open redirect blocked: returnTo "${trimmed}" contains a backslash.`,
18
+ ErrorCode.OpenRedirectBlocked,
19
+ { statusCode: 400 }
20
+ );
21
+ }
22
+ const decoded = decodeURIComponent(trimmed.slice(1, 5).toLowerCase());
23
+ if (decoded.startsWith("/") || decoded.startsWith("\\")) {
24
+ throw new ThunderIDError(
25
+ `Open redirect blocked: returnTo "${trimmed}" contains an encoded redirect sequence.`,
26
+ ErrorCode.OpenRedirectBlocked,
27
+ { statusCode: 400 }
28
+ );
29
+ }
30
+ return trimmed;
31
+ }
32
+ export function safeReturnUrl(url, fallback = "/") {
33
+ try {
34
+ return validateReturnUrl(url);
35
+ } catch {
36
+ return fallback;
37
+ }
38
+ }
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@thunderid/nuxt",
3
+ "version": "0.0.1",
4
+ "description": "Nuxt SDK for ThunderID",
5
+ "keywords": [
6
+ "thunderid",
7
+ "nuxt",
8
+ "nuxtjs",
9
+ "ssr"
10
+ ],
11
+ "homepage": "https://github.com/thunder-id/thunderid/tree/main/sdks/nuxt#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/thunder-id/thunderid/issues"
14
+ },
15
+ "author": "WSO2",
16
+ "license": "Apache-2.0",
17
+ "type": "module",
18
+ "main": "./dist/module.mjs",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/types.d.mts",
22
+ "import": "./dist/module.mjs"
23
+ },
24
+ "./server": {
25
+ "types": "./dist/runtime/server/index.d.ts",
26
+ "import": "./dist/runtime/server/index.js"
27
+ },
28
+ "./errors": {
29
+ "types": "./dist/runtime/errors/index.d.ts",
30
+ "import": "./dist/runtime/errors/index.js"
31
+ },
32
+ "./utils": {
33
+ "types": "./dist/runtime/utils/index.d.ts",
34
+ "import": "./dist/runtime/utils/index.js"
35
+ }
36
+ },
37
+ "typesVersions": {
38
+ "*": {
39
+ ".": [
40
+ "./dist/types.d.mts"
41
+ ],
42
+ "server": [
43
+ "./dist/runtime/server/index.d.ts"
44
+ ],
45
+ "errors": [
46
+ "./dist/runtime/errors/index.d.ts"
47
+ ],
48
+ "utils": [
49
+ "./dist/runtime/utils/index.d.ts"
50
+ ]
51
+ }
52
+ },
53
+ "files": [
54
+ "dist",
55
+ "LICENSE",
56
+ "README.md"
57
+ ],
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "https://github.com/thunder-id/thunderid",
61
+ "directory": "packages/nuxt"
62
+ },
63
+ "dependencies": {
64
+ "@nuxt/kit": "3.16.2",
65
+ "defu": "6.1.5",
66
+ "jose": "5.2.0",
67
+ "@thunderid/browser": "^0.0.1",
68
+ "@thunderid/node": "^0.0.1",
69
+ "@thunderid/vue": "^0.0.1"
70
+ },
71
+ "devDependencies": {
72
+ "@nuxt/devtools": "2.4.0",
73
+ "@nuxt/module-builder": "1.0.1",
74
+ "@nuxt/schema": "3.16.2",
75
+ "@nuxt/test-utils": "3.17.2",
76
+ "@types/node": "24.7.2",
77
+ "eslint": "9.39.4",
78
+ "h3": "1.15.11",
79
+ "nuxt": "3.16.2",
80
+ "typescript": "5.9.3",
81
+ "vitest": "4.1.3",
82
+ "@thunderid/eslint-plugin": "^0.0.0",
83
+ "@thunderid/prettier-config": "^0.0.0"
84
+ },
85
+ "peerDependencies": {
86
+ "nuxt": ">=3.10.0",
87
+ "vue": ">=3.5.0"
88
+ },
89
+ "publishConfig": {
90
+ "access": "public"
91
+ },
92
+ "scripts": {
93
+ "build": "nuxt module-build prepare && nuxt module-build build",
94
+ "format:check": "prettier --check --cache .",
95
+ "format:fix": "prettier --write --cache .",
96
+ "lint": "nuxt prepare && eslint .",
97
+ "lint:fix": "nuxt prepare && eslint . --fix",
98
+ "test": "vitest run --passWithNoTests",
99
+ "typecheck": "nuxt module-build prepare && vue-tsc --noEmit"
100
+ }
101
+ }