@thunderid/nuxt 0.1.0 → 0.2.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.
- package/README.md +2 -1
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +13 -13
- package/dist/runtime/components/ThunderIDRoot.js +1 -1
- package/dist/runtime/components/actions/SignInButton.js +4 -4
- package/dist/runtime/components/actions/SignOutButton.js +3 -3
- package/dist/runtime/components/actions/SignUpButton.js +4 -4
- package/dist/runtime/components/auth/Callback.js +1 -1
- package/dist/runtime/components/auth/SignIn.js +1 -1
- package/dist/runtime/components/auth/SignUp.js +7 -9
- package/dist/runtime/components/control/Loading.js +2 -2
- package/dist/runtime/components/control/SignedIn.js +2 -2
- package/dist/runtime/components/control/SignedOut.js +2 -2
- package/dist/runtime/components/organization/Organization.js +2 -2
- package/dist/runtime/components/user/User.js +2 -2
- package/dist/runtime/composables/useThunderID.js +3 -14
- package/dist/runtime/middleware/defineThunderIDMiddleware.js +2 -2
- package/dist/runtime/plugins/thunderid.js +4 -3
- package/dist/runtime/server/ThunderIDNuxtClient.d.ts +2 -3
- package/dist/runtime/server/ThunderIDNuxtClient.js +3 -36
- package/dist/runtime/server/plugins/thunderid-ssr.js +5 -5
- package/dist/runtime/server/routes/auth/branding/branding.get.js +1 -1
- package/dist/runtime/server/routes/auth/session/callback.get.js +5 -5
- package/dist/runtime/server/routes/auth/session/signin.get.js +1 -1
- package/dist/runtime/server/routes/auth/session/signin.post.d.ts +1 -1
- package/dist/runtime/server/routes/auth/session/signin.post.js +1 -1
- package/dist/runtime/server/routes/auth/session/signup.post.js +2 -2
- package/dist/runtime/server/utils/session.d.ts +2 -2
- package/dist/runtime/server/utils/session.js +9 -9
- package/dist/runtime/types.d.ts +3 -9
- package/dist/runtime/utils/log.js +1 -1
- package/dist/runtime/utils/url-validation.js +1 -1
- package/package.json +15 -12
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
Nuxt SDK for ThunderID. Provides authentication for Nuxt 3 applications with support for SSR, server routes, and
|
|
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
package/dist/module.json
CHANGED
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
|
|
17
|
-
afterSignOutUrl: process.env
|
|
18
|
-
applicationId: process.env
|
|
19
|
-
baseUrl: process.env
|
|
20
|
-
clientId: process.env
|
|
21
|
-
signInUrl: process.env
|
|
22
|
-
signUpUrl: process.env
|
|
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
|
|
35
|
-
sessionSecret: process.env
|
|
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?.
|
|
60
|
-
delete publicThunderID
|
|
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?.
|
|
66
|
-
delete publicThunderID
|
|
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
|
);
|
|
@@ -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
|
|
37
|
+
const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
|
|
38
38
|
return h(
|
|
39
39
|
BaseSignInButton,
|
|
40
40
|
{
|
|
41
|
-
class: attrs
|
|
41
|
+
class: attrs.class,
|
|
42
42
|
isLoading: isLoading.value,
|
|
43
43
|
onClick: handleSignIn,
|
|
44
|
-
style: attrs
|
|
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
|
|
29
|
+
const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
|
|
30
30
|
return h(
|
|
31
31
|
BaseSignOutButton,
|
|
32
32
|
{
|
|
33
|
-
class: attrs
|
|
33
|
+
class: attrs.class,
|
|
34
34
|
isLoading: isLoading.value,
|
|
35
35
|
onClick: handleSignOut,
|
|
36
|
-
style: attrs
|
|
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
|
|
34
|
+
const slotContent = slots.default ? () => slots.default({ isLoading: isLoading.value }) : void 0;
|
|
35
35
|
return h(
|
|
36
36
|
BaseSignUpButton,
|
|
37
37
|
{
|
|
38
|
-
class: attrs
|
|
38
|
+
class: attrs.class,
|
|
39
39
|
isLoading: isLoading.value,
|
|
40
40
|
onClick: handleSignUp,
|
|
41
|
-
style: attrs
|
|
41
|
+
style: attrs.style
|
|
42
42
|
},
|
|
43
43
|
slotContent
|
|
44
44
|
);
|
|
@@ -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,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
EmbeddedFlowType
|
|
4
|
-
} from "@thunderid/browser";
|
|
1
|
+
import { navigateTo } from "#app";
|
|
2
|
+
import { EmbeddedFlowResponseType, EmbeddedFlowType } from "@thunderid/browser";
|
|
5
3
|
import { BaseSignUp } from "@thunderid/vue";
|
|
6
4
|
import { defineComponent, h } from "vue";
|
|
7
|
-
import { navigateTo } from "#app";
|
|
8
5
|
import { useThunderID } from "#imports";
|
|
9
6
|
const SignUp = defineComponent({
|
|
10
7
|
name: "SignUp",
|
|
@@ -24,7 +21,7 @@ const SignUp = defineComponent({
|
|
|
24
21
|
variant: { default: "outlined", type: String }
|
|
25
22
|
},
|
|
26
23
|
setup(props, { slots }) {
|
|
27
|
-
const { signUp, isInitialized, applicationId } = useThunderID();
|
|
24
|
+
const { signUp, isInitialized, applicationId, scopes } = useThunderID();
|
|
28
25
|
const handleInitialize = async (payload) => {
|
|
29
26
|
let applicationIdFromUrl = null;
|
|
30
27
|
if (import.meta.client) {
|
|
@@ -34,7 +31,8 @@ const SignUp = defineComponent({
|
|
|
34
31
|
const effectiveApplicationId = applicationId || applicationIdFromUrl || void 0;
|
|
35
32
|
const initialPayload = payload || {
|
|
36
33
|
flowType: EmbeddedFlowType.Registration,
|
|
37
|
-
...effectiveApplicationId && { applicationId: effectiveApplicationId }
|
|
34
|
+
...effectiveApplicationId && { applicationId: effectiveApplicationId },
|
|
35
|
+
...scopes && { scopes }
|
|
38
36
|
};
|
|
39
37
|
return await signUp(initialPayload);
|
|
40
38
|
};
|
|
@@ -46,7 +44,7 @@ const SignUp = defineComponent({
|
|
|
46
44
|
await navigateTo(oauthRedirectUrl, { external: true });
|
|
47
45
|
return;
|
|
48
46
|
}
|
|
49
|
-
if (props.shouldRedirectAfterSignUp && response?.type !== EmbeddedFlowResponseType.Redirection && props.afterSignUpUrl) {
|
|
47
|
+
if (props.shouldRedirectAfterSignUp && response?.type !== EmbeddedFlowResponseType.Redirection && props.afterSignUpUrl && !response?.assertion) {
|
|
50
48
|
await navigateTo(props.afterSignUpUrl, { external: true });
|
|
51
49
|
}
|
|
52
50
|
if (props.shouldRedirectAfterSignUp && response?.type === EmbeddedFlowResponseType.Redirection && response?.data?.redirectURL && !response.data.redirectURL.includes("oauth") && !response.data.redirectURL.includes("auth")) {
|
|
@@ -72,7 +70,7 @@ const SignUp = defineComponent({
|
|
|
72
70
|
size: props.size,
|
|
73
71
|
variant: props.variant
|
|
74
72
|
},
|
|
75
|
-
slots
|
|
73
|
+
slots.default ? { default: (renderProps) => slots.default(renderProps) } : void 0
|
|
76
74
|
);
|
|
77
75
|
}
|
|
78
76
|
});
|
|
@@ -6,10 +6,10 @@ const Loading = defineComponent({
|
|
|
6
6
|
const { isLoading } = useThunderID();
|
|
7
7
|
return () => {
|
|
8
8
|
if (!isLoading.value) {
|
|
9
|
-
const fallback = slots
|
|
9
|
+
const fallback = slots.fallback?.();
|
|
10
10
|
return fallback ? h(Fragment, {}, fallback) : null;
|
|
11
11
|
}
|
|
12
|
-
const content = slots
|
|
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
|
|
9
|
+
const fallback = slots.fallback?.();
|
|
10
10
|
return fallback ? h(Fragment, {}, fallback) : null;
|
|
11
11
|
}
|
|
12
|
-
const content = slots
|
|
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
|
|
9
|
+
const fallback = slots.fallback?.();
|
|
10
10
|
return fallback ? h(Fragment, {}, fallback) : null;
|
|
11
11
|
}
|
|
12
|
-
const content = slots
|
|
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
|
|
9
|
+
const fallback = slots.fallback?.();
|
|
10
10
|
return fallback ? h(Fragment, {}, fallback) : null;
|
|
11
11
|
}
|
|
12
|
-
const content = slots
|
|
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
|
|
9
|
+
const fallback = slots.fallback?.();
|
|
10
10
|
return fallback ? h(Fragment, {}, fallback) : null;
|
|
11
11
|
}
|
|
12
|
-
const content = slots
|
|
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) => {
|
|
@@ -24,13 +24,13 @@ export function useThunderID() {
|
|
|
24
24
|
}
|
|
25
25
|
return {
|
|
26
26
|
authData: {},
|
|
27
|
-
flowStatus: EmbeddedSignInFlowStatus.
|
|
27
|
+
flowStatus: EmbeddedSignInFlowStatus.Complete
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
return res.data;
|
|
31
31
|
}
|
|
32
32
|
const options = arg0;
|
|
33
|
-
const returnTo = typeof options?.
|
|
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;
|
|
@@ -41,17 +41,6 @@ export function useThunderID() {
|
|
|
41
41
|
};
|
|
42
42
|
const signUp = async (...args) => {
|
|
43
43
|
const payload = args[0];
|
|
44
|
-
if (payload && typeof payload === "object" && "flowType" in payload) {
|
|
45
|
-
const res = await $fetch("/api/auth/signup", {
|
|
46
|
-
body: { payload },
|
|
47
|
-
method: "POST"
|
|
48
|
-
});
|
|
49
|
-
if (res.data?.afterSignUpUrl) {
|
|
50
|
-
await navigateTo(res.data.afterSignUpUrl, { external: false });
|
|
51
|
-
return void 0;
|
|
52
|
-
}
|
|
53
|
-
return res.data;
|
|
54
|
-
}
|
|
55
44
|
const cfg = useRuntimeConfig().public.thunderid ?? {};
|
|
56
45
|
if (cfg.signUpUrl) {
|
|
57
46
|
await navigateTo(cfg.signUpUrl, { external: true });
|
|
@@ -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?.
|
|
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?.
|
|
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?.
|
|
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?.
|
|
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,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* specific language governing permissions and limitations
|
|
16
16
|
* under the License.
|
|
17
17
|
*/
|
|
18
|
-
import { ThunderIDNodeClient, type IdToken, type Organization, type OrganizationDetails, type CreateOrganizationPayload, type Storage, type TokenExchangeRequestConfig, type TokenResponse, type User, type UserProfile, type UpdateMeProfileConfig, type AllOrganizationsApiResponse, type
|
|
18
|
+
import { ThunderIDNodeClient, type IdToken, type Organization, type OrganizationDetails, type CreateOrganizationPayload, type Storage, type TokenExchangeRequestConfig, type TokenResponse, type User, type UserProfile, type UpdateMeProfileConfig, type AllOrganizationsApiResponse, type ExtendedAuthorizeRequestUrlParams, type SignUpOptions, type GetBrandingPreferenceConfig, type BrandingPreference } from '@thunderid/node';
|
|
19
19
|
import type { ThunderIDNuxtConfig, ThunderIDSessionPayload } from '../types.js';
|
|
20
20
|
declare class ThunderIDNuxtClient extends ThunderIDNodeClient<ThunderIDNuxtConfig> {
|
|
21
21
|
private static instance;
|
|
@@ -26,8 +26,7 @@ declare class ThunderIDNuxtClient extends ThunderIDNodeClient<ThunderIDNuxtConfi
|
|
|
26
26
|
reInitialize(config: Partial<ThunderIDNuxtConfig>): Promise<boolean>;
|
|
27
27
|
rehydrateSessionFromPayload(session: ThunderIDSessionPayload): Promise<void>;
|
|
28
28
|
signIn(...args: any[]): Promise<any>;
|
|
29
|
-
signUp(
|
|
30
|
-
signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse>;
|
|
29
|
+
signUp(_options?: SignUpOptions): Promise<void>;
|
|
31
30
|
getAuthorizeRequestUrl(customParams: ExtendedAuthorizeRequestUrlParams, userId?: string): Promise<string>;
|
|
32
31
|
signOut(...args: any[]): Promise<string>;
|
|
33
32
|
getUser(sessionId?: string): Promise<User>;
|
|
@@ -4,10 +4,7 @@ import {
|
|
|
4
4
|
getMeOrganizations,
|
|
5
5
|
getAllOrganizations,
|
|
6
6
|
createOrganization,
|
|
7
|
-
getOrganization
|
|
8
|
-
initializeEmbeddedSignInFlow,
|
|
9
|
-
executeEmbeddedSignInFlow,
|
|
10
|
-
executeEmbeddedSignUpFlow
|
|
7
|
+
getOrganization
|
|
11
8
|
} from "@thunderid/node";
|
|
12
9
|
class ThunderIDNuxtClient extends ThunderIDNodeClient {
|
|
13
10
|
static instance;
|
|
@@ -66,26 +63,6 @@ class ThunderIDNuxtClient extends ThunderIDNodeClient {
|
|
|
66
63
|
}
|
|
67
64
|
signIn(...args) {
|
|
68
65
|
const arg0 = args[0];
|
|
69
|
-
if (typeof arg0 === "object" && arg0 !== null && "flowId" in arg0) {
|
|
70
|
-
const sessionId = args[2];
|
|
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
|
-
});
|
|
82
|
-
}
|
|
83
|
-
const request = args[1] ?? {};
|
|
84
|
-
return executeEmbeddedSignInFlow({
|
|
85
|
-
payload: arg0,
|
|
86
|
-
url: request.url
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
66
|
if (typeof arg0 === "object" && arg0 !== null && ("code" in arg0 || "state" in arg0)) {
|
|
90
67
|
const payload = arg0;
|
|
91
68
|
const code = typeof payload.code === "string" ? payload.code : void 0;
|
|
@@ -99,17 +76,8 @@ class ThunderIDNuxtClient extends ThunderIDNodeClient {
|
|
|
99
76
|
}
|
|
100
77
|
return super.signIn(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
101
78
|
}
|
|
102
|
-
async signUp(
|
|
103
|
-
|
|
104
|
-
return void 0;
|
|
105
|
-
}
|
|
106
|
-
const configData = this.getStorageManager().getConfigData();
|
|
107
|
-
const baseUrl = configData?.baseUrl;
|
|
108
|
-
const response = await executeEmbeddedSignUpFlow({
|
|
109
|
-
baseUrl,
|
|
110
|
-
payload: payloadOrOptions
|
|
111
|
-
});
|
|
112
|
-
return response;
|
|
79
|
+
async signUp(_options) {
|
|
80
|
+
return void 0;
|
|
113
81
|
}
|
|
114
82
|
async getAuthorizeRequestUrl(customParams, userId) {
|
|
115
83
|
return this.getSignInUrl(customParams, userId);
|
|
@@ -161,7 +129,6 @@ class ThunderIDNuxtClient extends ThunderIDNodeClient {
|
|
|
161
129
|
headers: { Authorization: `Bearer ${accessToken}` }
|
|
162
130
|
});
|
|
163
131
|
}
|
|
164
|
-
// eslint-disable-next-line class-methods-use-this
|
|
165
132
|
async getBrandingPreference(config) {
|
|
166
133
|
return getBrandingPreference(config);
|
|
167
134
|
}
|
|
@@ -23,9 +23,9 @@ export default defineNitroPlugin((nitro) => {
|
|
|
23
23
|
);
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
const sessionSecret2 = process.env
|
|
26
|
+
const sessionSecret2 = process.env.THUNDERID_SESSION_SECRET || privateConfig?.sessionSecret;
|
|
27
27
|
if (!sessionSecret2) {
|
|
28
|
-
if (process.env
|
|
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
|
|
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?.
|
|
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
|
|
133
|
+
eventContext.__thunderidAuth = authState;
|
|
134
134
|
});
|
|
135
135
|
});
|
|
@@ -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
|
|
16
|
-
const state = query
|
|
17
|
-
const sessionState = query
|
|
18
|
-
const error = query
|
|
19
|
-
const errorDescription = query
|
|
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
|
|
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);
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Handles embedded (app-native) sign-in flow steps.
|
|
22
22
|
*
|
|
23
23
|
* Request body:
|
|
24
|
-
* - `payload` — the embedded flow step payload
|
|
24
|
+
* - `payload` — the embedded flow step payload.
|
|
25
25
|
* When omitted or `{}`, the flow is initialised and the authorize URL is returned.
|
|
26
26
|
* - `request` — optional per-step config (e.g. `{ url }` override).
|
|
27
27
|
*
|
|
@@ -66,7 +66,7 @@ export default defineEventHandler(async (event) => {
|
|
|
66
66
|
statusMessage: `Embedded sign-in step failed: ${err?.message ?? String(err)}`
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
if (response?.flowStatus === EmbeddedSignInFlowStatus.
|
|
69
|
+
if (response?.flowStatus === EmbeddedSignInFlowStatus.Complete) {
|
|
70
70
|
const authData = response?.authData ?? {};
|
|
71
71
|
const { code, state, session_state: sessionState } = authData;
|
|
72
72
|
if (!code) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EmbeddedSignUpFlowStatus } from "@thunderid/node";
|
|
2
2
|
import { defineEventHandler, readBody, createError } from "h3";
|
|
3
3
|
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
4
4
|
import { useRuntimeConfig } from "#imports";
|
|
@@ -23,7 +23,7 @@ export default defineEventHandler(async (event) => {
|
|
|
23
23
|
statusMessage: `Embedded sign-up step failed: ${err?.message ?? String(err)}`
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
if (hasFlowStatus(response) && response.flowStatus ===
|
|
26
|
+
if (hasFlowStatus(response) && response.flowStatus === EmbeddedSignUpFlowStatus.Complete) {
|
|
27
27
|
return { data: { afterSignUpUrl }, success: true };
|
|
28
28
|
}
|
|
29
29
|
return { data: response, success: true };
|
|
@@ -61,13 +61,13 @@ export declare function getTempSessionCookieName(): string;
|
|
|
61
61
|
/**
|
|
62
62
|
* Session cookie options.
|
|
63
63
|
*/
|
|
64
|
-
|
|
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
|
|
6
|
+
const secret = sessionSecret || process.env.THUNDERID_SESSION_SECRET;
|
|
7
7
|
if (!secret) {
|
|
8
|
-
if (process.env
|
|
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
|
|
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
|
|
52
|
+
if (payload.type !== "temp") {
|
|
53
53
|
throw new Error("Invalid token type: expected temp session");
|
|
54
54
|
}
|
|
55
55
|
return {
|
|
56
|
-
returnTo: payload
|
|
57
|
-
sessionId: payload
|
|
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
|
|
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
|
|
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
|
|
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(
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* specific language governing permissions and limitations
|
|
16
16
|
* under the License.
|
|
17
17
|
*/
|
|
18
|
-
import type { BrandingPreference, I18nPreferences, Organization,
|
|
18
|
+
import type { BrandingPreference, I18nPreferences, Organization, TokenEndpointAuthMethod, User, UserProfile } from '@thunderid/node';
|
|
19
19
|
import type { JWTPayload } from 'jose';
|
|
20
20
|
/**
|
|
21
21
|
* Configuration for the ThunderID Nuxt module.
|
|
@@ -30,18 +30,12 @@ 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://
|
|
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;
|
|
37
37
|
/** OAuth2 Client Secret (server-only, use THUNDERID_CLIENT_SECRET env var) */
|
|
38
38
|
clientSecret?: string;
|
|
39
|
-
/**
|
|
40
|
-
* Identity platform variant. Set to `Platform.ThunderID` 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
39
|
/**
|
|
46
40
|
* Feature-gating preferences that control which server-side data fetches
|
|
47
41
|
* the Nitro plugin performs on every SSR request.
|
|
@@ -72,7 +66,7 @@ export interface ThunderIDNuxtConfig {
|
|
|
72
66
|
};
|
|
73
67
|
};
|
|
74
68
|
/** OAuth2 scopes to request */
|
|
75
|
-
scopes?: string[];
|
|
69
|
+
scopes?: string | string[];
|
|
76
70
|
/** Secret for signing session JWTs (use THUNDERID_SESSION_SECRET env var) */
|
|
77
71
|
sessionSecret?: string;
|
|
78
72
|
/**
|
|
@@ -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
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Nuxt SDK for ThunderID",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"thunderid",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"nuxtjs",
|
|
9
9
|
"ssr"
|
|
10
10
|
],
|
|
11
|
-
"homepage": "https://github.com/thunder-id/
|
|
11
|
+
"homepage": "https://github.com/thunder-id/javascript-sdks/tree/main/packages/nuxt#readme",
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/thunder-id/thunderid/issues"
|
|
14
14
|
},
|
|
@@ -57,30 +57,30 @@
|
|
|
57
57
|
],
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
60
|
-
"url": "https://github.com/thunder-id/
|
|
60
|
+
"url": "https://github.com/thunder-id/javascript-sdks",
|
|
61
61
|
"directory": "packages/nuxt"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@nuxt/kit": "3.21.
|
|
64
|
+
"@nuxt/kit": "3.21.7",
|
|
65
65
|
"defu": "6.1.5",
|
|
66
66
|
"jose": "5.2.0",
|
|
67
|
-
"@thunderid/
|
|
68
|
-
"@thunderid/
|
|
69
|
-
"@thunderid/vue": "^0.
|
|
67
|
+
"@thunderid/browser": "^0.3.1",
|
|
68
|
+
"@thunderid/node": "^0.2.1",
|
|
69
|
+
"@thunderid/vue": "^0.3.3"
|
|
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.
|
|
74
|
+
"@nuxt/schema": "3.21.7",
|
|
75
75
|
"@nuxt/test-utils": "3.17.2",
|
|
76
|
+
"@thunderid/eslint-plugin": "0.0.2",
|
|
77
|
+
"@thunderid/prettier-config": "0.0.2",
|
|
76
78
|
"@types/node": "24.7.2",
|
|
77
79
|
"eslint": "9.39.4",
|
|
78
80
|
"h3": "1.15.11",
|
|
79
|
-
"nuxt": "3.21.
|
|
81
|
+
"nuxt": "3.21.7",
|
|
80
82
|
"typescript": "5.9.3",
|
|
81
|
-
"vitest": "4.1.
|
|
82
|
-
"@thunderid/eslint-plugin": "^0.0.0",
|
|
83
|
-
"@thunderid/prettier-config": "^0.0.0"
|
|
83
|
+
"vitest": "4.1.8"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"nuxt": ">=3.10.0",
|
|
@@ -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 .",
|