@asgardeo/nuxt 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +482 -6
- package/dist/module.d.mts +17 -24
- package/dist/module.json +2 -2
- package/dist/module.mjs +174 -48
- package/dist/runtime/components/AsgardeoRoot.d.ts +52 -0
- package/dist/runtime/components/AsgardeoRoot.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/useAsgardeo.d.ts +38 -0
- package/dist/runtime/composables/useAsgardeo.js +73 -0
- package/dist/runtime/errors/asgardeo-error.d.ts +47 -0
- package/dist/runtime/errors/asgardeo-error.js +15 -0
- package/dist/runtime/errors/error-codes.d.ts +40 -0
- package/dist/runtime/errors/error-codes.js +19 -0
- package/dist/runtime/{composables/asgardeo/useAuth.d.ts → errors/index.d.ts} +2 -2
- package/dist/runtime/errors/index.js +2 -0
- package/dist/runtime/middleware/auth.d.ts +35 -0
- package/dist/runtime/middleware/auth.js +2 -0
- package/dist/runtime/middleware/defineAsgardeoMiddleware.d.ts +53 -0
- package/dist/runtime/middleware/defineAsgardeoMiddleware.js +24 -0
- package/dist/runtime/plugins/asgardeo.d.ts +40 -0
- package/dist/runtime/plugins/asgardeo.js +129 -0
- package/dist/runtime/server/AsgardeoNuxtClient.d.ts +182 -0
- package/dist/runtime/server/AsgardeoNuxtClient.js +366 -0
- package/dist/runtime/server/index.d.ts +33 -0
- package/dist/runtime/server/index.js +3 -0
- package/dist/runtime/server/plugins/asgardeo-ssr.d.ts +41 -0
- package/dist/runtime/server/plugins/asgardeo-ssr.js +134 -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/{handler.d.ts → routes/auth/user/profile.patch.d.ts} +17 -13
- package/dist/runtime/server/routes/auth/user/profile.patch.js +33 -0
- package/dist/runtime/server/{services/asgardeo/index.d.ts → routes/auth/user/user.get.d.ts} +6 -4
- 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/augments.d.ts +61 -0
- package/dist/runtime/types.d.ts +115 -38
- 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 +0 -2
- package/package.json +48 -23
- package/dist/runtime/composables/asgardeo/useAuth.js +0 -129
- package/dist/runtime/server/handler.js +0 -231
- package/dist/runtime/server/services/asgardeo/index.js +0 -18
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getCookie, createError } from "h3";
|
|
2
|
+
import { verifySessionToken, getSessionCookieName } from "./session.js";
|
|
3
|
+
import AsgardeoNuxtClient from "../AsgardeoNuxtClient.js";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export async function useServerSession(event) {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const sessionSecret = config.asgardeo?.sessionSecret;
|
|
8
|
+
const sessionCookie = getCookie(event, getSessionCookieName());
|
|
9
|
+
if (!sessionCookie) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return await verifySessionToken(sessionCookie, sessionSecret);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function requireServerSession(event) {
|
|
19
|
+
const session = await useServerSession(event);
|
|
20
|
+
if (!session) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 401,
|
|
23
|
+
statusMessage: "Unauthorized: Authentication required."
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return session;
|
|
27
|
+
}
|
|
28
|
+
export async function verifyAndRehydrateSession(event, sessionSecret) {
|
|
29
|
+
const sessionCookie = getCookie(event, getSessionCookieName());
|
|
30
|
+
if (!sessionCookie) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
let session;
|
|
34
|
+
try {
|
|
35
|
+
session = await verifySessionToken(sessionCookie, sessionSecret);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await AsgardeoNuxtClient.getInstance().rehydrateSessionFromPayload(session);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
return session;
|
|
44
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
import type { TokenResponse } from '@asgardeo/node';
|
|
19
|
+
import type { H3Event } from 'h3';
|
|
20
|
+
import type { AsgardeoSessionPayload } from '../../types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Create a signed JWT session token.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createSessionToken(params: {
|
|
25
|
+
accessToken: string;
|
|
26
|
+
/** Unix timestamp (seconds) when the access token expires. */
|
|
27
|
+
accessTokenExpiresAt?: number;
|
|
28
|
+
expirySeconds?: number;
|
|
29
|
+
/** Raw ID token string. */
|
|
30
|
+
idToken?: string;
|
|
31
|
+
organizationId?: string;
|
|
32
|
+
/** Refresh token for silent re-auth. */
|
|
33
|
+
refreshToken?: string;
|
|
34
|
+
scopes: string;
|
|
35
|
+
sessionId: string;
|
|
36
|
+
userId: string;
|
|
37
|
+
}, sessionSecret?: string): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Create a signed JWT temp session token (used during OAuth flow).
|
|
40
|
+
*/
|
|
41
|
+
export declare function createTempSessionToken(sessionId: string, sessionSecret?: string, returnTo?: string): Promise<string>;
|
|
42
|
+
/**
|
|
43
|
+
* Verify and decode a session JWT.
|
|
44
|
+
*/
|
|
45
|
+
export declare function verifySessionToken(token: string, sessionSecret?: string): Promise<AsgardeoSessionPayload>;
|
|
46
|
+
/**
|
|
47
|
+
* Verify and decode a temp session JWT.
|
|
48
|
+
*/
|
|
49
|
+
export declare function verifyTempSessionToken(token: string, sessionSecret?: string): Promise<{
|
|
50
|
+
returnTo?: string;
|
|
51
|
+
sessionId: string;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Session cookie name.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getSessionCookieName(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Temp session cookie name.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getTempSessionCookieName(): string;
|
|
61
|
+
/**
|
|
62
|
+
* Session cookie options.
|
|
63
|
+
*/
|
|
64
|
+
type SessionCookieOptions = {
|
|
65
|
+
httpOnly: boolean;
|
|
66
|
+
maxAge: number;
|
|
67
|
+
path: string;
|
|
68
|
+
sameSite: 'lax';
|
|
69
|
+
secure: boolean;
|
|
70
|
+
};
|
|
71
|
+
export declare function getSessionCookieOptions(): SessionCookieOptions;
|
|
72
|
+
/**
|
|
73
|
+
* Temp session cookie options (15 min TTL).
|
|
74
|
+
*/
|
|
75
|
+
export declare function getTempSessionCookieOptions(): SessionCookieOptions;
|
|
76
|
+
/**
|
|
77
|
+
* Decode a token response into a signed session JWT and write it as the
|
|
78
|
+
* session cookie on the H3 event.
|
|
79
|
+
*
|
|
80
|
+
* Extracted from the inline blocks in `callback.get.ts` and
|
|
81
|
+
* `organizations/switch.post.ts` so that all three callers (callback.get,
|
|
82
|
+
* switch.post, and the new `signin.post`) share one implementation.
|
|
83
|
+
*/
|
|
84
|
+
export declare function issueSessionCookie(event: H3Event, sessionId: string, tokenResponse: TokenResponse, sessionSecret?: string): Promise<void>;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { CookieConfig } from "@asgardeo/node";
|
|
2
|
+
import { setCookie } from "h3";
|
|
3
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
4
|
+
const DEFAULT_EXPIRY_SECONDS = 3600;
|
|
5
|
+
function getSecret(sessionSecret) {
|
|
6
|
+
const secret = sessionSecret || process.env["ASGARDEO_SESSION_SECRET"];
|
|
7
|
+
if (!secret) {
|
|
8
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"[asgardeo] ASGARDEO_SESSION_SECRET environment variable is required in production. Set it to a secure random string of at least 32 characters."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
console.warn(
|
|
14
|
+
"[asgardeo] Using default session secret for development. Set ASGARDEO_SESSION_SECRET for production."
|
|
15
|
+
);
|
|
16
|
+
return new TextEncoder().encode("asgardeo-dev-secret-not-for-production");
|
|
17
|
+
}
|
|
18
|
+
return new TextEncoder().encode(secret);
|
|
19
|
+
}
|
|
20
|
+
export async function createSessionToken(params, sessionSecret) {
|
|
21
|
+
const secret = getSecret(sessionSecret);
|
|
22
|
+
return new SignJWT({
|
|
23
|
+
accessToken: params.accessToken,
|
|
24
|
+
accessTokenExpiresAt: params.accessTokenExpiresAt,
|
|
25
|
+
idToken: params.idToken,
|
|
26
|
+
organizationId: params.organizationId,
|
|
27
|
+
refreshToken: params.refreshToken,
|
|
28
|
+
scopes: params.scopes,
|
|
29
|
+
sessionId: params.sessionId,
|
|
30
|
+
type: "session"
|
|
31
|
+
}).setProtectedHeader({ alg: "HS256" }).setSubject(params.userId).setIssuedAt().setExpirationTime(Date.now() / 1e3 + (params.expirySeconds ?? DEFAULT_EXPIRY_SECONDS)).sign(secret);
|
|
32
|
+
}
|
|
33
|
+
export async function createTempSessionToken(sessionId, sessionSecret, returnTo) {
|
|
34
|
+
const secret = getSecret(sessionSecret);
|
|
35
|
+
const payload = {
|
|
36
|
+
sessionId,
|
|
37
|
+
type: "temp"
|
|
38
|
+
};
|
|
39
|
+
if (returnTo) {
|
|
40
|
+
payload["returnTo"] = returnTo;
|
|
41
|
+
}
|
|
42
|
+
return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime("15m").sign(secret);
|
|
43
|
+
}
|
|
44
|
+
export async function verifySessionToken(token, sessionSecret) {
|
|
45
|
+
const secret = getSecret(sessionSecret);
|
|
46
|
+
const { payload } = await jwtVerify(token, secret);
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
export async function verifyTempSessionToken(token, sessionSecret) {
|
|
50
|
+
const secret = getSecret(sessionSecret);
|
|
51
|
+
const { payload } = await jwtVerify(token, secret);
|
|
52
|
+
if (payload["type"] !== "temp") {
|
|
53
|
+
throw new Error("Invalid token type: expected temp session");
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
returnTo: payload["returnTo"],
|
|
57
|
+
sessionId: payload["sessionId"]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function getSessionCookieName() {
|
|
61
|
+
return CookieConfig.SESSION_COOKIE_NAME;
|
|
62
|
+
}
|
|
63
|
+
export function getTempSessionCookieName() {
|
|
64
|
+
return CookieConfig.TEMP_SESSION_COOKIE_NAME;
|
|
65
|
+
}
|
|
66
|
+
export function getSessionCookieOptions() {
|
|
67
|
+
return {
|
|
68
|
+
httpOnly: true,
|
|
69
|
+
maxAge: DEFAULT_EXPIRY_SECONDS,
|
|
70
|
+
path: "/",
|
|
71
|
+
sameSite: "lax",
|
|
72
|
+
secure: process.env["NODE_ENV"] === "production"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function getTempSessionCookieOptions() {
|
|
76
|
+
return {
|
|
77
|
+
httpOnly: true,
|
|
78
|
+
maxAge: 15 * 60,
|
|
79
|
+
path: "/",
|
|
80
|
+
sameSite: "lax",
|
|
81
|
+
secure: process.env["NODE_ENV"] === "production"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function issueSessionCookie(event, sessionId, tokenResponse, sessionSecret) {
|
|
85
|
+
const { default: AsgardeoNuxtClient } = await import("../AsgardeoNuxtClient.js");
|
|
86
|
+
const client = AsgardeoNuxtClient.getInstance();
|
|
87
|
+
const idToken = await client.getDecodedIdToken(sessionId, tokenResponse.idToken);
|
|
88
|
+
const userId = idToken.sub || sessionId;
|
|
89
|
+
const organizationId = idToken["user_org"] || idToken["organization_id"];
|
|
90
|
+
const expiresInSeconds = parseInt(tokenResponse.expiresIn ?? "3600", 10);
|
|
91
|
+
const accessTokenExpiresAt = Math.floor(Date.now() / 1e3) + (Number.isFinite(expiresInSeconds) ? expiresInSeconds : 3600);
|
|
92
|
+
const sessionToken = await createSessionToken(
|
|
93
|
+
{
|
|
94
|
+
accessToken: tokenResponse.accessToken,
|
|
95
|
+
accessTokenExpiresAt,
|
|
96
|
+
idToken: tokenResponse.idToken || void 0,
|
|
97
|
+
organizationId,
|
|
98
|
+
refreshToken: tokenResponse.refreshToken || void 0,
|
|
99
|
+
scopes: tokenResponse.scope || "",
|
|
100
|
+
sessionId,
|
|
101
|
+
userId
|
|
102
|
+
},
|
|
103
|
+
sessionSecret
|
|
104
|
+
);
|
|
105
|
+
setCookie(event, getSessionCookieName(), sessionToken, getSessionCookieOptions());
|
|
106
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
|
|
3
|
+
*
|
|
4
|
+
* WSO2 LLC. licenses this file to you under the Apache License,
|
|
5
|
+
* Version 2.0 (the "License"); you may not use this file except
|
|
6
|
+
* in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing,
|
|
12
|
+
* software distributed under the License is distributed on an
|
|
13
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
* KIND, either express or implied. See the License for the
|
|
15
|
+
* specific language governing permissions and limitations
|
|
16
|
+
* under the License.
|
|
17
|
+
*/
|
|
18
|
+
import { type H3Event } from 'h3';
|
|
19
|
+
/**
|
|
20
|
+
* Return a valid access token for the current request.
|
|
21
|
+
*
|
|
22
|
+
* If the stored access token is still fresh (or has no expiry metadata —
|
|
23
|
+
* e.g. sessions created before Phase 2), it is returned as-is.
|
|
24
|
+
*
|
|
25
|
+
* When the token is within `REFRESH_SKEW_SECONDS` of expiring and a
|
|
26
|
+
* refresh token is present, a `refresh_token` grant is sent to the OIDC
|
|
27
|
+
* token endpoint. On success the session cookie is reissued with the new
|
|
28
|
+
* tokens so subsequent calls within the same browser session are also fresh.
|
|
29
|
+
*
|
|
30
|
+
* Throws a 401 if the token is expired and no refresh token is available, or
|
|
31
|
+
* if the refresh call itself fails.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* // In a Nitro API route:
|
|
36
|
+
* export default defineEventHandler(async (event) => {
|
|
37
|
+
* const accessToken = await getValidAccessToken(event);
|
|
38
|
+
* // use accessToken to call a protected API
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function getValidAccessToken(event: H3Event): Promise<string>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createError, setCookie } from "h3";
|
|
2
|
+
import { requireServerSession } from "./serverSession.js";
|
|
3
|
+
import { createSessionToken, getSessionCookieName, getSessionCookieOptions } from "./session.js";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
const REFRESH_SKEW_SECONDS = 60;
|
|
6
|
+
export async function getValidAccessToken(event) {
|
|
7
|
+
const session = await requireServerSession(event);
|
|
8
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9
|
+
if (!session.accessTokenExpiresAt || session.accessTokenExpiresAt - REFRESH_SKEW_SECONDS > now) {
|
|
10
|
+
return session.accessToken;
|
|
11
|
+
}
|
|
12
|
+
if (!session.refreshToken) {
|
|
13
|
+
throw createError({
|
|
14
|
+
statusCode: 401,
|
|
15
|
+
statusMessage: "Session expired. Please sign in again."
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const config = useRuntimeConfig(event);
|
|
19
|
+
const publicConfig = config.public.asgardeo;
|
|
20
|
+
const privateConfig = config.asgardeo;
|
|
21
|
+
const tokenEndpoint = `${publicConfig.baseUrl}/oauth2/token`;
|
|
22
|
+
const body = new URLSearchParams({
|
|
23
|
+
client_id: publicConfig.clientId,
|
|
24
|
+
grant_type: "refresh_token",
|
|
25
|
+
refresh_token: session.refreshToken
|
|
26
|
+
});
|
|
27
|
+
if (privateConfig?.clientSecret) {
|
|
28
|
+
body.set("client_secret", privateConfig.clientSecret);
|
|
29
|
+
}
|
|
30
|
+
let refreshed;
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(tokenEndpoint, {
|
|
33
|
+
body,
|
|
34
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
35
|
+
method: "POST"
|
|
36
|
+
});
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
const errText = await res.text().catch(() => String(res.status));
|
|
39
|
+
throw new Error(`Token endpoint returned ${res.status}: ${errText}`);
|
|
40
|
+
}
|
|
41
|
+
refreshed = await res.json();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
44
|
+
console.error("[asgardeo] Token refresh failed:", msg);
|
|
45
|
+
throw createError({
|
|
46
|
+
statusCode: 401,
|
|
47
|
+
statusMessage: "Token refresh failed. Please sign in again."
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const newSessionToken = await createSessionToken(
|
|
51
|
+
{
|
|
52
|
+
accessToken: refreshed.access_token,
|
|
53
|
+
accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600),
|
|
54
|
+
idToken: refreshed.id_token ?? session.idToken,
|
|
55
|
+
organizationId: session.organizationId,
|
|
56
|
+
refreshToken: refreshed.refresh_token ?? session.refreshToken,
|
|
57
|
+
scopes: refreshed.scope ?? session.scopes,
|
|
58
|
+
sessionId: session.sessionId,
|
|
59
|
+
userId: session.sub
|
|
60
|
+
},
|
|
61
|
+
privateConfig?.sessionSecret
|
|
62
|
+
);
|
|
63
|
+
setCookie(event, getSessionCookieName(), newSessionToken, getSessionCookieOptions());
|
|
64
|
+
return refreshed.access_token;
|
|
65
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
import type {AsgardeoSessionPayload, AsgardeoSSRData} from './types';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Nuxt schema augmentation — adds `asgardeo` to NuxtConfig / NuxtOptions
|
|
23
|
+
* and ensures the runtime config shapes are fully typed.
|
|
24
|
+
*/
|
|
25
|
+
declare module '@nuxt/schema' {
|
|
26
|
+
interface NuxtConfig {
|
|
27
|
+
asgardeo?: import('./types').AsgardeoNuxtConfig;
|
|
28
|
+
}
|
|
29
|
+
interface NuxtOptions {
|
|
30
|
+
asgardeo?: import('./types').AsgardeoNuxtConfig;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* H3 event context augmentation — provides a typed `event.context.asgardeo`
|
|
36
|
+
* property so server plugins and API routes no longer need `as any` casts.
|
|
37
|
+
*
|
|
38
|
+
* Set by the Nitro `asgardeo-ssr` plugin on every page request.
|
|
39
|
+
*/
|
|
40
|
+
declare module 'h3' {
|
|
41
|
+
interface H3EventContext {
|
|
42
|
+
/**
|
|
43
|
+
* Resolved auth state for the current request.
|
|
44
|
+
* Populated by the Nitro `asgardeo-ssr` plugin during SSR page requests.
|
|
45
|
+
* `null` when the request is an API route or when the session is absent/invalid.
|
|
46
|
+
*/
|
|
47
|
+
asgardeo?: {
|
|
48
|
+
isSignedIn: boolean;
|
|
49
|
+
session: AsgardeoSessionPayload | null;
|
|
50
|
+
/**
|
|
51
|
+
* Rich SSR payload populated by the Nitro `asgardeo-ssr` plugin.
|
|
52
|
+
* Contains the user, userProfile, organisations, and branding data
|
|
53
|
+
* resolved on the server and seeded into `useState` keys for
|
|
54
|
+
* zero-cost client hydration.
|
|
55
|
+
*/
|
|
56
|
+
ssr?: AsgardeoSSRData;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {};
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -15,53 +15,130 @@
|
|
|
15
15
|
* specific language governing permissions and limitations
|
|
16
16
|
* under the License.
|
|
17
17
|
*/
|
|
18
|
-
import type {
|
|
19
|
-
|
|
18
|
+
import type { BrandingPreference, I18nPreferences, Organization, User, UserProfile } from '@asgardeo/node';
|
|
19
|
+
import type { JWTPayload } from 'jose';
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for the Asgardeo Nuxt module.
|
|
22
|
+
*/
|
|
23
|
+
export interface AsgardeoNuxtConfig {
|
|
24
|
+
/** URL to redirect to after sign-in (default: '/') */
|
|
25
|
+
afterSignInUrl?: string;
|
|
26
|
+
/** URL to redirect to after sign-out (default: '/') */
|
|
27
|
+
afterSignOutUrl?: string;
|
|
20
28
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @default process.env.ASGARDEO_BASE_URL
|
|
29
|
+
* Asgardeo application id (`spId`) — appended to the redirect-based sign-up
|
|
30
|
+
* URL when present. Mirrors `applicationId` in the React/Next.js SDKs.
|
|
24
31
|
*/
|
|
25
|
-
|
|
32
|
+
applicationId?: string;
|
|
33
|
+
/** Base URL of the Asgardeo org tenant (e.g. https://api.asgardeo.io/t/your_org) */
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
/** OAuth2 Client ID */
|
|
36
|
+
clientId?: string;
|
|
37
|
+
/** OAuth2 Client Secret (server-only, use ASGARDEO_CLIENT_SECRET env var) */
|
|
38
|
+
clientSecret?: string;
|
|
26
39
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
40
|
+
* Feature-gating preferences that control which server-side data fetches
|
|
41
|
+
* the Nitro plugin performs on every SSR request.
|
|
29
42
|
*/
|
|
30
|
-
|
|
43
|
+
preferences?: {
|
|
44
|
+
/** i18n configuration forwarded to `I18nProvider`. */
|
|
45
|
+
i18n?: I18nPreferences;
|
|
46
|
+
theme?: {
|
|
47
|
+
/**
|
|
48
|
+
* When true (default), the Nitro plugin fetches the branding preference
|
|
49
|
+
* from Asgardeo and passes it to `BrandingProvider` / `ThemeProvider`.
|
|
50
|
+
*/
|
|
51
|
+
inheritFromBranding?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Theme mode forwarded to the Vue SDK's `ThemeProvider`.
|
|
54
|
+
* - `'light'` (default) | `'dark'`: Fixed color scheme. Toggle at runtime with `useTheme().toggleTheme()`.
|
|
55
|
+
* - `'system'`: Follows the OS `prefers-color-scheme`.
|
|
56
|
+
* - `'class'`: Reads a CSS class on `<html>` (works well with Tailwind dark-mode).
|
|
57
|
+
* - `'branding'`: Follows the active theme from the tenant's branding preference.
|
|
58
|
+
*/
|
|
59
|
+
mode?: 'light' | 'dark' | 'system' | 'class' | 'branding';
|
|
60
|
+
};
|
|
61
|
+
user?: {
|
|
62
|
+
/** Whether to fetch the user's organisations during SSR (default: true). */
|
|
63
|
+
fetchOrganizations?: boolean;
|
|
64
|
+
/** Whether to fetch the SCIM2 user profile during SSR (default: true). */
|
|
65
|
+
fetchUserProfile?: boolean;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
/** OAuth2 scopes to request */
|
|
69
|
+
scopes?: string[];
|
|
70
|
+
/** Secret for signing session JWTs (use ASGARDEO_SESSION_SECRET env var) */
|
|
71
|
+
sessionSecret?: string;
|
|
31
72
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
73
|
+
* Optional override for the redirect-based sign-in URL. Reserved for
|
|
74
|
+
* parity with the React/Next.js SDKs; not currently used by the redirect
|
|
75
|
+
* flow (which goes through `/api/auth/signin`).
|
|
34
76
|
*/
|
|
35
|
-
|
|
77
|
+
signInUrl?: string;
|
|
36
78
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
79
|
+
* Optional override for the redirect-based sign-up URL. When set,
|
|
80
|
+
* `<AsgardeoSignUpButton>` and `useAsgardeo().signUp()` (no-arg) navigate
|
|
81
|
+
* here instead of deriving the URL from `baseUrl`/`clientId`.
|
|
39
82
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
83
|
+
signUpUrl?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Payload stored in the session JWT cookie.
|
|
87
|
+
*/
|
|
88
|
+
export interface AsgardeoSessionPayload extends JWTPayload {
|
|
89
|
+
accessToken: string;
|
|
90
|
+
/** Unix timestamp (seconds) when the access token expires. Used for proactive refresh. */
|
|
91
|
+
accessTokenExpiresAt?: number;
|
|
92
|
+
exp: number;
|
|
93
|
+
iat: number;
|
|
94
|
+
/** Raw ID token string (for userinfo derivation without in-memory store). */
|
|
95
|
+
idToken?: string;
|
|
96
|
+
organizationId?: string;
|
|
97
|
+
/** Refresh token for obtaining new access tokens without re-authentication. */
|
|
98
|
+
refreshToken?: string;
|
|
99
|
+
scopes: string;
|
|
100
|
+
sessionId: string;
|
|
101
|
+
sub: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Payload stored in the temporary session JWT cookie (during OAuth flow).
|
|
105
|
+
*/
|
|
106
|
+
export interface AsgardeoTempSessionPayload extends JWTPayload {
|
|
107
|
+
/** URL to redirect to after successful sign-in */
|
|
108
|
+
returnTo?: string;
|
|
109
|
+
sessionId: string;
|
|
110
|
+
type: 'temp';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Full SSR payload resolved by the Nitro plugin on each page request.
|
|
114
|
+
* Written to `event.context.asgardeo.ssr` and subsequently seeded into
|
|
115
|
+
* hydrated `useState` keys so the client never re-fetches on first render.
|
|
116
|
+
*/
|
|
117
|
+
export interface AsgardeoSSRData {
|
|
118
|
+
/** Branding preference fetched from Asgardeo (null when `preferences.theme.inheritFromBranding` is false). */
|
|
119
|
+
brandingPreference: BrandingPreference | null;
|
|
120
|
+
/** The organisation the user is currently acting within (null when not in an org). */
|
|
121
|
+
currentOrganization: Organization | null;
|
|
122
|
+
isSignedIn: boolean;
|
|
123
|
+
/** All organisations the user is a member of (empty array when `preferences.user.fetchOrganizations` is false). */
|
|
124
|
+
myOrganizations: Organization[];
|
|
47
125
|
/**
|
|
48
|
-
* The
|
|
49
|
-
*
|
|
126
|
+
* The base URL actually used for this request.
|
|
127
|
+
* Equals `${baseUrl}/o` when the user is acting within an organisation
|
|
128
|
+
* (derived from the `user_org` claim in the ID token), otherwise equals
|
|
129
|
+
* the configured `baseUrl`.
|
|
50
130
|
*/
|
|
51
|
-
|
|
131
|
+
resolvedBaseUrl: string | null;
|
|
132
|
+
session: AsgardeoSessionPayload | null;
|
|
133
|
+
user: User | null;
|
|
134
|
+
/** Flattened SCIM2 profile + raw profile + schemas (null when `preferences.user.fetchUserProfile` is false). */
|
|
135
|
+
userProfile: UserProfile | null;
|
|
52
136
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
isAuthenticated: () => Promise<boolean>;
|
|
61
|
-
revokeAccessToken: () => Promise<void>;
|
|
62
|
-
signIn: (callbackUrl?: string) => Promise<void>;
|
|
63
|
-
signOut: () => Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Auth state hydrated from server to client via useState.
|
|
139
|
+
*/
|
|
140
|
+
export interface AsgardeoAuthState {
|
|
141
|
+
isLoading: boolean;
|
|
142
|
+
isSignedIn: boolean;
|
|
143
|
+
user: User | null;
|
|
64
144
|
}
|
|
65
|
-
export type SessionLastRefreshedAt = Date | undefined;
|
|
66
|
-
export type SessionStatus = 'authenticated' | 'unauthenticated' | 'loading';
|
|
67
|
-
export type { BasicUserInfo };
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
* Create a route matcher function from an array of glob-like patterns.
|
|
20
|
+
*
|
|
21
|
+
* Patterns support:
|
|
22
|
+
* - Literal paths: `/dashboard`
|
|
23
|
+
* - Wildcard segments: `/admin/*` matches `/admin/users` but not `/admin/users/1`
|
|
24
|
+
* - Deep wildcards: `/admin/**` matches `/admin/users` and `/admin/users/1`
|
|
25
|
+
* - Explicit regex groups: `/api/(users|posts)` stays as-is
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const isProtectedRoute = createRouteMatcher(['/dashboard/**', '/admin/**']);
|
|
30
|
+
* const isPublicRoute = createRouteMatcher(['/', '/about', '/sign-in']);
|
|
31
|
+
*
|
|
32
|
+
* // In a global middleware:
|
|
33
|
+
* export default defineNuxtRouteMiddleware((to) => {
|
|
34
|
+
* if (isProtectedRoute(to.path) && !authState.value?.isSignedIn) {
|
|
35
|
+
* return navigateTo('/api/auth/signin', { external: true });
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createRouteMatcher(patterns: string[]): (path: string) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function createRouteMatcher(patterns) {
|
|
2
|
+
const regexes = patterns.map((pattern) => {
|
|
3
|
+
const regexStr = pattern.replace(/[.+^${}|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLE_STAR___").replace(/\*/g, "[^/]*").replace(/___DOUBLE_STAR___/g, ".*");
|
|
4
|
+
return new RegExp(`^${regexStr}$`);
|
|
5
|
+
});
|
|
6
|
+
return (path) => regexes.some((regex) => regex.test(path));
|
|
7
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* @asgardeo/nuxt/utils
|
|
20
|
+
*
|
|
21
|
+
* Public utilities barrel. Import from this subpath to access
|
|
22
|
+
* utilities such as createRouteMatcher without polluting the
|
|
23
|
+
* global auto-import namespace.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { createRouteMatcher } from '@asgardeo/nuxt/utils';
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export { createRouteMatcher } from './createRouteMatcher.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createRouteMatcher } from "./createRouteMatcher.js";
|