@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,29 @@
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 { UserProfile } from '@thunderid/node';
19
+ /**
20
+ * GET /api/auth/user/profile
21
+ *
22
+ * Returns the full SCIM2 {@link UserProfile} (with `flattenedProfile` and
23
+ * `schemas`) for the authenticated user. Used by `ThunderIDRoot.revalidateProfile`
24
+ * to refresh client-side state after a profile update.
25
+ *
26
+ * Mirrors `getUserProfileAction` in the Next.js SDK.
27
+ */
28
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<UserProfile>>;
29
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { defineEventHandler, createError } from "h3";
2
+ import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
3
+ import { verifyAndRehydrateSession } from "../../../utils/serverSession.js";
4
+ import { useRuntimeConfig } from "#imports";
5
+ export default defineEventHandler(async (event) => {
6
+ const config = useRuntimeConfig();
7
+ const sessionSecret = config.thunderid?.sessionSecret;
8
+ const session = await verifyAndRehydrateSession(
9
+ event,
10
+ sessionSecret
11
+ );
12
+ if (!session) {
13
+ throw createError({ statusCode: 401, statusMessage: "Unauthorized: Invalid or expired session." });
14
+ }
15
+ try {
16
+ const client = ThunderIDNuxtClient.getInstance();
17
+ return await client.getUserProfile(session.sessionId);
18
+ } catch (err) {
19
+ throw createError({
20
+ statusCode: 500,
21
+ statusMessage: `Failed to retrieve user profile: ${err instanceof Error ? err.message : String(err)}`
22
+ });
23
+ }
24
+ });
@@ -0,0 +1,35 @@
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 { User } from '@thunderid/node';
19
+ /**
20
+ * PATCH /api/auth/user/profile
21
+ *
22
+ * Updates the SCIM2 /Me profile for the authenticated user.
23
+ * Mirrors the `updateUserProfileAction` Next.js server action.
24
+ *
25
+ * Request body: {@link UpdateMeProfileConfig} (the SCIM patch payload).
26
+ * Response: `{ data: { user: User }; success: boolean; error: string }`
27
+ */
28
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
29
+ data: {
30
+ user: User;
31
+ };
32
+ error: string;
33
+ success: boolean;
34
+ }>>;
35
+ export default _default;
@@ -0,0 +1,41 @@
1
+ import { Platform } from "@thunderid/node";
2
+ import { defineEventHandler, readBody, createError } from "h3";
3
+ import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
4
+ import { verifyAndRehydrateSession } from "../../../utils/serverSession.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export default defineEventHandler(
7
+ async (event) => {
8
+ const config = useRuntimeConfig();
9
+ const sessionSecret = config.thunderid?.sessionSecret;
10
+ const publicConfig = config.public.thunderid;
11
+ if (publicConfig?.platform === Platform.ThunderIDV2) {
12
+ throw createError({
13
+ statusCode: 501,
14
+ statusMessage: "Profile updates are not supported for the ThunderIDV2 (Thunder) platform."
15
+ });
16
+ }
17
+ const session = await verifyAndRehydrateSession(
18
+ event,
19
+ sessionSecret
20
+ );
21
+ if (!session) {
22
+ throw createError({ statusCode: 401, statusMessage: "Unauthorized: Invalid or expired session." });
23
+ }
24
+ let payload;
25
+ try {
26
+ payload = await readBody(event);
27
+ } catch {
28
+ throw createError({ statusCode: 400, statusMessage: "Invalid request body." });
29
+ }
30
+ try {
31
+ const client = ThunderIDNuxtClient.getInstance();
32
+ const user = await client.updateUserProfile(payload, session.sessionId);
33
+ return { data: { user }, error: "", success: true };
34
+ } catch (err) {
35
+ throw createError({
36
+ statusCode: 500,
37
+ statusMessage: `Failed to update user profile: ${err instanceof Error ? err.message : String(err)}`
38
+ });
39
+ }
40
+ }
41
+ );
@@ -0,0 +1,25 @@
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
+ * GET /api/auth/user
20
+ *
21
+ * Returns user information for the current session.
22
+ * Requires a valid session.
23
+ */
24
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("@thunderid/javascript").User>>;
25
+ export default _default;
@@ -0,0 +1,21 @@
1
+ import { defineEventHandler, createError } from "h3";
2
+ import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
3
+ import { verifyAndRehydrateSession } from "../../../utils/serverSession.js";
4
+ import { useRuntimeConfig } from "#imports";
5
+ export default defineEventHandler(async (event) => {
6
+ const config = useRuntimeConfig();
7
+ const sessionSecret = config.thunderid?.sessionSecret;
8
+ const session = await verifyAndRehydrateSession(
9
+ event,
10
+ sessionSecret
11
+ );
12
+ if (!session) {
13
+ throw createError({ statusCode: 401, statusMessage: "Unauthorized: Invalid or expired session." });
14
+ }
15
+ try {
16
+ const client = ThunderIDNuxtClient.getInstance();
17
+ return await client.getUser(session.sessionId);
18
+ } catch {
19
+ throw createError({ statusCode: 500, statusMessage: "Failed to retrieve user information." });
20
+ }
21
+ });
@@ -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
+ import type { H3Event } from 'h3';
19
+ import type { ThunderIDSessionPayload, ThunderIDSSRData } from '../../types.js';
20
+ /**
21
+ * The typed shape of `event.context.thunderid` set by the ThunderID Nitro plugin
22
+ * on every SSR request.
23
+ */
24
+ export interface ThunderIDEventContext {
25
+ /** Convenience boolean derived from the session presence. */
26
+ isSignedIn: boolean;
27
+ /** The decoded session payload, or null when the user is not signed in. */
28
+ session: ThunderIDSessionPayload | null;
29
+ /** SSR-prefetched data (user profile, orgs, branding). Present only after the SSR plugin runs. */
30
+ ssr?: ThunderIDSSRData;
31
+ }
32
+ /**
33
+ * Typed accessor for `event.context.thunderid`.
34
+ *
35
+ * Returns null when called before the ThunderID SSR plugin has populated
36
+ * the context (e.g. in non-Nuxt Nitro routes that run before the plugin).
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { getThunderIDContext } from '@thunderid/nuxt/server';
41
+ *
42
+ * export default defineEventHandler((event) => {
43
+ * const ctx = getThunderIDContext(event);
44
+ * if (!ctx?.isSignedIn) throw createError({ statusCode: 401 });
45
+ * return { userId: ctx.session!.sub };
46
+ * });
47
+ * ```
48
+ */
49
+ export declare function getThunderIDContext(event: H3Event): ThunderIDEventContext | null;
@@ -0,0 +1,3 @@
1
+ export function getThunderIDContext(event) {
2
+ return event.context.thunderid ?? null;
3
+ }
@@ -0,0 +1,65 @@
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 { H3Event } from 'h3';
19
+ import type { ThunderIDSessionPayload } from '../../types.js';
20
+ /**
21
+ * Get the current session from the request cookie.
22
+ * Returns the session payload if valid, or null if no session exists.
23
+ *
24
+ * Use this in custom server API routes to access session data.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * // server/api/me.get.ts
29
+ * export default defineEventHandler(async (event) => {
30
+ * const session = await useServerSession(event);
31
+ * if (!session) {
32
+ * throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
33
+ * }
34
+ * return { sessionId: session.sessionId, sub: session.sub };
35
+ * });
36
+ * ```
37
+ */
38
+ export declare function useServerSession(event: H3Event): Promise<ThunderIDSessionPayload | null>;
39
+ /**
40
+ * Get the current session or throw a 401 error.
41
+ * Use this when the route must be authenticated.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // server/api/protected-data.get.ts
46
+ * export default defineEventHandler(async (event) => {
47
+ * const session = await requireServerSession(event);
48
+ * // session is guaranteed to be non-null here
49
+ * return { userId: session.sub };
50
+ * });
51
+ * ```
52
+ */
53
+ export declare function requireServerSession(event: H3Event): Promise<ThunderIDSessionPayload>;
54
+ /**
55
+ * Verify the session cookie and rehydrate the legacy in-memory token store
56
+ * from its payload. Used internally by the SDK's SSR plugin and /api/auth/*
57
+ * routes so that subsequent calls which look up tokens by `sessionId`
58
+ * (`getAccessToken`, `getUser`, `getDecodedIdToken`, `signOut`) still succeed
59
+ * after a server restart, when the in-memory store is empty but the signed
60
+ * session cookie is still valid.
61
+ *
62
+ * Returns `null` for missing or invalid cookies — callers decide whether to
63
+ * 401 or silently treat the user as unauthenticated.
64
+ */
65
+ export declare function verifyAndRehydrateSession(event: H3Event, sessionSecret?: string): Promise<ThunderIDSessionPayload | null>;
@@ -0,0 +1,44 @@
1
+ import { getCookie, createError } from "h3";
2
+ import { verifySessionToken, getSessionCookieName } from "./session.js";
3
+ import ThunderIDNuxtClient from "../ThunderIDNuxtClient.js";
4
+ import { useRuntimeConfig } from "#imports";
5
+ export async function useServerSession(event) {
6
+ const config = useRuntimeConfig();
7
+ const sessionSecret = config.thunderid?.sessionSecret;
8
+ const sessionCookie = getCookie(event, getSessionCookieName());
9
+ if (!sessionCookie) {
10
+ return null;
11
+ }
12
+ try {
13
+ return await verifySessionToken(sessionCookie, sessionSecret);
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+ export async function requireServerSession(event) {
19
+ const session = await useServerSession(event);
20
+ if (!session) {
21
+ throw createError({
22
+ statusCode: 401,
23
+ statusMessage: "Unauthorized: Authentication required."
24
+ });
25
+ }
26
+ return session;
27
+ }
28
+ export async function verifyAndRehydrateSession(event, sessionSecret) {
29
+ const sessionCookie = getCookie(event, getSessionCookieName());
30
+ if (!sessionCookie) {
31
+ return null;
32
+ }
33
+ let session;
34
+ try {
35
+ session = await verifySessionToken(sessionCookie, sessionSecret);
36
+ } catch {
37
+ return null;
38
+ }
39
+ try {
40
+ await ThunderIDNuxtClient.getInstance().rehydrateSessionFromPayload(session);
41
+ } catch {
42
+ }
43
+ return session;
44
+ }
@@ -0,0 +1,85 @@
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 { TokenResponse } from '@thunderid/node';
19
+ import type { H3Event } from 'h3';
20
+ import type { ThunderIDSessionPayload } from '../../types.js';
21
+ /**
22
+ * Create a signed JWT session token.
23
+ */
24
+ export declare function createSessionToken(params: {
25
+ accessToken: string;
26
+ /** Unix timestamp (seconds) when the access token expires. */
27
+ accessTokenExpiresAt?: number;
28
+ expirySeconds?: number;
29
+ /** Raw ID token string. */
30
+ idToken?: string;
31
+ organizationId?: string;
32
+ /** Refresh token for silent re-auth. */
33
+ refreshToken?: string;
34
+ scopes: string;
35
+ sessionId: string;
36
+ userId: string;
37
+ }, sessionSecret?: string): Promise<string>;
38
+ /**
39
+ * Create a signed JWT temp session token (used during OAuth flow).
40
+ */
41
+ export declare function createTempSessionToken(sessionId: string, sessionSecret?: string, returnTo?: string): Promise<string>;
42
+ /**
43
+ * Verify and decode a session JWT.
44
+ */
45
+ export declare function verifySessionToken(token: string, sessionSecret?: string): Promise<ThunderIDSessionPayload>;
46
+ /**
47
+ * Verify and decode a temp session JWT.
48
+ */
49
+ export declare function verifyTempSessionToken(token: string, sessionSecret?: string): Promise<{
50
+ returnTo?: string;
51
+ sessionId: string;
52
+ }>;
53
+ /**
54
+ * Session cookie name.
55
+ */
56
+ export declare function getSessionCookieName(): string;
57
+ /**
58
+ * Temp session cookie name.
59
+ */
60
+ export declare function getTempSessionCookieName(): string;
61
+ /**
62
+ * Session cookie options.
63
+ */
64
+ type SessionCookieOptions = {
65
+ httpOnly: boolean;
66
+ maxAge: number;
67
+ path: string;
68
+ sameSite: 'lax';
69
+ secure: boolean;
70
+ };
71
+ export declare function getSessionCookieOptions(): SessionCookieOptions;
72
+ /**
73
+ * Temp session cookie options (15 min TTL).
74
+ */
75
+ export declare function getTempSessionCookieOptions(): SessionCookieOptions;
76
+ /**
77
+ * Decode a token response into a signed session JWT and write it as the
78
+ * session cookie on the H3 event.
79
+ *
80
+ * Extracted from the inline blocks in `callback.get.ts` and
81
+ * `organizations/switch.post.ts` so that all three callers (callback.get,
82
+ * switch.post, and the new `signin.post`) share one implementation.
83
+ */
84
+ export declare function issueSessionCookie(event: H3Event, sessionId: string, tokenResponse: TokenResponse, sessionSecret?: string): Promise<void>;
85
+ export {};
@@ -0,0 +1,106 @@
1
+ import { CookieConfig } from "@thunderid/node";
2
+ import { setCookie } from "h3";
3
+ import { SignJWT, jwtVerify } from "jose";
4
+ const DEFAULT_EXPIRY_SECONDS = 3600;
5
+ function getSecret(sessionSecret) {
6
+ const secret = sessionSecret || process.env["THUNDERID_SESSION_SECRET"];
7
+ if (!secret) {
8
+ if (process.env["NODE_ENV"] === "production") {
9
+ throw new Error(
10
+ "[thunderid] THUNDERID_SESSION_SECRET environment variable is required in production. Set it to a secure random string of at least 32 characters."
11
+ );
12
+ }
13
+ console.warn(
14
+ "[thunderid] Using default session secret for development. Set THUNDERID_SESSION_SECRET for production."
15
+ );
16
+ return new TextEncoder().encode("thunderid-dev-secret-not-for-production");
17
+ }
18
+ return new TextEncoder().encode(secret);
19
+ }
20
+ export async function createSessionToken(params, sessionSecret) {
21
+ const secret = getSecret(sessionSecret);
22
+ return new SignJWT({
23
+ accessToken: params.accessToken,
24
+ accessTokenExpiresAt: params.accessTokenExpiresAt,
25
+ idToken: params.idToken,
26
+ organizationId: params.organizationId,
27
+ refreshToken: params.refreshToken,
28
+ scopes: params.scopes,
29
+ sessionId: params.sessionId,
30
+ type: "session"
31
+ }).setProtectedHeader({ alg: "HS256" }).setSubject(params.userId).setIssuedAt().setExpirationTime(Date.now() / 1e3 + (params.expirySeconds ?? DEFAULT_EXPIRY_SECONDS)).sign(secret);
32
+ }
33
+ export async function createTempSessionToken(sessionId, sessionSecret, returnTo) {
34
+ const secret = getSecret(sessionSecret);
35
+ const payload = {
36
+ sessionId,
37
+ type: "temp"
38
+ };
39
+ if (returnTo) {
40
+ payload["returnTo"] = returnTo;
41
+ }
42
+ return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime("15m").sign(secret);
43
+ }
44
+ export async function verifySessionToken(token, sessionSecret) {
45
+ const secret = getSecret(sessionSecret);
46
+ const { payload } = await jwtVerify(token, secret);
47
+ return payload;
48
+ }
49
+ export async function verifyTempSessionToken(token, sessionSecret) {
50
+ const secret = getSecret(sessionSecret);
51
+ const { payload } = await jwtVerify(token, secret);
52
+ if (payload["type"] !== "temp") {
53
+ throw new Error("Invalid token type: expected temp session");
54
+ }
55
+ return {
56
+ returnTo: payload["returnTo"],
57
+ sessionId: payload["sessionId"]
58
+ };
59
+ }
60
+ export function getSessionCookieName() {
61
+ return CookieConfig.SESSION_COOKIE_NAME;
62
+ }
63
+ export function getTempSessionCookieName() {
64
+ return CookieConfig.TEMP_SESSION_COOKIE_NAME;
65
+ }
66
+ export function getSessionCookieOptions() {
67
+ return {
68
+ httpOnly: true,
69
+ maxAge: DEFAULT_EXPIRY_SECONDS,
70
+ path: "/",
71
+ sameSite: "lax",
72
+ secure: process.env["NODE_ENV"] === "production"
73
+ };
74
+ }
75
+ export function getTempSessionCookieOptions() {
76
+ return {
77
+ httpOnly: true,
78
+ maxAge: 15 * 60,
79
+ path: "/",
80
+ sameSite: "lax",
81
+ secure: process.env["NODE_ENV"] === "production"
82
+ };
83
+ }
84
+ export async function issueSessionCookie(event, sessionId, tokenResponse, sessionSecret) {
85
+ const { default: ThunderIDNuxtClient } = await import("../ThunderIDNuxtClient.js");
86
+ const client = ThunderIDNuxtClient.getInstance();
87
+ const idToken = await client.getDecodedIdToken(sessionId, tokenResponse.idToken);
88
+ const userId = idToken.sub || sessionId;
89
+ const organizationId = idToken["user_org"] || idToken["organization_id"];
90
+ const expiresInSeconds = parseInt(tokenResponse.expiresIn ?? "3600", 10);
91
+ const accessTokenExpiresAt = Math.floor(Date.now() / 1e3) + (Number.isFinite(expiresInSeconds) ? expiresInSeconds : 3600);
92
+ const sessionToken = await createSessionToken(
93
+ {
94
+ accessToken: tokenResponse.accessToken,
95
+ accessTokenExpiresAt,
96
+ idToken: tokenResponse.idToken || void 0,
97
+ organizationId,
98
+ refreshToken: tokenResponse.refreshToken || void 0,
99
+ scopes: tokenResponse.scope || "",
100
+ sessionId,
101
+ userId
102
+ },
103
+ sessionSecret
104
+ );
105
+ setCookie(event, getSessionCookieName(), sessionToken, getSessionCookieOptions());
106
+ }
@@ -0,0 +1,42 @@
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 H3Event } from 'h3';
19
+ /**
20
+ * Return a valid access token for the current request.
21
+ *
22
+ * If the stored access token is still fresh (or has no expiry metadata —
23
+ * e.g. sessions created before Phase 2), it is returned as-is.
24
+ *
25
+ * When the token is within `REFRESH_SKEW_SECONDS` of expiring and a
26
+ * refresh token is present, a `refresh_token` grant is sent to the OIDC
27
+ * token endpoint. On success the session cookie is reissued with the new
28
+ * tokens so subsequent calls within the same browser session are also fresh.
29
+ *
30
+ * Throws a 401 if the token is expired and no refresh token is available, or
31
+ * if the refresh call itself fails.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * // In a Nitro API route:
36
+ * export default defineEventHandler(async (event) => {
37
+ * const accessToken = await getValidAccessToken(event);
38
+ * // use accessToken to call a protected API
39
+ * });
40
+ * ```
41
+ */
42
+ export declare function getValidAccessToken(event: H3Event): Promise<string>;
@@ -0,0 +1,65 @@
1
+ import { createError, setCookie } from "h3";
2
+ import { requireServerSession } from "./serverSession.js";
3
+ import { createSessionToken, getSessionCookieName, getSessionCookieOptions } from "./session.js";
4
+ import { useRuntimeConfig } from "#imports";
5
+ const REFRESH_SKEW_SECONDS = 60;
6
+ export async function getValidAccessToken(event) {
7
+ const session = await requireServerSession(event);
8
+ const now = Math.floor(Date.now() / 1e3);
9
+ if (!session.accessTokenExpiresAt || session.accessTokenExpiresAt - REFRESH_SKEW_SECONDS > now) {
10
+ return session.accessToken;
11
+ }
12
+ if (!session.refreshToken) {
13
+ throw createError({
14
+ statusCode: 401,
15
+ statusMessage: "Session expired. Please sign in again."
16
+ });
17
+ }
18
+ const config = useRuntimeConfig(event);
19
+ const publicConfig = config.public.thunderid;
20
+ const privateConfig = config.thunderid;
21
+ const tokenEndpoint = `${publicConfig.baseUrl}/oauth2/token`;
22
+ const body = new URLSearchParams({
23
+ client_id: publicConfig.clientId,
24
+ grant_type: "refresh_token",
25
+ refresh_token: session.refreshToken
26
+ });
27
+ if (privateConfig?.clientSecret) {
28
+ body.set("client_secret", privateConfig.clientSecret);
29
+ }
30
+ let refreshed;
31
+ try {
32
+ const res = await fetch(tokenEndpoint, {
33
+ body,
34
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
35
+ method: "POST"
36
+ });
37
+ if (!res.ok) {
38
+ const errText = await res.text().catch(() => String(res.status));
39
+ throw new Error(`Token endpoint returned ${res.status}: ${errText}`);
40
+ }
41
+ refreshed = await res.json();
42
+ } catch (err) {
43
+ const msg = err instanceof Error ? err.message : String(err);
44
+ console.error("[thunderid] Token refresh failed:", msg);
45
+ throw createError({
46
+ statusCode: 401,
47
+ statusMessage: "Token refresh failed. Please sign in again."
48
+ });
49
+ }
50
+ const newSessionToken = await createSessionToken(
51
+ {
52
+ accessToken: refreshed.access_token,
53
+ accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600),
54
+ idToken: refreshed.id_token ?? session.idToken,
55
+ organizationId: session.organizationId,
56
+ refreshToken: refreshed.refresh_token ?? session.refreshToken,
57
+ scopes: refreshed.scope ?? session.scopes,
58
+ sessionId: session.sessionId,
59
+ userId: session.sub
60
+ },
61
+ privateConfig?.sessionSecret
62
+ );
63
+ setCookie(event, getSessionCookieName(), newSessionToken, getSessionCookieOptions());
64
+ return refreshed.access_token;
65
+ }