@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.
- package/LICENSE +201 -0
- package/dist/module.d.mts +46 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +216 -0
- package/dist/runtime/components/ThunderIDRoot.d.ts +52 -0
- package/dist/runtime/components/ThunderIDRoot.js +160 -0
- package/dist/runtime/components/actions/SignInButton.d.ts +37 -0
- package/dist/runtime/components/actions/SignInButton.js +51 -0
- package/dist/runtime/components/actions/SignOutButton.d.ts +34 -0
- package/dist/runtime/components/actions/SignOutButton.js +43 -0
- package/dist/runtime/components/actions/SignUpButton.d.ts +33 -0
- package/dist/runtime/components/actions/SignUpButton.js +48 -0
- package/dist/runtime/components/auth/Callback.d.ts +43 -0
- package/dist/runtime/components/auth/Callback.js +93 -0
- package/dist/runtime/components/auth/SignIn.d.ts +38 -0
- package/dist/runtime/components/auth/SignIn.js +60 -0
- package/dist/runtime/components/auth/SignUp.d.ts +40 -0
- package/dist/runtime/components/auth/SignUp.js +79 -0
- package/dist/runtime/components/control/Loading.d.ts +36 -0
- package/dist/runtime/components/control/Loading.js +17 -0
- package/dist/runtime/components/control/SignedIn.d.ts +38 -0
- package/dist/runtime/components/control/SignedIn.js +17 -0
- package/dist/runtime/components/control/SignedOut.d.ts +37 -0
- package/dist/runtime/components/control/SignedOut.js +17 -0
- package/dist/runtime/components/organization/CreateOrganization.d.ts +32 -0
- package/dist/runtime/components/organization/CreateOrganization.js +29 -0
- package/dist/runtime/components/organization/Organization.d.ts +39 -0
- package/dist/runtime/components/organization/Organization.js +17 -0
- package/dist/runtime/components/organization/OrganizationList.d.ts +34 -0
- package/dist/runtime/components/organization/OrganizationList.js +30 -0
- package/dist/runtime/components/organization/OrganizationProfile.d.ts +32 -0
- package/dist/runtime/components/organization/OrganizationProfile.js +32 -0
- package/dist/runtime/components/organization/OrganizationSwitcher.d.ts +36 -0
- package/dist/runtime/components/organization/OrganizationSwitcher.js +26 -0
- package/dist/runtime/components/user/User.d.ts +38 -0
- package/dist/runtime/components/user/User.js +17 -0
- package/dist/runtime/components/user/UserDropdown.d.ts +38 -0
- package/dist/runtime/components/user/UserDropdown.js +45 -0
- package/dist/runtime/components/user/UserProfile.d.ts +35 -0
- package/dist/runtime/components/user/UserProfile.js +35 -0
- package/dist/runtime/composables/useThunderID.d.ts +38 -0
- package/dist/runtime/composables/useThunderID.js +73 -0
- package/dist/runtime/errors/error-codes.d.ts +40 -0
- package/dist/runtime/errors/error-codes.js +19 -0
- package/dist/runtime/errors/index.d.ts +19 -0
- package/dist/runtime/errors/index.js +2 -0
- package/dist/runtime/errors/thunderid-error.d.ts +47 -0
- package/dist/runtime/errors/thunderid-error.js +15 -0
- package/dist/runtime/middleware/auth.d.ts +35 -0
- package/dist/runtime/middleware/auth.js +2 -0
- package/dist/runtime/middleware/defineThunderIDMiddleware.d.ts +53 -0
- package/dist/runtime/middleware/defineThunderIDMiddleware.js +24 -0
- package/dist/runtime/plugins/thunderid.d.ts +39 -0
- package/dist/runtime/plugins/thunderid.js +128 -0
- package/dist/runtime/server/ThunderIDNuxtClient.d.ts +186 -0
- package/dist/runtime/server/ThunderIDNuxtClient.js +384 -0
- package/dist/runtime/server/index.d.ts +33 -0
- package/dist/runtime/server/index.js +3 -0
- package/dist/runtime/server/plugins/thunderid-ssr.d.ts +40 -0
- package/dist/runtime/server/plugins/thunderid-ssr.js +135 -0
- package/dist/runtime/server/routes/auth/branding/branding.get.d.ts +31 -0
- package/dist/runtime/server/routes/auth/branding/branding.get.js +40 -0
- package/dist/runtime/server/routes/auth/organizations/current.get.d.ts +29 -0
- package/dist/runtime/server/routes/auth/organizations/current.get.js +24 -0
- package/dist/runtime/server/routes/auth/organizations/id.get.d.ts +28 -0
- package/dist/runtime/server/routes/auth/organizations/id.get.js +28 -0
- package/dist/runtime/server/routes/auth/organizations/index.get.d.ts +28 -0
- package/dist/runtime/server/routes/auth/organizations/index.get.js +24 -0
- package/dist/runtime/server/routes/auth/organizations/index.post.d.ts +30 -0
- package/dist/runtime/server/routes/auth/organizations/index.post.js +30 -0
- package/dist/runtime/server/routes/auth/organizations/me.get.d.ts +28 -0
- package/dist/runtime/server/routes/auth/organizations/me.get.js +24 -0
- package/dist/runtime/server/routes/auth/organizations/switch.post.d.ts +32 -0
- package/dist/runtime/server/routes/auth/organizations/switch.post.js +49 -0
- package/dist/runtime/server/routes/auth/session/callback.get.d.ts +27 -0
- package/dist/runtime/server/routes/auth/session/callback.get.js +91 -0
- package/dist/runtime/server/routes/auth/session/callback.post.d.ts +48 -0
- package/dist/runtime/server/routes/auth/session/callback.post.js +53 -0
- package/dist/runtime/server/routes/auth/session/session.get.d.ts +26 -0
- package/dist/runtime/server/routes/auth/session/session.get.js +22 -0
- package/dist/runtime/server/routes/auth/session/signin.get.d.ts +29 -0
- package/dist/runtime/server/routes/auth/session/signin.get.js +37 -0
- package/dist/runtime/server/routes/auth/session/signin.post.d.ts +37 -0
- package/dist/runtime/server/routes/auth/session/signin.post.js +102 -0
- package/dist/runtime/server/routes/auth/session/signout.post.d.ts +31 -0
- package/dist/runtime/server/routes/auth/session/signout.post.js +38 -0
- package/dist/runtime/server/routes/auth/session/signup.post.d.ts +36 -0
- package/dist/runtime/server/routes/auth/session/signup.post.js +30 -0
- package/dist/runtime/server/routes/auth/session/token.get.d.ts +29 -0
- package/dist/runtime/server/routes/auth/session/token.get.js +6 -0
- package/dist/runtime/server/routes/auth/user/profile.get.d.ts +29 -0
- package/dist/runtime/server/routes/auth/user/profile.get.js +24 -0
- package/dist/runtime/server/routes/auth/user/profile.patch.d.ts +35 -0
- package/dist/runtime/server/routes/auth/user/profile.patch.js +41 -0
- package/dist/runtime/server/routes/auth/user/user.get.d.ts +25 -0
- package/dist/runtime/server/routes/auth/user/user.get.js +21 -0
- package/dist/runtime/server/utils/event-context.d.ts +49 -0
- package/dist/runtime/server/utils/event-context.js +3 -0
- package/dist/runtime/server/utils/serverSession.d.ts +65 -0
- package/dist/runtime/server/utils/serverSession.js +44 -0
- package/dist/runtime/server/utils/session.d.ts +85 -0
- package/dist/runtime/server/utils/session.js +106 -0
- package/dist/runtime/server/utils/token-refresh.d.ts +42 -0
- package/dist/runtime/server/utils/token-refresh.js +65 -0
- package/dist/runtime/types.d.ts +161 -0
- package/dist/runtime/types.js +0 -0
- package/dist/runtime/utils/createRouteMatcher.d.ts +40 -0
- package/dist/runtime/utils/createRouteMatcher.js +7 -0
- package/dist/runtime/utils/index.d.ts +30 -0
- package/dist/runtime/utils/index.js +1 -0
- package/dist/runtime/utils/log.d.ts +44 -0
- package/dist/runtime/utils/log.js +25 -0
- package/dist/runtime/utils/url-validation.d.ts +49 -0
- package/dist/runtime/utils/url-validation.js +38 -0
- package/dist/types.d.mts +7 -0
- package/package.json +101 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { getRedirectBasedSignUpUrl } from "@thunderid/browser";
|
|
2
|
+
import { ThunderIDPlugin, THUNDERID_KEY } from "@thunderid/vue";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
import ThunderIDRoot from "../components/ThunderIDRoot.js";
|
|
5
|
+
import { defineNuxtPlugin, useState, useRequestEvent, useRuntimeConfig, navigateTo } from "#app";
|
|
6
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
7
|
+
const publicConfig = useRuntimeConfig().public.thunderid;
|
|
8
|
+
if (import.meta.client && import.meta.dev) {
|
|
9
|
+
if (!publicConfig?.baseUrl || !publicConfig?.clientId) {
|
|
10
|
+
console.warn(
|
|
11
|
+
"[@thunderid/nuxt] Missing baseUrl or clientId. Set NUXT_PUBLIC_THUNDERID_BASE_URL and NUXT_PUBLIC_THUNDERID_CLIENT_ID, or configure `thunderid` in nuxt.config. Auth endpoints will not function until this is resolved."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const authState = useState("thunderid:auth", () => ({
|
|
16
|
+
isLoading: true,
|
|
17
|
+
isSignedIn: false,
|
|
18
|
+
user: null
|
|
19
|
+
}));
|
|
20
|
+
const userProfileState = useState("thunderid:user-profile", () => null);
|
|
21
|
+
const currentOrgState = useState("thunderid:current-org", () => null);
|
|
22
|
+
const myOrgsState = useState("thunderid:my-orgs", () => []);
|
|
23
|
+
const brandingState = useState(
|
|
24
|
+
"thunderid:branding",
|
|
25
|
+
() => null
|
|
26
|
+
);
|
|
27
|
+
if (import.meta.server) {
|
|
28
|
+
const event = useRequestEvent();
|
|
29
|
+
const ssr = event?.context?.thunderid?.ssr;
|
|
30
|
+
if (ssr) {
|
|
31
|
+
authState.value = {
|
|
32
|
+
isLoading: false,
|
|
33
|
+
isSignedIn: ssr.isSignedIn,
|
|
34
|
+
user: ssr.user
|
|
35
|
+
};
|
|
36
|
+
userProfileState.value = ssr.userProfile;
|
|
37
|
+
currentOrgState.value = ssr.currentOrganization;
|
|
38
|
+
myOrgsState.value = ssr.myOrganizations;
|
|
39
|
+
brandingState.value = ssr.brandingPreference;
|
|
40
|
+
} else {
|
|
41
|
+
const ssrContext = event?.context?.thunderid;
|
|
42
|
+
if (ssrContext) {
|
|
43
|
+
authState.value = {
|
|
44
|
+
isLoading: false,
|
|
45
|
+
isSignedIn: ssrContext.isSignedIn ?? false,
|
|
46
|
+
user: ssrContext.session?.sub ? { sub: ssrContext.session.sub } : null
|
|
47
|
+
};
|
|
48
|
+
} else {
|
|
49
|
+
const legacyAuth = event?.context?.["__thunderidAuth"];
|
|
50
|
+
authState.value = legacyAuth ?? { isLoading: false, isSignedIn: false, user: null };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (import.meta.client) {
|
|
55
|
+
authState.value = { ...authState.value, isLoading: false };
|
|
56
|
+
}
|
|
57
|
+
const isSignedIn = computed(() => authState.value.isSignedIn);
|
|
58
|
+
const isLoading = computed(() => authState.value.isLoading);
|
|
59
|
+
const isInitialized = computed(() => !authState.value.isLoading);
|
|
60
|
+
const user = computed(() => authState.value.user ?? null);
|
|
61
|
+
const organizationRef = computed(() => currentOrgState.value);
|
|
62
|
+
const signIn = async (options) => {
|
|
63
|
+
const returnTo = typeof options?.["returnTo"] === "string" ? options["returnTo"] : void 0;
|
|
64
|
+
const url = returnTo ? `/api/auth/signin?returnTo=${encodeURIComponent(returnTo)}` : "/api/auth/signin";
|
|
65
|
+
await navigateTo(url, { external: true });
|
|
66
|
+
};
|
|
67
|
+
const signOut = async () => {
|
|
68
|
+
const res = await $fetch("/api/auth/signout", { method: "POST" });
|
|
69
|
+
await navigateTo(res.redirectUrl || "/", { external: true });
|
|
70
|
+
};
|
|
71
|
+
const signUp = async () => {
|
|
72
|
+
if (publicConfig.signUpUrl) {
|
|
73
|
+
await navigateTo(publicConfig.signUpUrl, { external: true });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const redirectUrl = getRedirectBasedSignUpUrl({
|
|
77
|
+
applicationId: publicConfig.applicationId,
|
|
78
|
+
baseUrl: publicConfig.baseUrl,
|
|
79
|
+
clientId: publicConfig.clientId
|
|
80
|
+
});
|
|
81
|
+
if (redirectUrl) {
|
|
82
|
+
await navigateTo(redirectUrl, { external: true });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
await navigateTo("/api/auth/signup", { external: true });
|
|
86
|
+
};
|
|
87
|
+
const getAccessToken = async () => {
|
|
88
|
+
try {
|
|
89
|
+
const res = await $fetch("/api/auth/token");
|
|
90
|
+
return res.accessToken ?? "";
|
|
91
|
+
} catch {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const noop = async () => void 0;
|
|
96
|
+
nuxtApp.vueApp.provide(THUNDERID_KEY, {
|
|
97
|
+
afterSignInUrl: publicConfig.afterSignInUrl,
|
|
98
|
+
applicationId: publicConfig.applicationId,
|
|
99
|
+
baseUrl: publicConfig.baseUrl,
|
|
100
|
+
clearSession: noop,
|
|
101
|
+
clientId: publicConfig.clientId,
|
|
102
|
+
exchangeToken: noop,
|
|
103
|
+
getAccessToken,
|
|
104
|
+
getDecodedIdToken: noop,
|
|
105
|
+
getIdToken: noop,
|
|
106
|
+
http: { request: noop, requestAll: noop },
|
|
107
|
+
instanceId: 0,
|
|
108
|
+
isInitialized,
|
|
109
|
+
isLoading,
|
|
110
|
+
isSignedIn,
|
|
111
|
+
organization: organizationRef,
|
|
112
|
+
organizationHandle: publicConfig.organizationHandle,
|
|
113
|
+
platform: void 0,
|
|
114
|
+
reInitialize: async () => false,
|
|
115
|
+
signIn,
|
|
116
|
+
signInOptions: void 0,
|
|
117
|
+
signInSilently: noop,
|
|
118
|
+
signInUrl: publicConfig.signInUrl,
|
|
119
|
+
signOut,
|
|
120
|
+
signUp,
|
|
121
|
+
signUpUrl: publicConfig.signUpUrl,
|
|
122
|
+
storage: void 0,
|
|
123
|
+
switchOrganization: noop,
|
|
124
|
+
user
|
|
125
|
+
});
|
|
126
|
+
nuxtApp.vueApp.component("ThunderIDRoot", ThunderIDRoot);
|
|
127
|
+
nuxtApp.vueApp.use(ThunderIDPlugin, { mode: "delegated" });
|
|
128
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
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 { ThunderIDNodeClient, type IdToken, type Organization, type OrganizationDetails, type CreateOrganizationPayload, type Storage, type TokenExchangeRequestConfig, type TokenResponse, type User, type UserProfile, type UpdateMeProfileConfig, type AllOrganizationsApiResponse, type GetBrandingPreferenceConfig, type BrandingPreference, type EmbeddedFlowExecuteRequestPayload, type EmbeddedFlowExecuteResponse, type ExtendedAuthorizeRequestUrlParams, type SignUpOptions } from '@thunderid/node';
|
|
19
|
+
import type { ThunderIDNuxtConfig, ThunderIDSessionPayload } from '../types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Singleton ThunderID client for Nuxt applications.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors the {@link ThunderIDNextClient} pattern: a single shared instance per
|
|
24
|
+
* server process that delegates OAuth/OIDC operations to an internal
|
|
25
|
+
* {@link LegacyThunderIDNodeClient}. The legacy client provisions its own default
|
|
26
|
+
* in-memory store (`MemoryCacheStore`) for PKCE state and tokens so that state
|
|
27
|
+
* persists across the sign-in → callback boundary.
|
|
28
|
+
*
|
|
29
|
+
* Consumers call {@link getInstance} directly from server routes and plugins —
|
|
30
|
+
* there is no per-request wrapper factory. Initialization happens once per
|
|
31
|
+
* process (guarded by {@link isInitialized}) from the `thunderid-init` Nitro
|
|
32
|
+
* plugin on the first request.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* // In a Nitro API route:
|
|
37
|
+
* export default defineEventHandler(async (event) => {
|
|
38
|
+
* const client = ThunderIDNuxtClient.getInstance();
|
|
39
|
+
* return client.getUser(sessionId);
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare class ThunderIDNuxtClient extends ThunderIDNodeClient<ThunderIDNuxtConfig> {
|
|
44
|
+
private static instance;
|
|
45
|
+
private legacy;
|
|
46
|
+
isInitialized: boolean;
|
|
47
|
+
private constructor();
|
|
48
|
+
/**
|
|
49
|
+
* Get the singleton instance of ThunderIDNuxtClient.
|
|
50
|
+
*/
|
|
51
|
+
static getInstance(): ThunderIDNuxtClient;
|
|
52
|
+
/**
|
|
53
|
+
* Initializes the underlying legacy client with OAuth/OIDC settings derived
|
|
54
|
+
* from the Nuxt module config. Idempotent — repeated calls are no-ops after
|
|
55
|
+
* the first successful initialization.
|
|
56
|
+
*/
|
|
57
|
+
initialize(config: ThunderIDNuxtConfig, storage?: Storage): Promise<boolean>;
|
|
58
|
+
reInitialize(config: Partial<ThunderIDNuxtConfig>): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Seeds the legacy in-memory token store from a verified session JWT payload.
|
|
61
|
+
*
|
|
62
|
+
* The signed session cookie is the source of truth for tokens in this SDK — it
|
|
63
|
+
* survives server restarts and new worker processes. The underlying
|
|
64
|
+
* {@link LegacyThunderIDNodeClient}, however, keeps tokens in a
|
|
65
|
+
* {@link MemoryCacheStore} keyed by `sessionId`, and its
|
|
66
|
+
* `getAccessToken` / `getUser` / `getDecodedIdToken` / `signOut` paths all
|
|
67
|
+
* read from that store. Without rehydration, those calls fail whenever the
|
|
68
|
+
* in-memory store and the cookie diverge (the classic case: `nuxi dev`
|
|
69
|
+
* restart while the browser still holds a valid session cookie).
|
|
70
|
+
*
|
|
71
|
+
* Writes the snake_case token shape the legacy helper expects
|
|
72
|
+
* (see `AuthenticationHelper.processTokenResponse`). Safe to call on every
|
|
73
|
+
* request — it's an in-memory write and the cookie always reflects the
|
|
74
|
+
* freshest tokens (the refresh path re-issues the cookie too).
|
|
75
|
+
*/
|
|
76
|
+
rehydrateSessionFromPayload(session: ThunderIDSessionPayload): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Initiates the authorization code flow, handles an embedded sign-in step,
|
|
79
|
+
* or exchanges a code for tokens.
|
|
80
|
+
*
|
|
81
|
+
* Overload 1 — **redirect-flow** (existing callers like `signin.get.ts`):
|
|
82
|
+
* ```
|
|
83
|
+
* signIn(authURLCallback, sessionId, code?, sessionState?, state?, config?)
|
|
84
|
+
* ```
|
|
85
|
+
* Overload 2 — **embedded flow initiate** (flowId === ''):
|
|
86
|
+
* ```
|
|
87
|
+
* signIn({flowId: ''}, request, sessionId)
|
|
88
|
+
* ```
|
|
89
|
+
* Dispatches to `initializeEmbeddedSignInFlow`.
|
|
90
|
+
*
|
|
91
|
+
* Overload 3 — **embedded flow execute** (flowId set):
|
|
92
|
+
* ```
|
|
93
|
+
* signIn(payload, request, sessionId)
|
|
94
|
+
* ```
|
|
95
|
+
* Dispatches to `executeEmbeddedSignInFlow`.
|
|
96
|
+
*
|
|
97
|
+
* Overload 4 — **code exchange** (completion after embedded flow):
|
|
98
|
+
* ```
|
|
99
|
+
* signIn({code, state, session_state}, {}, sessionId)
|
|
100
|
+
* ```
|
|
101
|
+
* Falls through to the legacy redirect-flow code-exchange path.
|
|
102
|
+
*/
|
|
103
|
+
signIn(...args: any[]): Promise<any>;
|
|
104
|
+
/**
|
|
105
|
+
* Executes the embedded sign-up flow step.
|
|
106
|
+
* Mirrors `ThunderIDNextClient.signUp` with an `EmbeddedFlowExecuteRequestPayload`.
|
|
107
|
+
*/
|
|
108
|
+
signUp(options?: SignUpOptions): Promise<void>;
|
|
109
|
+
signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse>;
|
|
110
|
+
/**
|
|
111
|
+
* Returns the OAuth2 authorization URL.
|
|
112
|
+
* Used by the redirect-flow GET handler and the embedded-flow initiation path.
|
|
113
|
+
*
|
|
114
|
+
* Mirrors `ThunderIDNextClient.getAuthorizeRequestUrl`.
|
|
115
|
+
*/
|
|
116
|
+
getAuthorizeRequestUrl(customParams: ExtendedAuthorizeRequestUrlParams, userId?: string): Promise<string>;
|
|
117
|
+
/**
|
|
118
|
+
* Clears the session and returns the RP-Initiated Logout URL.
|
|
119
|
+
* Accepts either `(sessionId: string)` or `(options?, sessionId?, callback?)`.
|
|
120
|
+
*
|
|
121
|
+
* For ThunderIDV2 (Thunder), RP-Initiated Logout is not yet supported by the platform.
|
|
122
|
+
* Skip the /oidc/logout call and return afterSignOutUrl directly — the caller
|
|
123
|
+
* (signout.post.ts) is responsible for clearing session cookies.
|
|
124
|
+
*/
|
|
125
|
+
signOut(...args: any[]): Promise<string>;
|
|
126
|
+
getUser(sessionId?: string): Promise<User>;
|
|
127
|
+
getAccessToken(sessionId?: string): Promise<string>;
|
|
128
|
+
/**
|
|
129
|
+
* Decodes and returns the ID token claims for the given session.
|
|
130
|
+
* Exposed here (as on {@link ThunderIDNextClient}) so route handlers can
|
|
131
|
+
* access ID token claims without falling back to the legacy client.
|
|
132
|
+
*/
|
|
133
|
+
getDecodedIdToken(sessionId?: string, idToken?: string): Promise<IdToken>;
|
|
134
|
+
isSignedIn(sessionId?: string): Promise<boolean>;
|
|
135
|
+
exchangeToken(config: TokenExchangeRequestConfig, sessionId?: string): Promise<TokenResponse | Response>;
|
|
136
|
+
/**
|
|
137
|
+
* Fetches the flattened SCIM2 user profile for the given session.
|
|
138
|
+
* Mirrors `ThunderIDNextClient.getUserProfile` — calls `getScim2Me` +
|
|
139
|
+
* `getSchemas` + `generateFlattenedUserProfile` and falls back to
|
|
140
|
+
* `getUser` claims if SCIM2 is unavailable.
|
|
141
|
+
*/
|
|
142
|
+
getUserProfile(sessionId: string): Promise<UserProfile>;
|
|
143
|
+
/**
|
|
144
|
+
* Extracts the current organisation from the decoded ID token.
|
|
145
|
+
* Returns null when the user is not acting within an organisation.
|
|
146
|
+
*/
|
|
147
|
+
getCurrentOrganization(sessionId: string): Promise<Organization | null>;
|
|
148
|
+
/**
|
|
149
|
+
* Returns the list of organisations the authenticated user is a member of.
|
|
150
|
+
*/
|
|
151
|
+
getMyOrganizations(sessionId: string): Promise<Organization[]>;
|
|
152
|
+
/**
|
|
153
|
+
* Fetches the branding preference for the tenant / application.
|
|
154
|
+
* Delegates to the standalone `getBrandingPreference` API helper from
|
|
155
|
+
* `@thunderid/node`, which does not require an authenticated session.
|
|
156
|
+
*/
|
|
157
|
+
getBrandingPreference(config: GetBrandingPreferenceConfig): Promise<BrandingPreference>;
|
|
158
|
+
/**
|
|
159
|
+
* Updates the SCIM2 /Me profile for the authenticated user.
|
|
160
|
+
* Mirrors `ThunderIDNextClient.updateUserProfile`.
|
|
161
|
+
*/
|
|
162
|
+
updateUserProfile(config: UpdateMeProfileConfig, sessionId: string): Promise<User>;
|
|
163
|
+
/**
|
|
164
|
+
* Retrieves all organisations accessible to the authenticated user
|
|
165
|
+
* (paginated). Mirrors `ThunderIDNextClient.getAllOrganizations`.
|
|
166
|
+
*/
|
|
167
|
+
getAllOrganizations(options?: any, sessionId?: string): Promise<AllOrganizationsApiResponse>;
|
|
168
|
+
/**
|
|
169
|
+
* Creates a new sub-organisation. Mirrors `ThunderIDNextClient.createOrganization`.
|
|
170
|
+
*/
|
|
171
|
+
createOrganization(payload: CreateOrganizationPayload, sessionId: string): Promise<Organization>;
|
|
172
|
+
/**
|
|
173
|
+
* Fetches the details of a single organisation by ID.
|
|
174
|
+
* Mirrors `ThunderIDNextClient.getOrganization`.
|
|
175
|
+
*/
|
|
176
|
+
getOrganization(organizationId: string, sessionId: string): Promise<OrganizationDetails>;
|
|
177
|
+
/**
|
|
178
|
+
* Performs an organisation-switch token exchange and returns the new
|
|
179
|
+
* `TokenResponse`. The caller (the Nitro route) is responsible for
|
|
180
|
+
* persisting the new session cookie.
|
|
181
|
+
*
|
|
182
|
+
* Mirrors `ThunderIDNextClient.switchOrganization`.
|
|
183
|
+
*/
|
|
184
|
+
switchOrganization(organization: Organization, sessionId: string): Promise<TokenResponse | Response>;
|
|
185
|
+
}
|
|
186
|
+
export default ThunderIDNuxtClient;
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ThunderIDNodeClient,
|
|
3
|
+
LegacyThunderIDNodeClient,
|
|
4
|
+
Platform,
|
|
5
|
+
getBrandingPreference,
|
|
6
|
+
getMeOrganizations,
|
|
7
|
+
getAllOrganizations,
|
|
8
|
+
createOrganization,
|
|
9
|
+
getOrganization,
|
|
10
|
+
getScim2Me,
|
|
11
|
+
getSchemas,
|
|
12
|
+
flattenUserSchema,
|
|
13
|
+
generateFlattenedUserProfile,
|
|
14
|
+
updateMeProfile,
|
|
15
|
+
initializeEmbeddedSignInFlow,
|
|
16
|
+
executeEmbeddedSignInFlow,
|
|
17
|
+
executeEmbeddedSignUpFlow
|
|
18
|
+
} from "@thunderid/node";
|
|
19
|
+
class ThunderIDNuxtClient extends ThunderIDNodeClient {
|
|
20
|
+
static instance;
|
|
21
|
+
legacy;
|
|
22
|
+
isInitialized = false;
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
this.legacy = new LegacyThunderIDNodeClient();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the singleton instance of ThunderIDNuxtClient.
|
|
29
|
+
*/
|
|
30
|
+
static getInstance() {
|
|
31
|
+
if (!ThunderIDNuxtClient.instance) {
|
|
32
|
+
ThunderIDNuxtClient.instance = new ThunderIDNuxtClient();
|
|
33
|
+
}
|
|
34
|
+
return ThunderIDNuxtClient.instance;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initializes the underlying legacy client with OAuth/OIDC settings derived
|
|
38
|
+
* from the Nuxt module config. Idempotent — repeated calls are no-ops after
|
|
39
|
+
* the first successful initialization.
|
|
40
|
+
*/
|
|
41
|
+
async initialize(config, storage) {
|
|
42
|
+
if (this.isInitialized) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const authConfig = {
|
|
46
|
+
afterSignInUrl: config.afterSignInUrl,
|
|
47
|
+
afterSignOutUrl: config.afterSignOutUrl || "/",
|
|
48
|
+
baseUrl: config.baseUrl,
|
|
49
|
+
clientId: config.clientId,
|
|
50
|
+
clientSecret: config.clientSecret || void 0,
|
|
51
|
+
enablePKCE: true,
|
|
52
|
+
platform: config.platform,
|
|
53
|
+
scopes: config.scopes || ["openid", "profile"],
|
|
54
|
+
tokenRequest: config.tokenRequest
|
|
55
|
+
};
|
|
56
|
+
const result = await this.legacy.initialize(authConfig, storage);
|
|
57
|
+
this.isInitialized = true;
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
async reInitialize(config) {
|
|
61
|
+
await this.legacy.reInitialize(config);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Seeds the legacy in-memory token store from a verified session JWT payload.
|
|
66
|
+
*
|
|
67
|
+
* The signed session cookie is the source of truth for tokens in this SDK — it
|
|
68
|
+
* survives server restarts and new worker processes. The underlying
|
|
69
|
+
* {@link LegacyThunderIDNodeClient}, however, keeps tokens in a
|
|
70
|
+
* {@link MemoryCacheStore} keyed by `sessionId`, and its
|
|
71
|
+
* `getAccessToken` / `getUser` / `getDecodedIdToken` / `signOut` paths all
|
|
72
|
+
* read from that store. Without rehydration, those calls fail whenever the
|
|
73
|
+
* in-memory store and the cookie diverge (the classic case: `nuxi dev`
|
|
74
|
+
* restart while the browser still holds a valid session cookie).
|
|
75
|
+
*
|
|
76
|
+
* Writes the snake_case token shape the legacy helper expects
|
|
77
|
+
* (see `AuthenticationHelper.processTokenResponse`). Safe to call on every
|
|
78
|
+
* request — it's an in-memory write and the cookie always reflects the
|
|
79
|
+
* freshest tokens (the refresh path re-issues the cookie too).
|
|
80
|
+
*/
|
|
81
|
+
async rehydrateSessionFromPayload(session) {
|
|
82
|
+
if (!this.isInitialized || !session?.sessionId || !session?.accessToken) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const storageManager = await this.legacy.getStorageManager();
|
|
86
|
+
const iatSeconds = typeof session.iat === "number" ? session.iat : Math.floor(Date.now() / 1e3);
|
|
87
|
+
const expiresInSeconds = typeof session.accessTokenExpiresAt === "number" ? Math.max(0, session.accessTokenExpiresAt - iatSeconds) : 3600;
|
|
88
|
+
await storageManager.setSessionData(
|
|
89
|
+
{
|
|
90
|
+
access_token: session.accessToken,
|
|
91
|
+
created_at: iatSeconds * 1e3,
|
|
92
|
+
expires_in: String(expiresInSeconds || 3600),
|
|
93
|
+
id_token: session.idToken ?? "",
|
|
94
|
+
refresh_token: session.refreshToken ?? "",
|
|
95
|
+
scope: session.scopes ?? "",
|
|
96
|
+
session_state: "",
|
|
97
|
+
token_type: "Bearer"
|
|
98
|
+
},
|
|
99
|
+
session.sessionId
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Initiates the authorization code flow, handles an embedded sign-in step,
|
|
104
|
+
* or exchanges a code for tokens.
|
|
105
|
+
*
|
|
106
|
+
* Overload 1 — **redirect-flow** (existing callers like `signin.get.ts`):
|
|
107
|
+
* ```
|
|
108
|
+
* signIn(authURLCallback, sessionId, code?, sessionState?, state?, config?)
|
|
109
|
+
* ```
|
|
110
|
+
* Overload 2 — **embedded flow initiate** (flowId === ''):
|
|
111
|
+
* ```
|
|
112
|
+
* signIn({flowId: ''}, request, sessionId)
|
|
113
|
+
* ```
|
|
114
|
+
* Dispatches to `initializeEmbeddedSignInFlow`.
|
|
115
|
+
*
|
|
116
|
+
* Overload 3 — **embedded flow execute** (flowId set):
|
|
117
|
+
* ```
|
|
118
|
+
* signIn(payload, request, sessionId)
|
|
119
|
+
* ```
|
|
120
|
+
* Dispatches to `executeEmbeddedSignInFlow`.
|
|
121
|
+
*
|
|
122
|
+
* Overload 4 — **code exchange** (completion after embedded flow):
|
|
123
|
+
* ```
|
|
124
|
+
* signIn({code, state, session_state}, {}, sessionId)
|
|
125
|
+
* ```
|
|
126
|
+
* Falls through to the legacy redirect-flow code-exchange path.
|
|
127
|
+
*/
|
|
128
|
+
signIn(...args) {
|
|
129
|
+
const arg0 = args[0];
|
|
130
|
+
if (typeof arg0 === "object" && arg0 !== null && "flowId" in arg0) {
|
|
131
|
+
const sessionId = args[2];
|
|
132
|
+
if (arg0.flowId === "") {
|
|
133
|
+
return this.getAuthorizeRequestUrl(
|
|
134
|
+
{ client_secret: "{{clientSecret}}", response_mode: "direct" },
|
|
135
|
+
sessionId
|
|
136
|
+
).then((authorizeUrl) => {
|
|
137
|
+
const url = new URL(authorizeUrl);
|
|
138
|
+
return initializeEmbeddedSignInFlow({
|
|
139
|
+
payload: Object.fromEntries(url.searchParams.entries()),
|
|
140
|
+
url: `${url.origin}${url.pathname}`
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const request = args[1] ?? {};
|
|
145
|
+
return executeEmbeddedSignInFlow({
|
|
146
|
+
payload: arg0,
|
|
147
|
+
url: request.url
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (typeof arg0 === "object" && arg0 !== null && ("code" in arg0 || "state" in arg0)) {
|
|
151
|
+
const payload = arg0;
|
|
152
|
+
const code = typeof payload.code === "string" ? payload.code : void 0;
|
|
153
|
+
const sessionState = typeof payload.session_state === "string" ? payload.session_state : void 0;
|
|
154
|
+
const state = typeof payload.state === "string" ? payload.state : void 0;
|
|
155
|
+
const extraParams = {};
|
|
156
|
+
if (code) {
|
|
157
|
+
extraParams.code = code;
|
|
158
|
+
}
|
|
159
|
+
if (sessionState) {
|
|
160
|
+
extraParams.session_state = sessionState;
|
|
161
|
+
}
|
|
162
|
+
if (state) {
|
|
163
|
+
extraParams.state = state;
|
|
164
|
+
}
|
|
165
|
+
return this.legacy.signIn(args[3], args[2], code, sessionState, state, extraParams);
|
|
166
|
+
}
|
|
167
|
+
return this.legacy.signIn(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
168
|
+
}
|
|
169
|
+
async signUp(payloadOrOptions) {
|
|
170
|
+
if (!payloadOrOptions || !("flowType" in payloadOrOptions)) {
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
const configData = await this.legacy.getConfigData?.();
|
|
174
|
+
const baseUrl = configData?.baseUrl;
|
|
175
|
+
const response = await executeEmbeddedSignUpFlow({
|
|
176
|
+
baseUrl,
|
|
177
|
+
payload: payloadOrOptions
|
|
178
|
+
});
|
|
179
|
+
return response;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns the OAuth2 authorization URL.
|
|
183
|
+
* Used by the redirect-flow GET handler and the embedded-flow initiation path.
|
|
184
|
+
*
|
|
185
|
+
* Mirrors `ThunderIDNextClient.getAuthorizeRequestUrl`.
|
|
186
|
+
*/
|
|
187
|
+
async getAuthorizeRequestUrl(customParams, userId) {
|
|
188
|
+
return this.legacy.getSignInUrl(customParams, userId);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Clears the session and returns the RP-Initiated Logout URL.
|
|
192
|
+
* Accepts either `(sessionId: string)` or `(options?, sessionId?, callback?)`.
|
|
193
|
+
*
|
|
194
|
+
* For ThunderIDV2 (Thunder), RP-Initiated Logout is not yet supported by the platform.
|
|
195
|
+
* Skip the /oidc/logout call and return afterSignOutUrl directly — the caller
|
|
196
|
+
* (signout.post.ts) is responsible for clearing session cookies.
|
|
197
|
+
*/
|
|
198
|
+
async signOut(...args) {
|
|
199
|
+
const sessionId = typeof args[0] === "string" ? args[0] : args[1];
|
|
200
|
+
const configData = await this.legacy.getConfigData?.();
|
|
201
|
+
if (configData?.platform === Platform.ThunderIDV2) {
|
|
202
|
+
return configData?.afterSignOutUrl || configData?.afterSignInUrl || "/";
|
|
203
|
+
}
|
|
204
|
+
return this.legacy.signOut(sessionId);
|
|
205
|
+
}
|
|
206
|
+
getUser(sessionId) {
|
|
207
|
+
return this.legacy.getUser(sessionId);
|
|
208
|
+
}
|
|
209
|
+
getAccessToken(sessionId) {
|
|
210
|
+
return this.legacy.getAccessToken(sessionId);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Decodes and returns the ID token claims for the given session.
|
|
214
|
+
* Exposed here (as on {@link ThunderIDNextClient}) so route handlers can
|
|
215
|
+
* access ID token claims without falling back to the legacy client.
|
|
216
|
+
*/
|
|
217
|
+
getDecodedIdToken(sessionId, idToken) {
|
|
218
|
+
return this.legacy.getDecodedIdToken(sessionId, idToken);
|
|
219
|
+
}
|
|
220
|
+
isSignedIn(sessionId) {
|
|
221
|
+
return this.legacy.isSignedIn(sessionId);
|
|
222
|
+
}
|
|
223
|
+
exchangeToken(config, sessionId) {
|
|
224
|
+
return this.legacy.exchangeToken(config, sessionId);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Fetches the flattened SCIM2 user profile for the given session.
|
|
228
|
+
* Mirrors `ThunderIDNextClient.getUserProfile` — calls `getScim2Me` +
|
|
229
|
+
* `getSchemas` + `generateFlattenedUserProfile` and falls back to
|
|
230
|
+
* `getUser` claims if SCIM2 is unavailable.
|
|
231
|
+
*/
|
|
232
|
+
async getUserProfile(sessionId) {
|
|
233
|
+
const accessToken = await this.getAccessToken(sessionId);
|
|
234
|
+
const configData = await this.legacy.getConfigData?.();
|
|
235
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
236
|
+
if (configData?.platform === Platform.ThunderIDV2) {
|
|
237
|
+
const user = await this.getUser(sessionId);
|
|
238
|
+
return { flattenedProfile: user, profile: user, schemas: [] };
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const authHeaders = { Authorization: `Bearer ${accessToken}` };
|
|
242
|
+
const [profile, schemas] = await Promise.all([
|
|
243
|
+
getScim2Me({ baseUrl, headers: authHeaders }),
|
|
244
|
+
getSchemas({ baseUrl, headers: authHeaders })
|
|
245
|
+
]);
|
|
246
|
+
const processedSchemas = flattenUserSchema(schemas);
|
|
247
|
+
return {
|
|
248
|
+
flattenedProfile: generateFlattenedUserProfile(profile, processedSchemas),
|
|
249
|
+
profile,
|
|
250
|
+
schemas: processedSchemas
|
|
251
|
+
};
|
|
252
|
+
} catch {
|
|
253
|
+
const user = await this.getUser(sessionId);
|
|
254
|
+
return { flattenedProfile: user, profile: user, schemas: [] };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Extracts the current organisation from the decoded ID token.
|
|
259
|
+
* Returns null when the user is not acting within an organisation.
|
|
260
|
+
*/
|
|
261
|
+
async getCurrentOrganization(sessionId) {
|
|
262
|
+
try {
|
|
263
|
+
const idToken = await this.getDecodedIdToken(sessionId);
|
|
264
|
+
if (!idToken?.org_id) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
id: idToken.org_id,
|
|
269
|
+
name: idToken.org_name ?? "",
|
|
270
|
+
orgHandle: idToken.org_handle ?? ""
|
|
271
|
+
};
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns the list of organisations the authenticated user is a member of.
|
|
278
|
+
*/
|
|
279
|
+
async getMyOrganizations(sessionId) {
|
|
280
|
+
const accessToken = await this.getAccessToken(sessionId);
|
|
281
|
+
const configData = await this.legacy.getConfigData?.();
|
|
282
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
283
|
+
return getMeOrganizations({
|
|
284
|
+
baseUrl,
|
|
285
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Fetches the branding preference for the tenant / application.
|
|
290
|
+
* Delegates to the standalone `getBrandingPreference` API helper from
|
|
291
|
+
* `@thunderid/node`, which does not require an authenticated session.
|
|
292
|
+
*/
|
|
293
|
+
// eslint-disable-next-line class-methods-use-this
|
|
294
|
+
async getBrandingPreference(config) {
|
|
295
|
+
return getBrandingPreference(config);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Updates the SCIM2 /Me profile for the authenticated user.
|
|
299
|
+
* Mirrors `ThunderIDNextClient.updateUserProfile`.
|
|
300
|
+
*/
|
|
301
|
+
async updateUserProfile(config, sessionId) {
|
|
302
|
+
const accessToken = await this.getAccessToken(sessionId);
|
|
303
|
+
const configData = await this.legacy.getConfigData?.();
|
|
304
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
305
|
+
if (configData?.platform === Platform.ThunderIDV2) {
|
|
306
|
+
throw new Error("Profile updates are not supported for the ThunderIDV2 (Thunder) platform.");
|
|
307
|
+
}
|
|
308
|
+
return updateMeProfile({
|
|
309
|
+
...config,
|
|
310
|
+
// pass-through, includes payload
|
|
311
|
+
baseUrl,
|
|
312
|
+
headers: { ...config.headers, Authorization: `Bearer ${accessToken}` }
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Retrieves all organisations accessible to the authenticated user
|
|
317
|
+
* (paginated). Mirrors `ThunderIDNextClient.getAllOrganizations`.
|
|
318
|
+
*/
|
|
319
|
+
async getAllOrganizations(options, sessionId) {
|
|
320
|
+
const resolvedSessionId = sessionId ?? "";
|
|
321
|
+
const accessToken = await this.getAccessToken(resolvedSessionId);
|
|
322
|
+
const configData = await this.legacy.getConfigData?.();
|
|
323
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
324
|
+
return getAllOrganizations({
|
|
325
|
+
baseUrl,
|
|
326
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Creates a new sub-organisation. Mirrors `ThunderIDNextClient.createOrganization`.
|
|
331
|
+
*/
|
|
332
|
+
async createOrganization(payload, sessionId) {
|
|
333
|
+
const accessToken = await this.getAccessToken(sessionId);
|
|
334
|
+
const configData = await this.legacy.getConfigData?.();
|
|
335
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
336
|
+
return createOrganization({
|
|
337
|
+
baseUrl,
|
|
338
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
339
|
+
payload
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Fetches the details of a single organisation by ID.
|
|
344
|
+
* Mirrors `ThunderIDNextClient.getOrganization`.
|
|
345
|
+
*/
|
|
346
|
+
async getOrganization(organizationId, sessionId) {
|
|
347
|
+
const accessToken = await this.getAccessToken(sessionId);
|
|
348
|
+
const configData = await this.legacy.getConfigData?.();
|
|
349
|
+
const baseUrl = configData?.baseUrl ?? "";
|
|
350
|
+
return getOrganization({
|
|
351
|
+
baseUrl,
|
|
352
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
353
|
+
organizationId
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Performs an organisation-switch token exchange and returns the new
|
|
358
|
+
* `TokenResponse`. The caller (the Nitro route) is responsible for
|
|
359
|
+
* persisting the new session cookie.
|
|
360
|
+
*
|
|
361
|
+
* Mirrors `ThunderIDNextClient.switchOrganization`.
|
|
362
|
+
*/
|
|
363
|
+
async switchOrganization(organization, sessionId) {
|
|
364
|
+
if (!organization.id) {
|
|
365
|
+
throw new Error("Organization ID is required for switching organizations.");
|
|
366
|
+
}
|
|
367
|
+
const exchangeConfig = {
|
|
368
|
+
attachToken: false,
|
|
369
|
+
data: {
|
|
370
|
+
client_id: "{{clientId}}",
|
|
371
|
+
client_secret: "{{clientSecret}}",
|
|
372
|
+
grant_type: "organization_switch",
|
|
373
|
+
scope: "{{scopes}}",
|
|
374
|
+
switching_organization: organization.id,
|
|
375
|
+
token: "{{accessToken}}"
|
|
376
|
+
},
|
|
377
|
+
id: "organization-switch",
|
|
378
|
+
returnsSession: true,
|
|
379
|
+
signInRequired: true
|
|
380
|
+
};
|
|
381
|
+
return this.legacy.exchangeToken(exchangeConfig, sessionId);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
export default ThunderIDNuxtClient;
|