@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,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* GET /api/auth/callback
|
|
20
|
+
*
|
|
21
|
+
* Handles the OAuth2 callback from ThunderID.
|
|
22
|
+
* Exchanges the authorization code for tokens,
|
|
23
|
+
* creates a signed session JWT cookie,
|
|
24
|
+
* and redirects to afterSignInUrl.
|
|
25
|
+
*/
|
|
26
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
27
|
+
export default _default;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { defineEventHandler, getQuery, getCookie, deleteCookie, sendRedirect, createError } from "h3";
|
|
2
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
3
|
+
import {
|
|
4
|
+
issueSessionCookie,
|
|
5
|
+
verifyTempSessionToken,
|
|
6
|
+
getTempSessionCookieName,
|
|
7
|
+
getTempSessionCookieOptions
|
|
8
|
+
} from "../../../utils/session.js";
|
|
9
|
+
import { useRuntimeConfig } from "#imports";
|
|
10
|
+
export default defineEventHandler(async (event) => {
|
|
11
|
+
const config = useRuntimeConfig();
|
|
12
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
13
|
+
const publicConfig = config.public.thunderid;
|
|
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"];
|
|
20
|
+
if (error) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 400,
|
|
23
|
+
statusMessage: `Authentication failed: ${errorDescription || error}`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (!code || !state) {
|
|
27
|
+
throw createError({
|
|
28
|
+
statusCode: 400,
|
|
29
|
+
statusMessage: "Missing required OAuth parameters: code and state are required."
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const tempSessionCookie = getCookie(event, getTempSessionCookieName());
|
|
33
|
+
if (!tempSessionCookie) {
|
|
34
|
+
throw createError({
|
|
35
|
+
statusCode: 400,
|
|
36
|
+
statusMessage: "No temporary session found. Please start the sign-in flow again."
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
let sessionId;
|
|
40
|
+
let returnTo;
|
|
41
|
+
try {
|
|
42
|
+
const tempSession = await verifyTempSessionToken(
|
|
43
|
+
tempSessionCookie,
|
|
44
|
+
sessionSecret
|
|
45
|
+
);
|
|
46
|
+
sessionId = tempSession.sessionId;
|
|
47
|
+
returnTo = tempSession.returnTo;
|
|
48
|
+
} catch {
|
|
49
|
+
throw createError({
|
|
50
|
+
statusCode: 400,
|
|
51
|
+
statusMessage: "Invalid or expired temporary session. Please start the sign-in flow again."
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
55
|
+
let tokenResponse;
|
|
56
|
+
try {
|
|
57
|
+
tokenResponse = await client.signIn(
|
|
58
|
+
() => {
|
|
59
|
+
},
|
|
60
|
+
// no-op redirect callback (we're handling the code exchange)
|
|
61
|
+
sessionId,
|
|
62
|
+
code,
|
|
63
|
+
sessionState || "",
|
|
64
|
+
state
|
|
65
|
+
);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw createError({
|
|
68
|
+
data: err?.message || "An unexpected error occurred during token exchange.",
|
|
69
|
+
statusCode: 500,
|
|
70
|
+
statusMessage: "Token exchange failed."
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!tokenResponse?.accessToken && !tokenResponse?.idToken) {
|
|
74
|
+
throw createError({
|
|
75
|
+
statusCode: 500,
|
|
76
|
+
statusMessage: "Token exchange failed: Invalid response from Identity Provider."
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await issueSessionCookie(event, sessionId, tokenResponse, sessionSecret);
|
|
81
|
+
deleteCookie(event, getTempSessionCookieName(), getTempSessionCookieOptions());
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error("[thunderid] Failed to create JWT session:", err?.message || err);
|
|
84
|
+
throw createError({
|
|
85
|
+
statusCode: 500,
|
|
86
|
+
statusMessage: "Failed to establish session after authentication."
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const redirectUrl = returnTo || publicConfig.afterSignInUrl || "/";
|
|
90
|
+
return sendRedirect(event, redirectUrl, 302);
|
|
91
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* POST /api/auth/callback
|
|
20
|
+
*
|
|
21
|
+
* Exchanges an authorization code for tokens and issues a session cookie.
|
|
22
|
+
* Called by the client-side `ThunderIDCallback` component after the IDP
|
|
23
|
+
* redirects back with `?code=...&state=...`.
|
|
24
|
+
*
|
|
25
|
+
* Request body:
|
|
26
|
+
* - `code` — authorization code from the IDP redirect
|
|
27
|
+
* - `state` — state parameter from the redirect
|
|
28
|
+
* - `sessionState` — session_state parameter from the redirect (optional)
|
|
29
|
+
*
|
|
30
|
+
* Response shape (success):
|
|
31
|
+
* ```json
|
|
32
|
+
* { "redirectUrl": "/dashboard", "success": true }
|
|
33
|
+
* ```
|
|
34
|
+
* Response shape (error):
|
|
35
|
+
* ```json
|
|
36
|
+
* { "success": false, "error": "..." }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
40
|
+
error: any;
|
|
41
|
+
success: boolean;
|
|
42
|
+
redirectUrl?: undefined;
|
|
43
|
+
} | {
|
|
44
|
+
redirectUrl: string;
|
|
45
|
+
success: boolean;
|
|
46
|
+
error?: undefined;
|
|
47
|
+
}>>;
|
|
48
|
+
export default _default;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineEventHandler, readBody, getCookie, deleteCookie, createError } from "h3";
|
|
2
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
3
|
+
import {
|
|
4
|
+
issueSessionCookie,
|
|
5
|
+
verifyTempSessionToken,
|
|
6
|
+
getTempSessionCookieName,
|
|
7
|
+
getTempSessionCookieOptions
|
|
8
|
+
} from "../../../utils/session.js";
|
|
9
|
+
import { useRuntimeConfig } from "#imports";
|
|
10
|
+
function isTokenResponse(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && ("accessToken" in value || "idToken" in value || "refreshToken" in value);
|
|
12
|
+
}
|
|
13
|
+
export default defineEventHandler(async (event) => {
|
|
14
|
+
const config = useRuntimeConfig();
|
|
15
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
16
|
+
const afterSignInUrl = config.public.thunderid?.afterSignInUrl || "/";
|
|
17
|
+
const body = await readBody(event);
|
|
18
|
+
const { code, state, sessionState } = body ?? {};
|
|
19
|
+
if (!code) {
|
|
20
|
+
throw createError({ statusCode: 400, statusMessage: "Missing required parameter: code" });
|
|
21
|
+
}
|
|
22
|
+
const tempCookie = getCookie(event, getTempSessionCookieName());
|
|
23
|
+
if (!tempCookie) {
|
|
24
|
+
throw createError({ statusCode: 400, statusMessage: "No active auth session found. Please restart sign-in." });
|
|
25
|
+
}
|
|
26
|
+
let sessionId;
|
|
27
|
+
try {
|
|
28
|
+
const tempSession = await verifyTempSessionToken(
|
|
29
|
+
tempCookie,
|
|
30
|
+
sessionSecret
|
|
31
|
+
);
|
|
32
|
+
sessionId = tempSession.sessionId;
|
|
33
|
+
} catch {
|
|
34
|
+
throw createError({ statusCode: 400, statusMessage: "Auth session expired or invalid. Please restart sign-in." });
|
|
35
|
+
}
|
|
36
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
37
|
+
let tokenResponse;
|
|
38
|
+
try {
|
|
39
|
+
tokenResponse = await client.signIn({ code, session_state: sessionState, state }, {}, sessionId);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return { error: err?.message ?? String(err), success: false };
|
|
42
|
+
}
|
|
43
|
+
if (!isTokenResponse(tokenResponse)) {
|
|
44
|
+
return { error: "Invalid token response from Identity Provider.", success: false };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await issueSessionCookie(event, sessionId, tokenResponse, sessionSecret);
|
|
48
|
+
deleteCookie(event, getTempSessionCookieName(), getTempSessionCookieOptions());
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return { error: `Failed to establish session: ${err?.message ?? String(err)}`, success: false };
|
|
51
|
+
}
|
|
52
|
+
return { redirectUrl: afterSignInUrl, success: true };
|
|
53
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
import type { ThunderIDAuthState } from '../../../../types.js';
|
|
19
|
+
/**
|
|
20
|
+
* GET /api/auth/session
|
|
21
|
+
*
|
|
22
|
+
* Returns the current auth state: { isSignedIn, user, isLoading }.
|
|
23
|
+
* Used by the client-side composable to hydrate auth state.
|
|
24
|
+
*/
|
|
25
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<ThunderIDAuthState>>;
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineEventHandler } from "h3";
|
|
2
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
3
|
+
import { verifyAndRehydrateSession } from "../../../utils/serverSession.js";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
8
|
+
const session = await verifyAndRehydrateSession(
|
|
9
|
+
event,
|
|
10
|
+
sessionSecret
|
|
11
|
+
);
|
|
12
|
+
if (!session) {
|
|
13
|
+
return { isLoading: false, isSignedIn: false, user: null };
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
17
|
+
const user = await client.getUser(session.sessionId);
|
|
18
|
+
return { isLoading: false, isSignedIn: true, user };
|
|
19
|
+
} catch {
|
|
20
|
+
return { isLoading: false, isSignedIn: false, user: null };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* GET /api/auth/signin
|
|
20
|
+
*
|
|
21
|
+
* Initiates the OAuth2 authorization code flow with PKCE.
|
|
22
|
+
* Creates a temp session, stores it in a signed JWT cookie,
|
|
23
|
+
* and redirects the user to ThunderID's authorization endpoint.
|
|
24
|
+
*
|
|
25
|
+
* Accepts an optional `returnTo` query parameter to redirect
|
|
26
|
+
* the user to a specific page after sign-in.
|
|
27
|
+
*/
|
|
28
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
29
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { generateSessionId } from "@thunderid/node";
|
|
2
|
+
import { defineEventHandler, getQuery, sendRedirect, setCookie, createError } from "h3";
|
|
3
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
4
|
+
import { createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions } from "../../../utils/session.js";
|
|
5
|
+
import { useRuntimeConfig } from "#imports";
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
8
|
+
const config = useRuntimeConfig();
|
|
9
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
10
|
+
const query = getQuery(event);
|
|
11
|
+
const returnTo = query["returnTo"];
|
|
12
|
+
const safeReturnTo = returnTo && returnTo.startsWith("/") && !returnTo.startsWith("//") ? returnTo : void 0;
|
|
13
|
+
const sessionId = generateSessionId();
|
|
14
|
+
const tempToken = await createTempSessionToken(sessionId, sessionSecret, safeReturnTo);
|
|
15
|
+
setCookie(event, getTempSessionCookieName(), tempToken, getTempSessionCookieOptions());
|
|
16
|
+
let authorizationUrl = null;
|
|
17
|
+
await client.signIn(
|
|
18
|
+
(url) => {
|
|
19
|
+
authorizationUrl = url;
|
|
20
|
+
},
|
|
21
|
+
sessionId,
|
|
22
|
+
void 0,
|
|
23
|
+
// no authorization code (initial redirect)
|
|
24
|
+
void 0,
|
|
25
|
+
// no session_state
|
|
26
|
+
void 0,
|
|
27
|
+
// no state
|
|
28
|
+
{}
|
|
29
|
+
);
|
|
30
|
+
if (!authorizationUrl) {
|
|
31
|
+
throw createError({
|
|
32
|
+
statusCode: 500,
|
|
33
|
+
statusMessage: "Failed to generate authorization URL."
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return sendRedirect(event, authorizationUrl, 302);
|
|
37
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* POST /api/auth/signin
|
|
20
|
+
*
|
|
21
|
+
* Handles embedded (app-native) sign-in flow steps.
|
|
22
|
+
*
|
|
23
|
+
* Request body:
|
|
24
|
+
* - `payload` — the embedded flow step payload (`EmbeddedSignInFlowHandleRequestPayload`).
|
|
25
|
+
* When omitted or `{}`, the flow is initialised and the authorize URL is returned.
|
|
26
|
+
* - `request` — optional per-step config (e.g. `{ url }` override).
|
|
27
|
+
*
|
|
28
|
+
* Response shape:
|
|
29
|
+
* ```json
|
|
30
|
+
* { "data": { ... }, "success": true }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
34
|
+
data: unknown;
|
|
35
|
+
success: boolean;
|
|
36
|
+
}>>;
|
|
37
|
+
export default _default;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { EmbeddedSignInFlowStatus, generateSessionId, isEmpty } from "@thunderid/node";
|
|
2
|
+
import { defineEventHandler, readBody, getCookie, setCookie, deleteCookie, createError } from "h3";
|
|
3
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
4
|
+
import { useServerSession } from "../../../utils/serverSession.js";
|
|
5
|
+
import {
|
|
6
|
+
issueSessionCookie,
|
|
7
|
+
createTempSessionToken,
|
|
8
|
+
verifyTempSessionToken,
|
|
9
|
+
getTempSessionCookieName,
|
|
10
|
+
getTempSessionCookieOptions
|
|
11
|
+
} from "../../../utils/session.js";
|
|
12
|
+
import { useRuntimeConfig } from "#imports";
|
|
13
|
+
function isTokenResponse(value) {
|
|
14
|
+
return typeof value === "object" && value !== null && ("accessToken" in value || "idToken" in value || "refreshToken" in value);
|
|
15
|
+
}
|
|
16
|
+
export default defineEventHandler(async (event) => {
|
|
17
|
+
const config = useRuntimeConfig();
|
|
18
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
19
|
+
const afterSignInUrl = config.public.thunderid?.afterSignInUrl || "/";
|
|
20
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
21
|
+
let sessionId;
|
|
22
|
+
const liveSession = await useServerSession(event);
|
|
23
|
+
if (liveSession?.sessionId) {
|
|
24
|
+
sessionId = liveSession.sessionId;
|
|
25
|
+
} else {
|
|
26
|
+
const tempCookie = getCookie(event, getTempSessionCookieName());
|
|
27
|
+
if (tempCookie) {
|
|
28
|
+
try {
|
|
29
|
+
const tempSession = await verifyTempSessionToken(
|
|
30
|
+
tempCookie,
|
|
31
|
+
sessionSecret
|
|
32
|
+
);
|
|
33
|
+
sessionId = tempSession.sessionId;
|
|
34
|
+
} catch {
|
|
35
|
+
sessionId = generateSessionId();
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
sessionId = generateSessionId();
|
|
39
|
+
}
|
|
40
|
+
const tempToken = await createTempSessionToken(sessionId, sessionSecret);
|
|
41
|
+
setCookie(event, getTempSessionCookieName(), tempToken, getTempSessionCookieOptions());
|
|
42
|
+
}
|
|
43
|
+
const body = await readBody(event);
|
|
44
|
+
const payload = body?.payload ?? {};
|
|
45
|
+
const request = body?.request ?? {};
|
|
46
|
+
if (isEmpty(payload) || !("flowId" in payload)) {
|
|
47
|
+
try {
|
|
48
|
+
const signInUrl = await client.getAuthorizeRequestUrl(
|
|
49
|
+
{ client_secret: "{{clientSecret}}", response_mode: "direct" },
|
|
50
|
+
sessionId
|
|
51
|
+
);
|
|
52
|
+
return { data: { signInUrl }, success: true };
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throw createError({
|
|
55
|
+
statusCode: 500,
|
|
56
|
+
statusMessage: `Failed to build authorize URL: ${err?.message ?? String(err)}`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let response;
|
|
61
|
+
try {
|
|
62
|
+
response = await client.signIn(payload, request, sessionId);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
throw createError({
|
|
65
|
+
statusCode: 502,
|
|
66
|
+
statusMessage: `Embedded sign-in step failed: ${err?.message ?? String(err)}`
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (response?.flowStatus === EmbeddedSignInFlowStatus.SuccessCompleted) {
|
|
70
|
+
const authData = response?.authData ?? {};
|
|
71
|
+
const { code, state, session_state: sessionState } = authData;
|
|
72
|
+
if (!code) {
|
|
73
|
+
throw createError({ statusCode: 502, statusMessage: "Authorization code missing from completed flow response." });
|
|
74
|
+
}
|
|
75
|
+
let tokenResponse;
|
|
76
|
+
try {
|
|
77
|
+
tokenResponse = await client.signIn({ code, session_state: sessionState, state }, {}, sessionId);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
throw createError({
|
|
80
|
+
statusCode: 502,
|
|
81
|
+
statusMessage: `Token exchange failed after embedded flow: ${err?.message ?? String(err)}`
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (!isTokenResponse(tokenResponse)) {
|
|
85
|
+
throw createError({
|
|
86
|
+
statusCode: 502,
|
|
87
|
+
statusMessage: "Token exchange failed: Invalid token response from Identity Provider."
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
await issueSessionCookie(event, sessionId, tokenResponse, sessionSecret);
|
|
92
|
+
deleteCookie(event, getTempSessionCookieName(), getTempSessionCookieOptions());
|
|
93
|
+
} catch (err) {
|
|
94
|
+
throw createError({
|
|
95
|
+
statusCode: 500,
|
|
96
|
+
statusMessage: `Failed to establish session: ${err?.message ?? String(err)}`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return { data: { afterSignInUrl }, success: true };
|
|
100
|
+
}
|
|
101
|
+
return { data: response, success: true };
|
|
102
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* POST /api/auth/signout
|
|
20
|
+
*
|
|
21
|
+
* Signs the user out by:
|
|
22
|
+
* 1. Getting the sign-out URL from ThunderID (for RP-Initiated Logout)
|
|
23
|
+
* 2. Clearing all session cookies
|
|
24
|
+
* 3. Returning `{ redirectUrl }` for the client to navigate to
|
|
25
|
+
*
|
|
26
|
+
* Using POST instead of GET prevents CSRF-based forced sign-outs.
|
|
27
|
+
*/
|
|
28
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
+
redirectUrl: string;
|
|
30
|
+
}>>;
|
|
31
|
+
export default _default;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineEventHandler, deleteCookie } from "h3";
|
|
2
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
3
|
+
import { verifyAndRehydrateSession } from "../../../utils/serverSession.js";
|
|
4
|
+
import {
|
|
5
|
+
getSessionCookieName,
|
|
6
|
+
getSessionCookieOptions,
|
|
7
|
+
getTempSessionCookieName,
|
|
8
|
+
getTempSessionCookieOptions
|
|
9
|
+
} from "../../../utils/session.js";
|
|
10
|
+
import { useRuntimeConfig } from "#imports";
|
|
11
|
+
export default defineEventHandler(async (event) => {
|
|
12
|
+
const config = useRuntimeConfig();
|
|
13
|
+
const sessionSecret = config.thunderid?.sessionSecret;
|
|
14
|
+
const publicConfig = config.public.thunderid;
|
|
15
|
+
const fallbackUrl = publicConfig.afterSignOutUrl || "/";
|
|
16
|
+
const clearCookies = () => {
|
|
17
|
+
deleteCookie(event, getSessionCookieName(), getSessionCookieOptions());
|
|
18
|
+
deleteCookie(event, getTempSessionCookieName(), getTempSessionCookieOptions());
|
|
19
|
+
};
|
|
20
|
+
const session = await verifyAndRehydrateSession(
|
|
21
|
+
event,
|
|
22
|
+
sessionSecret
|
|
23
|
+
);
|
|
24
|
+
if (!session) {
|
|
25
|
+
clearCookies();
|
|
26
|
+
return { redirectUrl: fallbackUrl };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
30
|
+
const signOutUrl = await client.signOut(session.sessionId);
|
|
31
|
+
clearCookies();
|
|
32
|
+
return { redirectUrl: signOutUrl || fallbackUrl };
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error("[thunderid] Sign-out error:", err?.message || err);
|
|
35
|
+
clearCookies();
|
|
36
|
+
return { redirectUrl: fallbackUrl };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* POST /api/auth/signup
|
|
20
|
+
*
|
|
21
|
+
* Handles embedded (app-native) sign-up flow steps.
|
|
22
|
+
*
|
|
23
|
+
* Request body:
|
|
24
|
+
* - `payload` — the embedded sign-up flow step payload (`EmbeddedFlowExecuteRequestPayload`).
|
|
25
|
+
* When omitted, returns an empty `signUpUrl` (caller should redirect to the sign-up page).
|
|
26
|
+
*
|
|
27
|
+
* Response shape:
|
|
28
|
+
* ```json
|
|
29
|
+
* { "data": { ... }, "success": true }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
33
|
+
data: unknown;
|
|
34
|
+
success: boolean;
|
|
35
|
+
}>>;
|
|
36
|
+
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { EmbeddedFlowStatus } from "@thunderid/node";
|
|
2
|
+
import { defineEventHandler, readBody, createError } from "h3";
|
|
3
|
+
import ThunderIDNuxtClient from "../../../ThunderIDNuxtClient.js";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
function hasFlowStatus(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && "flowStatus" in value;
|
|
7
|
+
}
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const config = useRuntimeConfig();
|
|
10
|
+
const afterSignUpUrl = config.public.thunderid?.afterSignInUrl || "/";
|
|
11
|
+
const body = await readBody(event);
|
|
12
|
+
const payload = body?.payload;
|
|
13
|
+
if (!payload) {
|
|
14
|
+
return { data: { signUpUrl: "" }, success: true };
|
|
15
|
+
}
|
|
16
|
+
const client = ThunderIDNuxtClient.getInstance();
|
|
17
|
+
let response;
|
|
18
|
+
try {
|
|
19
|
+
response = await client.signUp(payload);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 502,
|
|
23
|
+
statusMessage: `Embedded sign-up step failed: ${err?.message ?? String(err)}`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (hasFlowStatus(response) && response.flowStatus === EmbeddedFlowStatus.Complete) {
|
|
27
|
+
return { data: { afterSignUpUrl }, success: true };
|
|
28
|
+
}
|
|
29
|
+
return { data: response, success: true };
|
|
30
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* GET /api/auth/token
|
|
20
|
+
*
|
|
21
|
+
* Returns a valid access token for the current session.
|
|
22
|
+
* Proactively refreshes the token if it is within 60 seconds of expiry
|
|
23
|
+
* (requires a refresh token stored in the session JWT).
|
|
24
|
+
* Returns 401 if there is no active session or the token cannot be refreshed.
|
|
25
|
+
*/
|
|
26
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
27
|
+
accessToken: string;
|
|
28
|
+
}>>;
|
|
29
|
+
export default _default;
|