@thunderid/nuxt 0.1.0 → 0.2.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.
Files changed (30) hide show
  1. package/README.md +2 -1
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +13 -13
  5. package/dist/runtime/components/ThunderIDRoot.js +1 -1
  6. package/dist/runtime/components/actions/SignInButton.js +4 -4
  7. package/dist/runtime/components/actions/SignOutButton.js +3 -3
  8. package/dist/runtime/components/actions/SignUpButton.js +4 -4
  9. package/dist/runtime/components/auth/Callback.js +1 -1
  10. package/dist/runtime/components/auth/SignIn.js +1 -1
  11. package/dist/runtime/components/auth/SignUp.js +6 -5
  12. package/dist/runtime/components/control/Loading.js +2 -2
  13. package/dist/runtime/components/control/SignedIn.js +2 -2
  14. package/dist/runtime/components/control/SignedOut.js +2 -2
  15. package/dist/runtime/components/organization/Organization.js +2 -2
  16. package/dist/runtime/components/user/User.js +2 -2
  17. package/dist/runtime/composables/useThunderID.js +2 -2
  18. package/dist/runtime/middleware/defineThunderIDMiddleware.js +2 -2
  19. package/dist/runtime/plugins/thunderid.js +4 -3
  20. package/dist/runtime/server/ThunderIDNuxtClient.js +9 -11
  21. package/dist/runtime/server/plugins/thunderid-ssr.js +5 -5
  22. package/dist/runtime/server/routes/auth/branding/branding.get.js +1 -1
  23. package/dist/runtime/server/routes/auth/session/callback.get.js +5 -5
  24. package/dist/runtime/server/routes/auth/session/signin.get.js +1 -1
  25. package/dist/runtime/server/utils/session.d.ts +2 -2
  26. package/dist/runtime/server/utils/session.js +9 -9
  27. package/dist/runtime/types.d.ts +2 -2
  28. package/dist/runtime/utils/log.js +1 -1
  29. package/dist/runtime/utils/url-validation.js +1 -1
  30. package/package.json +11 -8
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  ![ThunderID Nuxt SDK](https://raw.githubusercontent.com/thunder-id/thunderid/refs/heads/main/docs/static/assets/images/readme/repo-banner-nuxt-sdk.png)
2
2
 
3
- Nuxt SDK for ThunderID. Provides authentication for Nuxt 3 applications with support for SSR, server routes, and composables.
3
+ Nuxt SDK for ThunderID. Provides authentication for Nuxt 3 applications with support for SSR, server routes, and
4
+ composables.
4
5
 
5
6
  ## Installation
6
7
 
package/dist/module.d.mts CHANGED
@@ -19,7 +19,7 @@ declare module '@nuxt/schema' {
19
19
  clientId: string;
20
20
  platform?: ThunderIDNuxtConfig['platform'];
21
21
  preferences?: ThunderIDNuxtConfig['preferences'];
22
- scopes: string[];
22
+ scopes: string | string[];
23
23
  signInUrl?: string;
24
24
  signUpUrl?: string;
25
25
  };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "configKey": "thunderid",
3
3
  "name": "@thunderid/nuxt",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -13,13 +13,13 @@ const module$1 = defineNuxtModule({
13
13
  const publicConfig = defu(
14
14
  // Layer 1: environment variables — only win when actually set
15
15
  {
16
- afterSignInUrl: process.env["NUXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL"],
17
- afterSignOutUrl: process.env["NUXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL"],
18
- applicationId: process.env["NUXT_PUBLIC_THUNDERID_APPLICATION_ID"],
19
- baseUrl: process.env["NUXT_PUBLIC_THUNDERID_BASE_URL"],
20
- clientId: process.env["NUXT_PUBLIC_THUNDERID_CLIENT_ID"],
21
- signInUrl: process.env["NUXT_PUBLIC_THUNDERID_SIGN_IN_URL"],
22
- signUpUrl: process.env["NUXT_PUBLIC_THUNDERID_SIGN_UP_URL"]
16
+ afterSignInUrl: process.env.NUXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL,
17
+ afterSignOutUrl: process.env.NUXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL,
18
+ applicationId: process.env.NUXT_PUBLIC_THUNDERID_APPLICATION_ID,
19
+ baseUrl: process.env.NUXT_PUBLIC_THUNDERID_BASE_URL,
20
+ clientId: process.env.NUXT_PUBLIC_THUNDERID_CLIENT_ID,
21
+ signInUrl: process.env.NUXT_PUBLIC_THUNDERID_SIGN_IN_URL,
22
+ signUpUrl: process.env.NUXT_PUBLIC_THUNDERID_SIGN_UP_URL
23
23
  },
24
24
  // Layer 2: nuxt.config.ts options
25
25
  userOptions,
@@ -31,8 +31,8 @@ const module$1 = defineNuxtModule({
31
31
  }
32
32
  );
33
33
  const privateConfig = {
34
- clientSecret: process.env["THUNDERID_CLIENT_SECRET"] || userOptions.clientSecret || "",
35
- sessionSecret: process.env["THUNDERID_SESSION_SECRET"] || userOptions.sessionSecret || ""
34
+ clientSecret: process.env.THUNDERID_CLIENT_SECRET || userOptions.clientSecret || "",
35
+ sessionSecret: process.env.THUNDERID_SESSION_SECRET || userOptions.sessionSecret || ""
36
36
  };
37
37
  const { options } = nuxt;
38
38
  options.runtimeConfig.thunderid = defu(
@@ -56,14 +56,14 @@ const module$1 = defineNuxtModule({
56
56
  }
57
57
  );
58
58
  const publicThunderID = options.runtimeConfig.public.thunderid;
59
- if (publicThunderID?.["clientSecret"]) {
60
- delete publicThunderID["clientSecret"];
59
+ if (publicThunderID?.clientSecret) {
60
+ delete publicThunderID.clientSecret;
61
61
  console.error(
62
62
  `[${PACKAGE_NAME}] SECURITY: clientSecret found in public config. Removed. Use THUNDERID_CLIENT_SECRET env var.`
63
63
  );
64
64
  }
65
- if (publicThunderID?.["sessionSecret"]) {
66
- delete publicThunderID["sessionSecret"];
65
+ if (publicThunderID?.sessionSecret) {
66
+ delete publicThunderID.sessionSecret;
67
67
  console.error(
68
68
  `[${PACKAGE_NAME}] SECURITY: sessionSecret found in public config. Removed. Use THUNDERID_SESSION_SECRET env var.`
69
69
  );
@@ -141,7 +141,7 @@ const ThunderIDRoot = defineComponent({
141
141
  revalidateMyOrganizations: shouldFetchOrgs ? revalidateMyOrganizations : void 0
142
142
  },
143
143
  {
144
- default: () => slots["default"]?.()
144
+ default: () => slots.default?.()
145
145
  }
146
146
  )
147
147
  }
@@ -1,7 +1,7 @@
1
+ import { navigateTo } from "#app";
1
2
  import { ThunderIDRuntimeError } from "@thunderid/browser";
2
3
  import { BaseSignInButton } from "@thunderid/vue";
3
4
  import { defineComponent, h, ref } from "vue";
4
- import { navigateTo } from "#app";
5
5
  import { useThunderID } from "#imports";
6
6
  const SignInButton = defineComponent({
7
7
  emits: ["click", "error"],
@@ -34,14 +34,14 @@ const SignInButton = defineComponent({
34
34
  }
35
35
  };
36
36
  return () => {
37
- const slotContent = slots["default"] ? () => slots["default"]({ isLoading: isLoading.value }) : void 0;
37
+ const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
38
38
  return h(
39
39
  BaseSignInButton,
40
40
  {
41
- class: attrs["class"],
41
+ class: attrs.class,
42
42
  isLoading: isLoading.value,
43
43
  onClick: handleSignIn,
44
- style: attrs["style"]
44
+ style: attrs.style
45
45
  },
46
46
  slotContent
47
47
  );
@@ -26,14 +26,14 @@ const SignOutButton = defineComponent({
26
26
  }
27
27
  };
28
28
  return () => {
29
- const slotContent = slots["default"] ? () => slots["default"]({ isLoading: isLoading.value }) : void 0;
29
+ const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
30
30
  return h(
31
31
  BaseSignOutButton,
32
32
  {
33
- class: attrs["class"],
33
+ class: attrs.class,
34
34
  isLoading: isLoading.value,
35
35
  onClick: handleSignOut,
36
- style: attrs["style"]
36
+ style: attrs.style
37
37
  },
38
38
  slotContent
39
39
  );
@@ -1,7 +1,7 @@
1
+ import { navigateTo } from "#app";
1
2
  import { ThunderIDRuntimeError } from "@thunderid/browser";
2
3
  import { BaseSignUpButton } from "@thunderid/vue";
3
4
  import { defineComponent, h, ref } from "vue";
4
- import { navigateTo } from "#app";
5
5
  import { useThunderID } from "#imports";
6
6
  const SignUpButton = defineComponent({
7
7
  emits: ["click", "error"],
@@ -31,14 +31,14 @@ const SignUpButton = defineComponent({
31
31
  }
32
32
  };
33
33
  return () => {
34
- const slotContent = slots["default"] ? () => slots["default"]({ isLoading: isLoading.value }) : void 0;
34
+ const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
35
35
  return h(
36
36
  BaseSignUpButton,
37
37
  {
38
- class: attrs["class"],
38
+ class: attrs.class,
39
39
  isLoading: isLoading.value,
40
40
  onClick: handleSignUp,
41
- style: attrs["style"]
41
+ style: attrs.style
42
42
  },
43
43
  slotContent
44
44
  );
@@ -1,5 +1,5 @@
1
- import { defineComponent, onMounted } from "vue";
2
1
  import { navigateTo } from "#app";
2
+ import { defineComponent, onMounted } from "vue";
3
3
  const error = (msg, ...args) => {
4
4
  console.error(`[@thunderid/nuxt] Callback: ${msg}`, ...args);
5
5
  };
@@ -1,6 +1,6 @@
1
+ import { navigateTo } from "#app";
1
2
  import { BaseSignIn } from "@thunderid/vue";
2
3
  import { defineComponent, h } from "vue";
3
- import { navigateTo } from "#app";
4
4
  import { useThunderID } from "#imports";
5
5
  const SignIn = defineComponent({
6
6
  emits: ["error", "success"],
@@ -1,10 +1,10 @@
1
+ import { navigateTo } from "#app";
1
2
  import {
2
3
  EmbeddedFlowResponseType,
3
4
  EmbeddedFlowType
4
5
  } from "@thunderid/browser";
5
6
  import { BaseSignUp } from "@thunderid/vue";
6
7
  import { defineComponent, h } from "vue";
7
- import { navigateTo } from "#app";
8
8
  import { useThunderID } from "#imports";
9
9
  const SignUp = defineComponent({
10
10
  name: "SignUp",
@@ -24,7 +24,7 @@ const SignUp = defineComponent({
24
24
  variant: { default: "outlined", type: String }
25
25
  },
26
26
  setup(props, { slots }) {
27
- const { signUp, isInitialized, applicationId } = useThunderID();
27
+ const { signUp, isInitialized, applicationId, scopes } = useThunderID();
28
28
  const handleInitialize = async (payload) => {
29
29
  let applicationIdFromUrl = null;
30
30
  if (import.meta.client) {
@@ -34,7 +34,8 @@ const SignUp = defineComponent({
34
34
  const effectiveApplicationId = applicationId || applicationIdFromUrl || void 0;
35
35
  const initialPayload = payload || {
36
36
  flowType: EmbeddedFlowType.Registration,
37
- ...effectiveApplicationId && { applicationId: effectiveApplicationId }
37
+ ...effectiveApplicationId && { applicationId: effectiveApplicationId },
38
+ ...scopes && { scopes }
38
39
  };
39
40
  return await signUp(initialPayload);
40
41
  };
@@ -46,7 +47,7 @@ const SignUp = defineComponent({
46
47
  await navigateTo(oauthRedirectUrl, { external: true });
47
48
  return;
48
49
  }
49
- if (props.shouldRedirectAfterSignUp && response?.type !== EmbeddedFlowResponseType.Redirection && props.afterSignUpUrl) {
50
+ if (props.shouldRedirectAfterSignUp && response?.type !== EmbeddedFlowResponseType.Redirection && props.afterSignUpUrl && !response?.assertion) {
50
51
  await navigateTo(props.afterSignUpUrl, { external: true });
51
52
  }
52
53
  if (props.shouldRedirectAfterSignUp && response?.type === EmbeddedFlowResponseType.Redirection && response?.data?.redirectURL && !response.data.redirectURL.includes("oauth") && !response.data.redirectURL.includes("auth")) {
@@ -72,7 +73,7 @@ const SignUp = defineComponent({
72
73
  size: props.size,
73
74
  variant: props.variant
74
75
  },
75
- slots["default"] ? { default: (renderProps) => slots["default"](renderProps) } : void 0
76
+ slots.default ? { default: (renderProps) => slots.default(renderProps) } : void 0
76
77
  );
77
78
  }
78
79
  });
@@ -6,10 +6,10 @@ const Loading = defineComponent({
6
6
  const { isLoading } = useThunderID();
7
7
  return () => {
8
8
  if (!isLoading.value) {
9
- const fallback = slots["fallback"]?.();
9
+ const fallback = slots.fallback?.();
10
10
  return fallback ? h(Fragment, {}, fallback) : null;
11
11
  }
12
- const content = slots["default"]?.();
12
+ const content = slots.default?.();
13
13
  return content ? h(Fragment, {}, content) : null;
14
14
  };
15
15
  }
@@ -6,10 +6,10 @@ const SignedIn = defineComponent({
6
6
  const { isSignedIn } = useThunderID();
7
7
  return () => {
8
8
  if (!isSignedIn.value) {
9
- const fallback = slots["fallback"]?.();
9
+ const fallback = slots.fallback?.();
10
10
  return fallback ? h(Fragment, {}, fallback) : null;
11
11
  }
12
- const content = slots["default"]?.();
12
+ const content = slots.default?.();
13
13
  return content ? h(Fragment, {}, content) : null;
14
14
  };
15
15
  }
@@ -6,10 +6,10 @@ const SignedOut = defineComponent({
6
6
  const { isSignedIn } = useThunderID();
7
7
  return () => {
8
8
  if (isSignedIn.value) {
9
- const fallback = slots["fallback"]?.();
9
+ const fallback = slots.fallback?.();
10
10
  return fallback ? h(Fragment, {}, fallback) : null;
11
11
  }
12
- const content = slots["default"]?.();
12
+ const content = slots.default?.();
13
13
  return content ? h(Fragment, {}, content) : null;
14
14
  };
15
15
  }
@@ -6,10 +6,10 @@ const Organization = defineComponent({
6
6
  const { currentOrganization } = useOrganization();
7
7
  return () => {
8
8
  if (!currentOrganization?.value) {
9
- const fallback = slots["fallback"]?.();
9
+ const fallback = slots.fallback?.();
10
10
  return fallback ? h(Fragment, {}, fallback) : null;
11
11
  }
12
- const content = slots["default"]?.({ organization: currentOrganization.value });
12
+ const content = slots.default?.({ organization: currentOrganization.value });
13
13
  return content ? h(Fragment, {}, content) : null;
14
14
  };
15
15
  }
@@ -6,10 +6,10 @@ const User = defineComponent({
6
6
  const { user } = useThunderID();
7
7
  return () => {
8
8
  if (!user.value) {
9
- const fallback = slots["fallback"]?.();
9
+ const fallback = slots.fallback?.();
10
10
  return fallback ? h(Fragment, {}, fallback) : null;
11
11
  }
12
- const content = slots["default"]?.({ user: user.value });
12
+ const content = slots.default?.({ user: user.value });
13
13
  return content ? h(Fragment, {}, content) : null;
14
14
  };
15
15
  }
@@ -1,6 +1,6 @@
1
+ import { navigateTo, useState, useRuntimeConfig } from "#app";
1
2
  import { EmbeddedSignInFlowStatus, getRedirectBasedSignUpUrl } from "@thunderid/browser";
2
3
  import { useThunderID as useThunderIDVue } from "@thunderid/vue";
3
- import { navigateTo, useState, useRuntimeConfig } from "#app";
4
4
  export function useThunderID() {
5
5
  const context = useThunderIDVue();
6
6
  const signIn = async (...args) => {
@@ -30,7 +30,7 @@ export function useThunderID() {
30
30
  return res.data;
31
31
  }
32
32
  const options = arg0;
33
- const returnTo = typeof options?.["returnTo"] === "string" ? options["returnTo"] : void 0;
33
+ const returnTo = typeof options?.returnTo === "string" ? options.returnTo : void 0;
34
34
  const url = returnTo ? `/api/auth/signin?returnTo=${encodeURIComponent(returnTo)}` : "/api/auth/signin";
35
35
  await navigateTo(url, { external: true });
36
36
  return void 0;
@@ -9,11 +9,11 @@ export function defineThunderIDMiddleware(options = {}) {
9
9
  return navigateTo(`${redirectTo}?returnTo=${returnTo}`, { external: true });
10
10
  }
11
11
  const user = authState.value.user;
12
- if (requireOrganization && !user?.["organizationId"]) {
12
+ if (requireOrganization && !user?.organizationId) {
13
13
  return navigateTo(redirectTo, { external: true });
14
14
  }
15
15
  if (requireScopes.length > 0) {
16
- const sessionScopes = String(user?.["scopes"] ?? "").split(" ");
16
+ const sessionScopes = String(user?.scopes ?? "").split(" ");
17
17
  const hasAllScopes = requireScopes.every((s) => sessionScopes.includes(s));
18
18
  if (!hasAllScopes) {
19
19
  return navigateTo(redirectTo, { external: true });
@@ -1,8 +1,8 @@
1
+ import { defineNuxtPlugin, useState, useRequestEvent, useRuntimeConfig, navigateTo } from "#app";
1
2
  import { getRedirectBasedSignUpUrl } from "@thunderid/browser";
2
3
  import { ThunderIDPlugin, THUNDERID_KEY } from "@thunderid/vue";
3
4
  import { computed } from "vue";
4
5
  import ThunderIDRoot from "../components/ThunderIDRoot.js";
5
- import { defineNuxtPlugin, useState, useRequestEvent, useRuntimeConfig, navigateTo } from "#app";
6
6
  export default defineNuxtPlugin((nuxtApp) => {
7
7
  const publicConfig = useRuntimeConfig().public.thunderid;
8
8
  if (import.meta.client && import.meta.dev) {
@@ -46,7 +46,7 @@ export default defineNuxtPlugin((nuxtApp) => {
46
46
  user: ssrContext.session?.sub ? { sub: ssrContext.session.sub } : null
47
47
  };
48
48
  } else {
49
- const legacyAuth = event?.context?.["__thunderidAuth"];
49
+ const legacyAuth = event?.context?.__thunderidAuth;
50
50
  authState.value = legacyAuth ?? { isLoading: false, isSignedIn: false, user: null };
51
51
  }
52
52
  }
@@ -60,7 +60,7 @@ export default defineNuxtPlugin((nuxtApp) => {
60
60
  const user = computed(() => authState.value.user ?? null);
61
61
  const organizationRef = computed(() => currentOrgState.value);
62
62
  const signIn = async (options) => {
63
- const returnTo = typeof options?.["returnTo"] === "string" ? options["returnTo"] : void 0;
63
+ const returnTo = typeof options?.returnTo === "string" ? options.returnTo : void 0;
64
64
  const url = returnTo ? `/api/auth/signin?returnTo=${encodeURIComponent(returnTo)}` : "/api/auth/signin";
65
65
  await navigateTo(url, { external: true });
66
66
  };
@@ -112,6 +112,7 @@ export default defineNuxtPlugin((nuxtApp) => {
112
112
  organizationHandle: publicConfig.organizationHandle,
113
113
  platform: void 0,
114
114
  reInitialize: async () => false,
115
+ scopes: publicConfig.scopes,
115
116
  signIn,
116
117
  signInOptions: void 0,
117
118
  signInSilently: noop,
@@ -69,16 +69,15 @@ class ThunderIDNuxtClient extends ThunderIDNodeClient {
69
69
  if (typeof arg0 === "object" && arg0 !== null && "flowId" in arg0) {
70
70
  const sessionId = args[2];
71
71
  if (arg0.flowId === "") {
72
- return this.getSignInUrl(
73
- { client_secret: "{{clientSecret}}", response_mode: "direct" },
74
- sessionId
75
- ).then((authorizeUrl) => {
76
- const url = new URL(authorizeUrl);
77
- return initializeEmbeddedSignInFlow({
78
- payload: Object.fromEntries(url.searchParams.entries()),
79
- url: `${url.origin}${url.pathname}`
80
- });
81
- });
72
+ return this.getSignInUrl({ client_secret: "{{clientSecret}}", response_mode: "direct" }, sessionId).then(
73
+ (authorizeUrl) => {
74
+ const url = new URL(authorizeUrl);
75
+ return initializeEmbeddedSignInFlow({
76
+ payload: Object.fromEntries(url.searchParams.entries()),
77
+ url: `${url.origin}${url.pathname}`
78
+ });
79
+ }
80
+ );
82
81
  }
83
82
  const request = args[1] ?? {};
84
83
  return executeEmbeddedSignInFlow({
@@ -161,7 +160,6 @@ class ThunderIDNuxtClient extends ThunderIDNodeClient {
161
160
  headers: { Authorization: `Bearer ${accessToken}` }
162
161
  });
163
162
  }
164
- // eslint-disable-next-line class-methods-use-this
165
163
  async getBrandingPreference(config) {
166
164
  return getBrandingPreference(config);
167
165
  }
@@ -23,9 +23,9 @@ export default defineNitroPlugin((nitro) => {
23
23
  );
24
24
  return;
25
25
  }
26
- const sessionSecret2 = process.env["THUNDERID_SESSION_SECRET"] || privateConfig?.sessionSecret;
26
+ const sessionSecret2 = process.env.THUNDERID_SESSION_SECRET || privateConfig?.sessionSecret;
27
27
  if (!sessionSecret2) {
28
- if (process.env["NODE_ENV"] === "production") {
28
+ if (process.env.NODE_ENV === "production") {
29
29
  log.error(
30
30
  "THUNDERID_SESSION_SECRET is required in production. Set it to a secure random string of at least 32 characters. Refusing to initialize ThunderID client."
31
31
  );
@@ -58,7 +58,7 @@ export default defineNitroPlugin((nitro) => {
58
58
  const config = useRuntimeConfig(event);
59
59
  const publicConfig = config.public.thunderid;
60
60
  const prefs = publicConfig?.preferences;
61
- const sessionSecret = process.env["THUNDERID_SESSION_SECRET"] || config.thunderid?.sessionSecret;
61
+ const sessionSecret = process.env.THUNDERID_SESSION_SECRET || config.thunderid?.sessionSecret;
62
62
  const session = await verifyAndRehydrateSession(
63
63
  event,
64
64
  sessionSecret
@@ -77,7 +77,7 @@ export default defineNitroPlugin((nitro) => {
77
77
  const idToken = await client.getDecodedIdToken(
78
78
  session.sessionId
79
79
  );
80
- if (idToken?.["user_org"]) {
80
+ if (idToken?.user_org) {
81
81
  resolvedBaseUrl = `${baseUrl}/o`;
82
82
  }
83
83
  }
@@ -130,6 +130,6 @@ export default defineNitroPlugin((nitro) => {
130
130
  isSignedIn: true,
131
131
  user: ssrData.user
132
132
  };
133
- eventContext["__thunderidAuth"] = authState;
133
+ eventContext.__thunderidAuth = authState;
134
134
  });
135
135
  });
@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
21
21
  const idToken = await client.getDecodedIdToken(
22
22
  session.sessionId
23
23
  );
24
- if (idToken?.["user_org"]) {
24
+ if (idToken?.user_org) {
25
25
  resolvedBaseUrl = `${baseUrl}/o`;
26
26
  }
27
27
  }
@@ -12,11 +12,11 @@ export default defineEventHandler(async (event) => {
12
12
  const sessionSecret = config.thunderid?.sessionSecret;
13
13
  const publicConfig = config.public.thunderid;
14
14
  const query = getQuery(event);
15
- const code = query["code"];
16
- const state = query["state"];
17
- const sessionState = query["session_state"];
18
- const error = query["error"];
19
- const errorDescription = query["error_description"];
15
+ const code = query.code;
16
+ const state = query.state;
17
+ const sessionState = query.session_state;
18
+ const error = query.error;
19
+ const errorDescription = query.error_description;
20
20
  if (error) {
21
21
  throw createError({
22
22
  statusCode: 400,
@@ -8,7 +8,7 @@ export default defineEventHandler(async (event) => {
8
8
  const config = useRuntimeConfig();
9
9
  const sessionSecret = config.thunderid?.sessionSecret;
10
10
  const query = getQuery(event);
11
- const returnTo = query["returnTo"];
11
+ const returnTo = query.returnTo;
12
12
  const safeReturnTo = returnTo && returnTo.startsWith("/") && !returnTo.startsWith("//") ? returnTo : void 0;
13
13
  const sessionId = generateSessionId();
14
14
  const tempToken = await createTempSessionToken(sessionId, sessionSecret, safeReturnTo);
@@ -61,13 +61,13 @@ export declare function getTempSessionCookieName(): string;
61
61
  /**
62
62
  * Session cookie options.
63
63
  */
64
- type SessionCookieOptions = {
64
+ interface SessionCookieOptions {
65
65
  httpOnly: boolean;
66
66
  maxAge: number;
67
67
  path: string;
68
68
  sameSite: 'lax';
69
69
  secure: boolean;
70
- };
70
+ }
71
71
  export declare function getSessionCookieOptions(): SessionCookieOptions;
72
72
  /**
73
73
  * Temp session cookie options (15 min TTL).
@@ -3,9 +3,9 @@ import { setCookie } from "h3";
3
3
  import { SignJWT, jwtVerify } from "jose";
4
4
  const DEFAULT_EXPIRY_SECONDS = 3600;
5
5
  function getSecret(sessionSecret) {
6
- const secret = sessionSecret || process.env["THUNDERID_SESSION_SECRET"];
6
+ const secret = sessionSecret || process.env.THUNDERID_SESSION_SECRET;
7
7
  if (!secret) {
8
- if (process.env["NODE_ENV"] === "production") {
8
+ if (process.env.NODE_ENV === "production") {
9
9
  throw new Error(
10
10
  "[thunderid] THUNDERID_SESSION_SECRET environment variable is required in production. Set it to a secure random string of at least 32 characters."
11
11
  );
@@ -37,7 +37,7 @@ export async function createTempSessionToken(sessionId, sessionSecret, returnTo)
37
37
  type: "temp"
38
38
  };
39
39
  if (returnTo) {
40
- payload["returnTo"] = returnTo;
40
+ payload.returnTo = returnTo;
41
41
  }
42
42
  return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime("15m").sign(secret);
43
43
  }
@@ -49,12 +49,12 @@ export async function verifySessionToken(token, sessionSecret) {
49
49
  export async function verifyTempSessionToken(token, sessionSecret) {
50
50
  const secret = getSecret(sessionSecret);
51
51
  const { payload } = await jwtVerify(token, secret);
52
- if (payload["type"] !== "temp") {
52
+ if (payload.type !== "temp") {
53
53
  throw new Error("Invalid token type: expected temp session");
54
54
  }
55
55
  return {
56
- returnTo: payload["returnTo"],
57
- sessionId: payload["sessionId"]
56
+ returnTo: payload.returnTo,
57
+ sessionId: payload.sessionId
58
58
  };
59
59
  }
60
60
  export function getSessionCookieName() {
@@ -69,7 +69,7 @@ export function getSessionCookieOptions() {
69
69
  maxAge: DEFAULT_EXPIRY_SECONDS,
70
70
  path: "/",
71
71
  sameSite: "lax",
72
- secure: process.env["NODE_ENV"] === "production"
72
+ secure: process.env.NODE_ENV === "production"
73
73
  };
74
74
  }
75
75
  export function getTempSessionCookieOptions() {
@@ -78,7 +78,7 @@ export function getTempSessionCookieOptions() {
78
78
  maxAge: 15 * 60,
79
79
  path: "/",
80
80
  sameSite: "lax",
81
- secure: process.env["NODE_ENV"] === "production"
81
+ secure: process.env.NODE_ENV === "production"
82
82
  };
83
83
  }
84
84
  export async function issueSessionCookie(event, sessionId, tokenResponse, sessionSecret) {
@@ -86,7 +86,7 @@ export async function issueSessionCookie(event, sessionId, tokenResponse, sessio
86
86
  const client = ThunderIDNuxtClient.getInstance();
87
87
  const idToken = await client.getDecodedIdToken(sessionId, tokenResponse.idToken);
88
88
  const userId = idToken.sub || sessionId;
89
- const organizationId = idToken["user_org"] || idToken["organization_id"];
89
+ const organizationId = idToken.user_org || idToken.organization_id;
90
90
  const expiresInSeconds = parseInt(tokenResponse.expiresIn ?? "3600", 10);
91
91
  const accessTokenExpiresAt = Math.floor(Date.now() / 1e3) + (Number.isFinite(expiresInSeconds) ? expiresInSeconds : 3600);
92
92
  const sessionToken = await createSessionToken(
@@ -30,7 +30,7 @@ export interface ThunderIDNuxtConfig {
30
30
  * URL when present. Mirrors `applicationId` in the React/Next.js SDKs.
31
31
  */
32
32
  applicationId?: string;
33
- /** Base URL of the ThunderID org tenant (e.g. https://api.asgardeo.io/t/your_org) */
33
+ /** Base URL of the ThunderID org tenant (e.g. https://localhost:8090) */
34
34
  baseUrl?: string;
35
35
  /** OAuth2 Client ID */
36
36
  clientId?: string;
@@ -72,7 +72,7 @@ export interface ThunderIDNuxtConfig {
72
72
  };
73
73
  };
74
74
  /** OAuth2 scopes to request */
75
- scopes?: string[];
75
+ scopes?: string | string[];
76
76
  /** Secret for signing session JWTs (use THUNDERID_SESSION_SECRET env var) */
77
77
  sessionSecret?: string;
78
78
  /**
@@ -8,7 +8,7 @@ export function createLogger(subsystem) {
8
8
  const tag = `[${PREFIX}:${subsystem}]`;
9
9
  return {
10
10
  debug: (...args) => {
11
- if (process.env["THUNDERID_DEBUG"]) {
11
+ if (process.env.THUNDERID_DEBUG) {
12
12
  console.log(tag, ...args);
13
13
  }
14
14
  },
@@ -1,5 +1,5 @@
1
- import { ThunderIDError } from "../errors/thunderid-error.js";
2
1
  import { ErrorCode } from "../errors/error-codes.js";
2
+ import { ThunderIDError } from "../errors/thunderid-error.js";
3
3
  export function validateReturnUrl(url) {
4
4
  if (typeof url !== "string" || url.trim() === "") {
5
5
  throw new ThunderIDError("returnTo must be a non-empty string.", ErrorCode.OpenRedirectBlocked, { statusCode: 400 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thunderid/nuxt",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Nuxt SDK for ThunderID",
5
5
  "keywords": [
6
6
  "thunderid",
@@ -61,24 +61,24 @@
61
61
  "directory": "packages/nuxt"
62
62
  },
63
63
  "dependencies": {
64
- "@nuxt/kit": "3.21.6",
64
+ "@nuxt/kit": "3.21.7",
65
65
  "defu": "6.1.5",
66
66
  "jose": "5.2.0",
67
- "@thunderid/node": "^0.1.0",
68
- "@thunderid/browser": "^0.1.0",
69
- "@thunderid/vue": "^0.1.0"
67
+ "@thunderid/node": "^0.2.0",
68
+ "@thunderid/vue": "^0.3.0",
69
+ "@thunderid/browser": "^0.3.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@nuxt/devtools": "2.6.4",
73
73
  "@nuxt/module-builder": "1.0.1",
74
- "@nuxt/schema": "3.21.6",
74
+ "@nuxt/schema": "3.21.7",
75
75
  "@nuxt/test-utils": "3.17.2",
76
76
  "@types/node": "24.7.2",
77
77
  "eslint": "9.39.4",
78
78
  "h3": "1.15.11",
79
- "nuxt": "3.21.6",
79
+ "nuxt": "3.21.7",
80
80
  "typescript": "5.9.3",
81
- "vitest": "4.1.3",
81
+ "vitest": "4.1.8",
82
82
  "@thunderid/eslint-plugin": "^0.0.0",
83
83
  "@thunderid/prettier-config": "^0.0.0"
84
84
  },
@@ -91,6 +91,9 @@
91
91
  },
92
92
  "scripts": {
93
93
  "build": "nuxt module-build prepare && nuxt module-build build",
94
+ "clean": "pnpm clean:dist && pnpm clean:node_modules",
95
+ "clean:dist": "rimraf dist .nuxt",
96
+ "clean:node_modules": "rimraf node_modules",
94
97
  "format:check": "prettier --check --cache .",
95
98
  "format:fix": "prettier --write --cache .",
96
99
  "lint": "nuxt prepare && eslint .",