From f7e70bd002e737cf29adacc9ddd7c3f546a8ba02 Mon Sep 17 00:00:00 2001 From: Brion Date: Fri, 11 Jul 2025 18:04:00 +0530 Subject: [PATCH 1/9] feat(nextjs): enhance Asgardeo integration with server-side middleware and improved exports --- packages/nextjs/esbuild.config.mjs | 6 +- packages/nextjs/package.json | 14 +++-- packages/nextjs/src/client/index.ts | 59 +++++++++++++++++ packages/nextjs/src/index.ts | 63 +------------------ packages/nextjs/src/server/asgardeo.ts | 27 ++++++++ packages/nextjs/src/server/index.ts | 28 +++++++++ .../middleware/asgardeoMiddleware.ts | 6 +- .../middleware/createRouteMatcher.ts | 0 packages/nextjs/tsconfig.lib.json | 2 +- pnpm-lock.yaml | 50 ++++++++++----- samples/teamspace-nextjs/app/layout.tsx | 2 +- samples/teamspace-nextjs/middleware.ts | 7 +-- 12 files changed, 170 insertions(+), 94 deletions(-) create mode 100644 packages/nextjs/src/client/index.ts create mode 100644 packages/nextjs/src/server/asgardeo.ts create mode 100644 packages/nextjs/src/server/index.ts rename packages/nextjs/src/{ => server}/middleware/asgardeoMiddleware.ts (97%) rename packages/nextjs/src/{ => server}/middleware/createRouteMatcher.ts (100%) diff --git a/packages/nextjs/esbuild.config.mjs b/packages/nextjs/esbuild.config.mjs index 29130762..b696504e 100644 --- a/packages/nextjs/esbuild.config.mjs +++ b/packages/nextjs/esbuild.config.mjs @@ -20,7 +20,7 @@ import {build} from 'esbuild'; const commonOptions = { bundle: false, - entryPoints: ['src/index.ts'], + entryPoints: ['src/index.ts', 'src/server/index.ts'], platform: 'node', target: ['node18'], }; @@ -28,13 +28,13 @@ const commonOptions = { await build({ ...commonOptions, format: 'esm', - outfile: 'dist/index.js', + outdir: 'dist/esm', sourcemap: true, }); await build({ ...commonOptions, format: 'cjs', - outfile: 'dist/cjs/index.js', + outdir: 'dist/cjs', sourcemap: true, }); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index b199495e..e031ea49 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -15,13 +15,19 @@ "author": "WSO2", "license": "Apache-2.0", "type": "module", - "main": "dist/index.js", - "module": "dist/index.js", + "main": "dist/esm/index.js", + "module": "dist/esm/index.js", "commonjs": "dist/cjs/index.js", "exports": { ".": { - "import": "./dist/index.js", + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js" + }, + "./server": { + "types": "./dist/types/server/index.d.ts", + "import": "./dist/esm/server/index.js", + "require": "./dist/cjs/server/index.js" } }, "files": [ @@ -36,7 +42,7 @@ "directory": "packages/next" }, "scripts": { - "build": "pnpm clean && node esbuild.config.mjs && tsc -p tsconfig.lib.json --outDir dist", + "build": "pnpm clean && node esbuild.config.mjs && tsc -p tsconfig.lib.json --outDir dist/esm", "clean": "rimraf dist", "fix:lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs", "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs", diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts new file mode 100644 index 00000000..20c82d72 --- /dev/null +++ b/packages/nextjs/src/client/index.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export {default as useAsgardeo} from './contexts/Asgardeo/useAsgardeo'; +export * from './contexts/Asgardeo/useAsgardeo'; + +export {default as CreateOrganization} from './components/presentation/CreateOrganization/CreateOrganization'; +export {CreateOrganizationProps} from './components/presentation/CreateOrganization/CreateOrganization'; + +export {default as OrganizationProfile} from './components/presentation/OrganizationProfile/OrganizationProfile'; +export {OrganizationProfileProps} from './components/presentation/OrganizationProfile/OrganizationProfile'; + +export {default as OrganizationSwitcher} from './components/presentation/OrganizationSwitcher/OrganizationSwitcher'; +export {OrganizationSwitcherProps} from './components/presentation/OrganizationSwitcher/OrganizationSwitcher'; + +export {default as SignedIn} from './components/control/SignedIn/SignedIn'; +export {SignedInProps} from './components/control/SignedIn/SignedIn'; + +export {default as SignedOut} from './components/control/SignedOut/SignedOut'; +export {SignedOutProps} from './components/control/SignedOut/SignedOut'; + +export {default as SignInButton} from './components/actions/SignInButton/SignInButton'; +export type {SignInButtonProps} from './components/actions/SignInButton/SignInButton'; + +export {default as SignUpButton} from './components/actions/SignUpButton/SignUpButton'; +export type {SignUpButtonProps} from './components/actions/SignUpButton/SignUpButton'; + +export {default as SignIn} from './components/presentation/SignIn/SignIn'; +export type {SignInProps} from './components/presentation/SignIn/SignIn'; + +export {default as SignOutButton} from './components/actions/SignOutButton/SignOutButton'; +export type {SignOutButtonProps} from './components/actions/SignOutButton/SignOutButton'; + +export {default as User} from './components/presentation/User/User'; +export type {UserProps} from './components/presentation/User/User'; + +export {default as SignUp} from './components/presentation/SignUp/SignUp'; +export type {SignUpProps} from './components/presentation/SignUp/SignUp'; + +export {default as UserDropdown} from './components/presentation/UserDropdown/UserDropdown'; +export type {UserDropdownProps} from './components/presentation/UserDropdown/UserDropdown'; + +export {default as UserProfile} from './components/presentation/UserProfile/UserProfile'; +export type {UserProfileProps} from './components/presentation/UserProfile/UserProfile'; diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 4c0f9bec..b6d37266 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -16,66 +16,7 @@ * under the License. */ -export {default as AsgardeoProvider} from './server/AsgardeoProvider'; -export * from './server/AsgardeoProvider'; - -export {default as useAsgardeo} from './client/contexts/Asgardeo/useAsgardeo'; -export * from './client/contexts/Asgardeo/useAsgardeo'; - -export {default as isSignedIn} from './server/actions/isSignedIn'; - -export {default as getSessionId} from './server/actions/getSessionId'; - -export {default as getSessionPayload} from './server/actions/getSessionPayload'; - -export {default as handleOAuthCallback} from './server/actions/handleOAuthCallbackAction'; - -export {default as SessionManager} from './utils/SessionManager'; -export * from './utils/SessionManager'; - -export {default as CreateOrganization} from './client/components/presentation/CreateOrganization/CreateOrganization'; -export {CreateOrganizationProps} from './client/components/presentation/CreateOrganization/CreateOrganization'; - -export {default as OrganizationProfile} from './client/components/presentation/OrganizationProfile/OrganizationProfile'; -export {OrganizationProfileProps} from './client/components/presentation/OrganizationProfile/OrganizationProfile'; - -export {default as OrganizationSwitcher} from './client/components/presentation/OrganizationSwitcher/OrganizationSwitcher'; -export {OrganizationSwitcherProps} from './client/components/presentation/OrganizationSwitcher/OrganizationSwitcher'; - -export {default as SignedIn} from './client/components/control/SignedIn/SignedIn'; -export {SignedInProps} from './client/components/control/SignedIn/SignedIn'; - -export {default as SignedOut} from './client/components/control/SignedOut/SignedOut'; -export {SignedOutProps} from './client/components/control/SignedOut/SignedOut'; - -export {default as SignInButton} from './client/components/actions/SignInButton/SignInButton'; -export type {SignInButtonProps} from './client/components/actions/SignInButton/SignInButton'; - -export {default as SignUpButton} from './client/components/actions/SignUpButton/SignUpButton'; -export type {SignUpButtonProps} from './client/components/actions/SignUpButton/SignUpButton'; - -export {default as SignIn} from './client/components/presentation/SignIn/SignIn'; -export type {SignInProps} from './client/components/presentation/SignIn/SignIn'; - -export {default as SignOutButton} from './client/components/actions/SignOutButton/SignOutButton'; -export type {SignOutButtonProps} from './client/components/actions/SignOutButton/SignOutButton'; - -export {default as User} from './client/components/presentation/User/User'; -export type {UserProps} from './client/components/presentation/User/User'; - -export {default as SignUp} from './client/components/presentation/SignUp/SignUp'; -export type {SignUpProps} from './client/components/presentation/SignUp/SignUp'; - -export {default as UserDropdown} from './client/components/presentation/UserDropdown/UserDropdown'; -export type {UserDropdownProps} from './client/components/presentation/UserDropdown/UserDropdown'; - -export {default as UserProfile} from './client/components/presentation/UserProfile/UserProfile'; -export type {UserProfileProps} from './client/components/presentation/UserProfile/UserProfile'; - export {default as AsgardeoNext} from './AsgardeoNextClient'; -export {default as asgardeoMiddleware} from './middleware/asgardeoMiddleware'; -export * from './middleware/asgardeoMiddleware'; - -export {default as createRouteMatcher} from './middleware/createRouteMatcher'; -export * from './middleware/createRouteMatcher'; +// @asgardeo/nextjs exports. +export * from './client'; diff --git a/packages/nextjs/src/server/asgardeo.ts b/packages/nextjs/src/server/asgardeo.ts new file mode 100644 index 00000000..a919cd39 --- /dev/null +++ b/packages/nextjs/src/server/asgardeo.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import AsgardeoNextClient from '../AsgardeoNextClient'; + +/** + * Server-only singleton for API access. + * Usage: + * import { asgardeo } from '@asgardeo/nextjs/server'; + * const token = await asgardeo.getAccessToken(); + */ +export const asgardeo = AsgardeoNextClient.getInstance(); diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts new file mode 100644 index 00000000..82f0587a --- /dev/null +++ b/packages/nextjs/src/server/index.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// @asgardeo/nextjs/server exports. + +export {default as AsgardeoProvider} from './AsgardeoProvider'; +export * from './AsgardeoProvider'; + +export {default as asgardeoMiddleware} from './middleware/asgardeoMiddleware'; +export * from './middleware/asgardeoMiddleware'; + +export {default as createRouteMatcher} from './middleware/createRouteMatcher'; +export * from './middleware/createRouteMatcher'; diff --git a/packages/nextjs/src/middleware/asgardeoMiddleware.ts b/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts similarity index 97% rename from packages/nextjs/src/middleware/asgardeoMiddleware.ts rename to packages/nextjs/src/server/middleware/asgardeoMiddleware.ts index b20955d6..c30d7ede 100644 --- a/packages/nextjs/src/middleware/asgardeoMiddleware.ts +++ b/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts @@ -18,13 +18,13 @@ import {NextRequest, NextResponse} from 'next/server'; import {CookieConfig} from '@asgardeo/node'; -import {AsgardeoNextConfig} from '../models/config'; -import SessionManager, {SessionTokenPayload} from '../utils/SessionManager'; +import {AsgardeoNextConfig} from '../../models/config'; +import SessionManager, {SessionTokenPayload} from '../../utils/SessionManager'; import { hasValidSession as hasValidJWTSession, getSessionFromRequest, getSessionIdFromRequest, -} from '../utils/sessionUtils'; +} from '../../utils/sessionUtils'; export type AsgardeoMiddlewareOptions = Partial; diff --git a/packages/nextjs/src/middleware/createRouteMatcher.ts b/packages/nextjs/src/server/middleware/createRouteMatcher.ts similarity index 100% rename from packages/nextjs/src/middleware/createRouteMatcher.ts rename to packages/nextjs/src/server/middleware/createRouteMatcher.ts diff --git a/packages/nextjs/tsconfig.lib.json b/packages/nextjs/tsconfig.lib.json index 8d37e00d..67e6a70e 100644 --- a/packages/nextjs/tsconfig.lib.json +++ b/packages/nextjs/tsconfig.lib.json @@ -3,7 +3,7 @@ "compilerOptions": { "declaration": true, "outDir": "dist", - "declarationDir": "dist", + "declarationDir": "dist/types", "types": ["node"] }, "exclude": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 051ebeae..ac696b81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -245,7 +245,7 @@ importers: version: 8.57.0 next: specifier: ^15.3.2 - version: 15.3.2(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.0) + version: 15.3.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.0) prettier: specifier: ^2.6.2 version: 2.8.8 @@ -8774,14 +8774,14 @@ snapshots: '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 - eslint-config-airbnb: 19.0.4(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0) - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-config-airbnb-typescript: 17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb: 19.0.4(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb-typescript: 17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-plugin-eslint-plugin: 5.5.1(eslint@8.57.0) eslint-plugin-header: 3.1.1(eslint@8.57.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) - eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.1.6) + eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-node: 11.1.0(eslint@8.57.0) eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) @@ -9897,6 +9897,15 @@ snapshots: escape-string-regexp@4.0.0: {} + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0): + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.57.0 + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) + object.assign: 4.1.7 + object.entries: 1.1.9 + semver: 6.3.1 + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: confusing-browser-globals: 1.0.11 @@ -9906,13 +9915,13 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0): + eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0): dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: @@ -9922,6 +9931,17 @@ snapshots: eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0) + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) + eslint-plugin-react: 7.37.5(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) + object.assign: 4.1.7 + object.entries: 1.1.9 + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 @@ -10039,12 +10059,12 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.1.6): + eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.7.3))(eslint@8.57.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) transitivePeerDependencies: - supports-color - typescript @@ -11479,7 +11499,7 @@ snapshots: negotiator@1.0.0: {} - next@15.3.2(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.0): + next@15.3.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.0): dependencies: '@next/env': 15.3.2 '@swc/counter': 0.1.3 @@ -11489,7 +11509,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.2 '@next/swc-darwin-x64': 15.3.2 @@ -12738,12 +12758,10 @@ snapshots: style-search@0.1.0: {} - styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.0): + styled-jsx@5.1.6(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 - optionalDependencies: - '@babel/core': 7.27.1 stylehacks@5.1.1(postcss@8.5.3): dependencies: diff --git a/samples/teamspace-nextjs/app/layout.tsx b/samples/teamspace-nextjs/app/layout.tsx index 30ecd274..cd3699d9 100644 --- a/samples/teamspace-nextjs/app/layout.tsx +++ b/samples/teamspace-nextjs/app/layout.tsx @@ -3,7 +3,7 @@ import type {Metadata} from 'next'; import {Inter} from 'next/font/google'; import './globals.css'; import {ThemeProvider} from '@/components/ThemeProvider'; -import {AsgardeoProvider} from '@asgardeo/nextjs'; +import {AsgardeoProvider} from '@asgardeo/nextjs/server'; const inter = Inter({subsets: ['latin']}); diff --git a/samples/teamspace-nextjs/middleware.ts b/samples/teamspace-nextjs/middleware.ts index 88ea6e34..8f701ca4 100644 --- a/samples/teamspace-nextjs/middleware.ts +++ b/samples/teamspace-nextjs/middleware.ts @@ -1,9 +1,6 @@ -import {asgardeoMiddleware, createRouteMatcher} from '@asgardeo/nextjs'; +import {asgardeoMiddleware, createRouteMatcher} from '@asgardeo/nextjs/server'; -const isProtectedRoute = createRouteMatcher([ - '/dashboard', - '/dashboard/(.*)', -]); +const isProtectedRoute = createRouteMatcher(['/dashboard', '/dashboard/(.*)']); export default asgardeoMiddleware(async (asgardeo, req) => { if (isProtectedRoute(req)) { From 2bbaef3a43e4271be70c8ea6a2216a2f4eb5e923 Mon Sep 17 00:00:00 2001 From: Brion Date: Fri, 11 Jul 2025 18:05:19 +0530 Subject: [PATCH 2/9] chore(nextjs): remove legacy session handling and update session cookie management --- .../src/server/actions/deleteSessionId.ts | 31 ------------ .../nextjs/src/server/actions/setSessionId.ts | 50 ------------------- .../nextjs/src/server/actions/signInAction.ts | 9 +--- .../src/server/actions/signOutAction.ts | 2 - .../server/middleware/asgardeoMiddleware.ts | 17 +------ packages/nextjs/src/utils/SessionManager.ts | 14 +++--- packages/nextjs/src/utils/sessionUtils.ts | 21 ++------ packages/node/src/constants/CookieConfig.ts | 6 ++- 8 files changed, 16 insertions(+), 134 deletions(-) delete mode 100644 packages/nextjs/src/server/actions/deleteSessionId.ts delete mode 100644 packages/nextjs/src/server/actions/setSessionId.ts diff --git a/packages/nextjs/src/server/actions/deleteSessionId.ts b/packages/nextjs/src/server/actions/deleteSessionId.ts deleted file mode 100644 index d6e56b82..00000000 --- a/packages/nextjs/src/server/actions/deleteSessionId.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -'use server'; - -import {CookieConfig} from '@asgardeo/node'; -import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies'; -import {cookies} from 'next/headers'; - -const deleteSessionId = async (): Promise => { - const cookieStore: ReadonlyRequestCookies = await cookies(); - - await cookieStore.delete(CookieConfig.SESSION_COOKIE_NAME); -}; - -export default deleteSessionId; diff --git a/packages/nextjs/src/server/actions/setSessionId.ts b/packages/nextjs/src/server/actions/setSessionId.ts deleted file mode 100644 index 5d21cefd..00000000 --- a/packages/nextjs/src/server/actions/setSessionId.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -'use server'; - -import {CookieConfig, generateSessionId} from '@asgardeo/node'; -import {RequestCookie} from 'next/dist/compiled/@edge-runtime/cookies'; -import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies'; -import {cookies} from 'next/headers'; - -const setSessionId = async (sessionId?: string): Promise => { - const cookieStore: ReadonlyRequestCookies = await cookies(); - - const sessionCookie: RequestCookie | undefined = cookieStore.get(CookieConfig.SESSION_COOKIE_NAME); - let sessionCookieValue: string | undefined = sessionId; - - if (!sessionCookieValue && sessionCookie) { - sessionCookieValue = sessionCookie.value; - } else if (sessionCookie) { - sessionCookieValue = sessionCookie.value; - } else { - sessionCookieValue = generateSessionId(); - } - - cookieStore.set(CookieConfig.SESSION_COOKIE_NAME, sessionCookieValue as string, { - httpOnly: CookieConfig?.DEFAULT_HTTP_ONLY, - maxAge: CookieConfig?.DEFAULT_MAX_AGE, - sameSite: CookieConfig?.DEFAULT_SAME_SITE, - secure: CookieConfig?.DEFAULT_SECURE, - }); - - return Promise.resolve(sessionCookieValue); -}; - -export default setSessionId; diff --git a/packages/nextjs/src/server/actions/signInAction.ts b/packages/nextjs/src/server/actions/signInAction.ts index 17a61dfa..ed4c9b4b 100644 --- a/packages/nextjs/src/server/actions/signInAction.ts +++ b/packages/nextjs/src/server/actions/signInAction.ts @@ -93,13 +93,6 @@ const signInAction = async ( tempSessionToken, SessionManager.getTempSessionCookieOptions(), ); - - cookieStore.set(CookieConfig.SESSION_COOKIE_NAME, sessionId, { - httpOnly: CookieConfig.DEFAULT_HTTP_ONLY, - maxAge: CookieConfig.DEFAULT_MAX_AGE, - sameSite: CookieConfig.DEFAULT_SAME_SITE, - secure: CookieConfig.DEFAULT_SECURE, - }); } // If no payload provided, redirect to sign-in URL for redirect-based sign-in. @@ -130,7 +123,7 @@ const signInAction = async ( const sessionToken = await SessionManager.createSessionToken( userIdFromToken, - sessionId, + sessionId as string, scopes, organizationId, ); diff --git a/packages/nextjs/src/server/actions/signOutAction.ts b/packages/nextjs/src/server/actions/signOutAction.ts index 1d319a4c..14760779 100644 --- a/packages/nextjs/src/server/actions/signOutAction.ts +++ b/packages/nextjs/src/server/actions/signOutAction.ts @@ -46,8 +46,6 @@ const signOutAction = async (): Promise<{success: boolean; data?: {afterSignOutU cookieStore.delete(SessionManager.getTempSessionCookieName()); - await import('./deleteSessionId').then(module => module.default()); - return {success: true, data: {afterSignOutUrl}}; } catch (error) { return {success: false, error}; diff --git a/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts b/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts index c30d7ede..da6d91fd 100644 --- a/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts +++ b/packages/nextjs/src/server/middleware/asgardeoMiddleware.ts @@ -52,19 +52,6 @@ type AsgardeoMiddlewareHandler = ( req: NextRequest, ) => Promise | NextResponse | void; -/** - * Legacy function: Checks if a request has a valid session ID in cookies. - * This is a lightweight check that can be used in middleware. - * - * @deprecated Use hasValidJWTSession for JWT-based sessions - * @param request - The Next.js request object - * @returns True if a session ID exists, false otherwise - */ -const hasValidSessionLegacy = (request: NextRequest): boolean => { - const sessionId = request.cookies.get(CookieConfig.SESSION_COOKIE_NAME)?.value; - return Boolean(sessionId && sessionId.trim().length > 0); -}; - /** * Enhanced session validation that checks both JWT and legacy sessions * @@ -73,11 +60,9 @@ const hasValidSessionLegacy = (request: NextRequest): boolean => { */ const hasValidSession = async (request: NextRequest): Promise => { try { - // Try JWT session first return await hasValidJWTSession(request); } catch { - // Fall back to legacy session check - return hasValidSessionLegacy(request); + return Promise.resolve(false); } }; diff --git a/packages/nextjs/src/utils/SessionManager.ts b/packages/nextjs/src/utils/SessionManager.ts index d9909ff2..82edb862 100644 --- a/packages/nextjs/src/utils/SessionManager.ts +++ b/packages/nextjs/src/utils/SessionManager.ts @@ -17,7 +17,7 @@ */ import {SignJWT, jwtVerify, JWTPayload} from 'jose'; -import {AsgardeoRuntimeError} from '@asgardeo/node'; +import {AsgardeoRuntimeError, CookieConfig} from '@asgardeo/node'; /** * Session token payload interface @@ -41,9 +41,7 @@ export interface SessionTokenPayload extends JWTPayload { * Session management utility class for JWT-based session cookies */ class SessionManager { - private static readonly SESSION_COOKIE_NAME = 'asgardeo_session'; - private static readonly TEMP_SESSION_COOKIE_NAME = 'asgardeo_temp_session'; - private static readonly DEFAULT_EXPIRY_SECONDS = 3600; // 1 hour + private static readonly DEFAULT_EXPIRY_SECONDS = 3600; /** * Get the signing secret from environment variable @@ -81,7 +79,7 @@ class SessionManager { }) .setProtectedHeader({alg: 'HS256'}) .setIssuedAt() - .setExpirationTime('15m') // Temporary sessions expire in 15 minutes + .setExpirationTime('15m') .sign(secret); return jwt; @@ -178,7 +176,7 @@ class SessionManager { secure: process.env['NODE_ENV'] === 'production', sameSite: 'lax' as const, path: '/', - maxAge: 15 * 60, // 15 minutes + maxAge: 15 * 60, }; } @@ -186,14 +184,14 @@ class SessionManager { * Get session cookie name */ static getSessionCookieName(): string { - return this.SESSION_COOKIE_NAME; + return CookieConfig.SESSION_COOKIE_NAME; } /** * Get temporary session cookie name */ static getTempSessionCookieName(): string { - return this.TEMP_SESSION_COOKIE_NAME; + return CookieConfig.TEMP_SESSION_COOKIE_NAME; } } diff --git a/packages/nextjs/src/utils/sessionUtils.ts b/packages/nextjs/src/utils/sessionUtils.ts index f6181309..d4e93bf2 100644 --- a/packages/nextjs/src/utils/sessionUtils.ts +++ b/packages/nextjs/src/utils/sessionUtils.ts @@ -70,17 +70,15 @@ export const getSessionFromRequest = async (request: NextRequest): Promise => { try { - // Try JWT session first const sessionPayload = await getSessionFromRequest(request); + if (sessionPayload) { return sessionPayload.sessionId; } - // Fall back to legacy session ID cookie for backward compatibility - return request.cookies.get(CookieConfig.SESSION_COOKIE_NAME)?.value; + return Promise.resolve(undefined); } catch { - // Fall back to legacy session ID cookie - return request.cookies.get(CookieConfig.SESSION_COOKIE_NAME)?.value; + return Promise.resolve(undefined); } }; @@ -103,16 +101,3 @@ export const getTempSessionFromRequest = async (request: NextRequest): Promise { - const sessionId = request.cookies.get(CookieConfig.SESSION_COOKIE_NAME)?.value; - return Boolean(sessionId && sessionId.trim().length > 0); -}; diff --git a/packages/node/src/constants/CookieConfig.ts b/packages/node/src/constants/CookieConfig.ts index 56be52ed..d8ed91fe 100644 --- a/packages/node/src/constants/CookieConfig.ts +++ b/packages/node/src/constants/CookieConfig.ts @@ -16,8 +16,12 @@ * under the License. */ +import {VendorConstants} from '@asgardeo/javascript'; + class CookieConfig { - static readonly SESSION_COOKIE_NAME: string = 'ASGARDEO_SESSION_ID'; + static readonly SESSION_COOKIE_NAME: string = `__${VendorConstants.VENDOR_PREFIX}__session`; + + static readonly TEMP_SESSION_COOKIE_NAME: string = `__${VendorConstants.VENDOR_PREFIX}__temp.session`; static readonly DEFAULT_MAX_AGE: number = 3600; From 3b3599abe600ebbb8f1ae8b22408959ddb117354 Mon Sep 17 00:00:00 2001 From: Brion Date: Sat, 12 Jul 2025 02:40:20 +0530 Subject: [PATCH 3/9] fix(nextjs): fix logout related issues when the initialization fails --- .../actions/SignOutButton/SignOutButton.tsx | 27 +++++++---------- .../contexts/Asgardeo/AsgardeoProvider.tsx | 16 ++++++++-- .../src/server/actions/signOutAction.ts | 29 +++++++++++++++---- packages/nextjs/src/utils/logger.ts | 25 ++++++++++++++++ 4 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 packages/nextjs/src/utils/logger.ts diff --git a/packages/nextjs/src/client/components/actions/SignOutButton/SignOutButton.tsx b/packages/nextjs/src/client/components/actions/SignOutButton/SignOutButton.tsx index 7d85d835..4c903f44 100644 --- a/packages/nextjs/src/client/components/actions/SignOutButton/SignOutButton.tsx +++ b/packages/nextjs/src/client/components/actions/SignOutButton/SignOutButton.tsx @@ -18,10 +18,10 @@ 'use client'; -import {FC, forwardRef, PropsWithChildren, ReactElement, Ref, useState, MouseEvent} from 'react'; +import {FC, forwardRef, ReactElement, Ref, useState, MouseEvent} from 'react'; import {BaseSignOutButton, BaseSignOutButtonProps, useTranslation} from '@asgardeo/react'; -import {AsgardeoRuntimeError} from '@asgardeo/node'; import useAsgardeo from '../../../../client/contexts/Asgardeo/useAsgardeo'; +import logger from '../../../../utils/logger'; /** * Interface for SignInButton component props. @@ -46,7 +46,10 @@ export type SignOutButtonProps = BaseSignOutButtonProps; * ``` */ const SignOutButton = forwardRef( - ({className, style, preferences, onClick, children, ...rest}: SignOutButtonProps, ref: Ref): ReactElement => { + ( + {className, style, preferences, onClick, children, ...rest}: SignOutButtonProps, + ref: Ref, + ): ReactElement => { const {signOut} = useAsgardeo(); const {t} = useTranslation(preferences?.i18n); @@ -55,31 +58,23 @@ const SignOutButton = forwardRef( const handleOnClick = async (e: MouseEvent): Promise => { try { setIsLoading(true); + + logger.debug('[SignOutButton] Initiating a sign-out from a button click'); + await signOut(); if (onClick) { onClick(e); } } catch (error) { - throw new AsgardeoRuntimeError( - `Sign out failed: ${error instanceof Error ? error.message : String(error)}`, - 'SignOutButton-handleOnClick-RuntimeError-001', - 'next', - 'Something went wrong while trying to sign out. Please try again later.', - ); + logger.error('[SignOutButton] Error occurred initiating sign-out from a button click:', error); } finally { setIsLoading(false); } }; return ( - + {children ?? t('elements.buttons.signOut')} ); diff --git a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx index 7666dcae..a99e03a2 100644 --- a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx @@ -44,6 +44,7 @@ import { import {useRouter, useSearchParams} from 'next/navigation'; import {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react'; import AsgardeoContext, {AsgardeoContextProps} from './AsgardeoContext'; +import logger from '../../../utils/logger'; /** * Props interface of {@link AsgardeoClientProvider} @@ -248,21 +249,32 @@ const AsgardeoClientProvider: FC> }; const handleSignOut = async () => { + logger.debug('[AsgardeoClientProvider][handleSignOut] `handleSignOut` called.'); + try { const result = await signOut(); + logger.debug('[AsgardeoClientProvider][handleSignOut] Sign out result:', result); + if (result?.data?.afterSignOutUrl) { router.push(result.data.afterSignOutUrl); + return {redirected: true, location: result.data.afterSignOutUrl}; } if (result?.error) { - throw new Error(result.error); + logger.error( + '[AsgardeoClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:', + result.error, + ); } return result?.data ?? result; } catch (error) { - throw error; + logger.error( + '[AsgardeoClientProvider][handleSignOut] Error occurred during signing the user out with a button click:', + error, + ); } }; diff --git a/packages/nextjs/src/server/actions/signOutAction.ts b/packages/nextjs/src/server/actions/signOutAction.ts index 14760779..2103b6a2 100644 --- a/packages/nextjs/src/server/actions/signOutAction.ts +++ b/packages/nextjs/src/server/actions/signOutAction.ts @@ -22,6 +22,7 @@ import {cookies} from 'next/headers'; import AsgardeoNextClient from '../../AsgardeoNextClient'; import SessionManager from '../../utils/SessionManager'; import getSessionId from './getSessionId'; +import logger from '../../utils/logger'; /** * Server action for signing out a user. @@ -30,6 +31,15 @@ import getSessionId from './getSessionId'; * @returns Promise that resolves with success status and optional after sign-out URL */ const signOutAction = async (): Promise<{success: boolean; data?: {afterSignOutUrl?: string}; error?: unknown}> => { + logger.debug('[signOutAction] Initiating sign out process from the server action.'); + + const clearSessionCookies = async () => { + const cookieStore = await cookies(); + + cookieStore.delete(SessionManager.getSessionCookieName()); + cookieStore.delete(SessionManager.getTempSessionCookieName()); + }; + try { const client = AsgardeoNextClient.getInstance(); const sessionId = await getSessionId(); @@ -37,18 +47,25 @@ const signOutAction = async (): Promise<{success: boolean; data?: {afterSignOutU let afterSignOutUrl: string = '/'; if (sessionId) { + logger.debug('[signOutAction] Session ID found, invoking the `signOut` to obtain the `afterSignOutUrl`.'); + afterSignOutUrl = await client.signOut({}, sessionId); } - const cookieStore = await cookies(); - - cookieStore.delete(SessionManager.getSessionCookieName()); - - cookieStore.delete(SessionManager.getTempSessionCookieName()); + await clearSessionCookies(); return {success: true, data: {afterSignOutUrl}}; } catch (error) { - return {success: false, error}; + logger.error('[signOutAction] Error during sign out from the server action:', error); + + logger.debug('[signOutAction] Clearing session cookies due to error as a fallback.'); + + await clearSessionCookies(); + + return { + success: false, + error: typeof error === 'string' ? error : error instanceof Error ? error.message : JSON.stringify(error), + }; } }; diff --git a/packages/nextjs/src/utils/logger.ts b/packages/nextjs/src/utils/logger.ts new file mode 100644 index 00000000..22cc01ae --- /dev/null +++ b/packages/nextjs/src/utils/logger.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {createLogger} from '@asgardeo/node'; + +const logger: any = createLogger({ + level: 'error', +}); + +export default logger; From e81c36f1e83c506f78ee87ad17079d8797558a45 Mon Sep 17 00:00:00 2001 From: Brion Date: Sat, 12 Jul 2025 02:43:29 +0530 Subject: [PATCH 4/9] chore(nextjs): remove unused Teamspace branding from sign-in and sign-up pages --- samples/teamspace-nextjs/app/signin/page.tsx | 10 ---------- samples/teamspace-nextjs/app/signup/page.tsx | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/samples/teamspace-nextjs/app/signin/page.tsx b/samples/teamspace-nextjs/app/signin/page.tsx index e92522e0..37e47369 100644 --- a/samples/teamspace-nextjs/app/signin/page.tsx +++ b/samples/teamspace-nextjs/app/signin/page.tsx @@ -10,17 +10,7 @@ export default function SignInPage() { return (
-
- -
- -
- Teamspace - -
- -
← Back to home diff --git a/samples/teamspace-nextjs/app/signup/page.tsx b/samples/teamspace-nextjs/app/signup/page.tsx index a4327e0a..a83ea750 100644 --- a/samples/teamspace-nextjs/app/signup/page.tsx +++ b/samples/teamspace-nextjs/app/signup/page.tsx @@ -10,17 +10,7 @@ export default function SignUpPage() { return (
-
- -
- -
- Teamspace - -
- -
← Back to home From 756c98add10f543ebac7ea40a274f0b32b9719eb Mon Sep 17 00:00:00 2001 From: Brion Date: Sun, 13 Jul 2025 22:57:48 +0530 Subject: [PATCH 5/9] chore(nextjs): update Asgardeo client initialization to accept optional storage parameter and add logging for initialization success and failure --- .../src/AsgardeoJavaScriptClient.ts | 3 +- packages/javascript/src/models/client.ts | 4 ++- packages/nextjs/src/AsgardeoNextClient.ts | 30 +++++++++++-------- .../nextjs/src/server/AsgardeoProvider.tsx | 6 ++++ packages/react/src/AsgardeoReactClient.ts | 3 +- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/javascript/src/AsgardeoJavaScriptClient.ts b/packages/javascript/src/AsgardeoJavaScriptClient.ts index 50d1013e..0b185e26 100644 --- a/packages/javascript/src/AsgardeoJavaScriptClient.ts +++ b/packages/javascript/src/AsgardeoJavaScriptClient.ts @@ -19,6 +19,7 @@ import {AllOrganizationsApiResponse} from './models/organization'; import {AsgardeoClient, SignInOptions, SignOutOptions, SignUpOptions} from './models/client'; import {Config} from './models/config'; +import {Storage} from './models/store'; import {EmbeddedFlowExecuteRequestPayload, EmbeddedFlowExecuteResponse} from './models/embedded-flow'; import {EmbeddedSignInFlowHandleRequestPayload} from './models/embedded-signin-flow'; import {TokenResponse} from './models/token'; @@ -34,7 +35,7 @@ import {User, UserProfile} from './models/user'; abstract class AsgardeoJavaScriptClient implements AsgardeoClient { abstract switchOrganization(organization: Organization, sessionId?: string): Promise; - abstract initialize(config: T): Promise; + abstract initialize(config: T, storage?: Storage): Promise; abstract getUser(options?: any): Promise; diff --git a/packages/javascript/src/models/client.ts b/packages/javascript/src/models/client.ts index a6c28c06..d67334fb 100644 --- a/packages/javascript/src/models/client.ts +++ b/packages/javascript/src/models/client.ts @@ -26,6 +26,7 @@ import {EmbeddedSignInFlowHandleRequestPayload} from './embedded-signin-flow'; import {Organization} from './organization'; import {User, UserProfile} from './user'; import {TokenResponse} from './token'; +import {Storage} from './store'; export type SignInOptions = Record; export type SignOutOptions = Record; @@ -87,9 +88,10 @@ export interface AsgardeoClient { * Initializes the authentication client with provided configuration. * * @param config - SDK Client instance configuration options. + * @param storage - Optional storage instance to persist data (e.g., session, user profile). * @returns Promise resolving to boolean indicating success. */ - initialize(config: T): Promise; + initialize(config: T, storage?: Storage): Promise; /** * Checks if the client is currently loading. diff --git a/packages/nextjs/src/AsgardeoNextClient.ts b/packages/nextjs/src/AsgardeoNextClient.ts index 0e2224b8..099b9aec 100644 --- a/packages/nextjs/src/AsgardeoNextClient.ts +++ b/packages/nextjs/src/AsgardeoNextClient.ts @@ -51,6 +51,7 @@ import { AllOrganizationsApiResponse, extractUserClaimsFromIdToken, TokenResponse, + Storage, } from '@asgardeo/node'; import {AsgardeoNextConfig} from './models/config'; import getSessionId from './server/actions/getSessionId'; @@ -99,7 +100,7 @@ class AsgardeoNextClient exte } } - override async initialize(config: T): Promise { + override async initialize(config: T, storage?: Storage): Promise { if (this.isInitialized) { return Promise.resolve(true); } @@ -126,18 +127,21 @@ class AsgardeoNextClient exte const origin: string = await getClientOrigin(); - return this.asgardeo.initialize({ - organizationHandle: resolvedOrganizationHandle, - baseUrl, - clientId, - clientSecret, - signInUrl, - signUpUrl, - afterSignInUrl: afterSignInUrl ?? origin, - afterSignOutUrl: afterSignOutUrl ?? origin, - enablePKCE: false, - ...rest, - } as any); + return this.asgardeo.initialize( + { + organizationHandle: resolvedOrganizationHandle, + baseUrl, + clientId, + clientSecret, + signInUrl, + signUpUrl, + afterSignInUrl: afterSignInUrl ?? origin, + afterSignOutUrl: afterSignOutUrl ?? origin, + enablePKCE: false, + ...rest, + } as any, + storage, + ); } override async getUser(userId?: string): Promise { diff --git a/packages/nextjs/src/server/AsgardeoProvider.tsx b/packages/nextjs/src/server/AsgardeoProvider.tsx index 9d5235ec..b053fa72 100644 --- a/packages/nextjs/src/server/AsgardeoProvider.tsx +++ b/packages/nextjs/src/server/AsgardeoProvider.tsx @@ -40,6 +40,7 @@ import updateUserProfileAction from './actions/updateUserProfileAction'; import AsgardeoNextClient from '../AsgardeoNextClient'; import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider'; import {AsgardeoNextConfig} from '../models/config'; +import logger from '../utils/logger'; /** * Props interface of {@link AsgardeoServerProvider} @@ -75,8 +76,13 @@ const AsgardeoServerProvider: FC> try { await asgardeoClient.initialize(_config as AsgardeoNextConfig); + + logger.debug('[AsgardeoServerProvider] Asgardeo client initialized successfully.'); + config = await asgardeoClient.getConfiguration(); } catch (error) { + logger.error('[AsgardeoServerProvider] Failed to initialize Asgardeo client:', error?.toString()); + throw new AsgardeoRuntimeError( `Failed to initialize Asgardeo client: ${error?.toString()}`, 'next-ConfigurationError-001', diff --git a/packages/react/src/AsgardeoReactClient.ts b/packages/react/src/AsgardeoReactClient.ts index f605d968..28f2d342 100644 --- a/packages/react/src/AsgardeoReactClient.ts +++ b/packages/react/src/AsgardeoReactClient.ts @@ -41,6 +41,7 @@ import { TokenResponse, HttpRequestConfig, HttpResponse, + Storage, } from '@asgardeo/browser'; import AuthAPI from './__temp__/api'; import getMeOrganizations from './api/getMeOrganizations'; @@ -89,7 +90,7 @@ class AsgardeoReactClient e } } - override initialize(config: AsgardeoReactConfig): Promise { + override initialize(config: AsgardeoReactConfig, storage?: Storage): Promise { let resolvedOrganizationHandle: string | undefined = config?.organizationHandle; if (!resolvedOrganizationHandle) { From c99a2c8209993118b4f98c028e937bed925454bb Mon Sep 17 00:00:00 2001 From: Brion Date: Sun, 13 Jul 2025 23:03:31 +0530 Subject: [PATCH 6/9] chore(logger): replace enum with string literals for log levels --- packages/javascript/src/utils/logger.ts | 60 +++++++++++-------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/packages/javascript/src/utils/logger.ts b/packages/javascript/src/utils/logger.ts index e77393d1..c0c0b8d5 100644 --- a/packages/javascript/src/utils/logger.ts +++ b/packages/javascript/src/utils/logger.ts @@ -19,13 +19,7 @@ /** * Log levels enum */ -export enum LogLevel { - DEBUG = 0, - INFO = 1, - WARN = 2, - ERROR = 3, - SILENT = 4, -} +export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; /** * Logger configuration interface @@ -49,7 +43,7 @@ const PREFIX: string = '🛡️ Asgardeo'; * Default logger configuration */ const DEFAULT_CONFIG: LoggerConfig = { - level: LogLevel.INFO, + level: 'info', prefix: `${PREFIX}`, timestamps: true, showLevel: true, @@ -140,13 +134,13 @@ class Logger { */ private getLevelString(level: LogLevel): string { switch (level) { - case LogLevel.DEBUG: + case 'debug': return 'DEBUG'; - case LogLevel.INFO: + case 'info': return 'INFO'; - case LogLevel.WARN: + case 'warn': return 'WARN'; - case LogLevel.ERROR: + case 'error': return 'ERROR'; default: return 'UNKNOWN'; @@ -175,16 +169,16 @@ class Logger { let coloredLevel: string; switch (level) { - case LogLevel.DEBUG: + case 'debug': coloredLevel = `${COLORS.gray}[${levelStr}]${COLORS.reset}`; break; - case LogLevel.INFO: + case 'info': coloredLevel = `${COLORS.blue}[${levelStr}]${COLORS.reset}`; break; - case LogLevel.WARN: + case 'warn': coloredLevel = `${COLORS.yellow}[${levelStr}]${COLORS.reset}`; break; - case LogLevel.ERROR: + case 'error': coloredLevel = `${COLORS.red}[${levelStr}]${COLORS.reset}`; break; default: @@ -248,16 +242,16 @@ class Logger { parts.push(`%c[${levelStr}]`); switch (level) { - case LogLevel.DEBUG: + case 'debug': styles.push(BROWSER_STYLES.debug); break; - case LogLevel.INFO: + case 'info': styles.push(BROWSER_STYLES.info); break; - case LogLevel.WARN: + case 'warn': styles.push(BROWSER_STYLES.warn); break; - case LogLevel.ERROR: + case 'error': styles.push(BROWSER_STYLES.error); break; default: @@ -272,16 +266,16 @@ class Logger { // Use appropriate console method switch (level) { - case LogLevel.DEBUG: + case 'debug': console.debug(formattedMessage, ...styles, ...args); break; - case LogLevel.INFO: + case 'info': console.info(formattedMessage, ...styles, ...args); break; - case LogLevel.WARN: + case 'warn': console.warn(formattedMessage, ...styles, ...args); break; - case LogLevel.ERROR: + case 'error': console.error(formattedMessage, ...styles, ...args); break; default: @@ -297,16 +291,16 @@ class Logger { // Use appropriate console method switch (level) { - case LogLevel.DEBUG: + case 'debug': console.debug(formattedMessage, ...args); break; - case LogLevel.INFO: + case 'info': console.info(formattedMessage, ...args); break; - case LogLevel.WARN: + case 'warn': console.warn(formattedMessage, ...args); break; - case LogLevel.ERROR: + case 'error': console.error(formattedMessage, ...args); break; default: @@ -318,28 +312,28 @@ class Logger { * Log debug message */ debug(message: string, ...args: any[]): void { - this.logMessage(LogLevel.DEBUG, message, ...args); + this.logMessage('debug', message, ...args); } /** * Log info message */ info(message: string, ...args: any[]): void { - this.logMessage(LogLevel.INFO, message, ...args); + this.logMessage('info', message, ...args); } /** * Log warning message */ warn(message: string, ...args: any[]): void { - this.logMessage(LogLevel.WARN, message, ...args); + this.logMessage('warn', message, ...args); } /** * Log error message */ error(message: string, ...args: any[]): void { - this.logMessage(LogLevel.ERROR, message, ...args); + this.logMessage('error', message, ...args); } /** @@ -411,7 +405,7 @@ export const createComponentLogger = (component: string) => { export const createPackageLogger = (packageName: string) => { return createLogger({ prefix: `${PREFIX} - ${packageName}`, - level: LogLevel.INFO, + level: 'info', timestamps: true, showLevel: true, }); From 1c6564763d6304b94d74ca7b9c41cc20ab4a0859 Mon Sep 17 00:00:00 2001 From: Brion Date: Mon, 14 Jul 2025 00:50:05 +0530 Subject: [PATCH 7/9] feat(nextjs): enhance token handling by allowing optional ID token in getDecodedIdToken and improve access token retrieval --- packages/javascript/src/__legacy__/client.ts | 6 +-- packages/nextjs/src/AsgardeoNextClient.ts | 32 ++++++++----- .../src/server/actions/getAccessToken.ts | 48 +++++++++++++++++++ .../nextjs/src/server/actions/getSessionId.ts | 1 - .../actions/handleOAuthCallbackAction.ts | 2 + .../nextjs/src/server/actions/signInAction.ts | 4 +- .../src/server/actions/switchOrganization.ts | 32 +++++++++++-- packages/nextjs/src/utils/SessionManager.ts | 4 +- packages/node/src/__legacy__/client.ts | 4 +- .../src/__legacy__/core/authentication.ts | 4 +- 10 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 packages/nextjs/src/server/actions/getAccessToken.ts diff --git a/packages/javascript/src/__legacy__/client.ts b/packages/javascript/src/__legacy__/client.ts index 3c6d3013..e54ddaec 100644 --- a/packages/javascript/src/__legacy__/client.ts +++ b/packages/javascript/src/__legacy__/client.ts @@ -590,9 +590,9 @@ export class AsgardeoAuthClient { * * @preserve */ - public async getDecodedIdToken(userId?: string): Promise { - const idToken: string = (await this._storageManager.getSessionData(userId)).id_token; - const payload: IdToken = this._cryptoHelper.decodeIdToken(idToken); + public async getDecodedIdToken(userId?: string, idToken?: string): Promise { + const _idToken: string = (await this._storageManager.getSessionData(userId)).id_token; + const payload: IdToken = this._cryptoHelper.decodeIdToken(_idToken ?? idToken); return payload; } diff --git a/packages/nextjs/src/AsgardeoNextClient.ts b/packages/nextjs/src/AsgardeoNextClient.ts index 099b9aec..6adcde8d 100644 --- a/packages/nextjs/src/AsgardeoNextClient.ts +++ b/packages/nextjs/src/AsgardeoNextClient.ts @@ -381,27 +381,33 @@ class AsgardeoNextClient exte return this.asgardeo.isSignedIn(sessionId as string); } - getAccessToken(sessionId?: string): Promise { - if (!sessionId) { - return Promise.reject(new Error('Session ID is required to get access token')); + /** + * Gets the access token from the session cookie if no sessionId is provided, + * otherwise falls back to legacy client method. + */ + async getAccessToken(sessionId?: string): Promise { + const {default: getAccessToken} = await import('./server/actions/getAccessToken'); + const token = await getAccessToken(); + + if (typeof token !== 'string' || !token) { + throw new Error('Access token not found'); + throw new AsgardeoRuntimeError( + 'Failed to get access token.', + 'AsgardeoNextClient-getAccessToken-RuntimeError-003', + 'nextjs', + 'An error occurred while obtaining the access token. Please check your configuration and network connection.', + ); } - return this.asgardeo.getAccessToken(sessionId as string).then( - token => { - return token; - }, - error => { - throw error; - }, - ); + return token; } /** * Get the decoded ID token for a session */ - async getDecodedIdToken(sessionId?: string): Promise { + async getDecodedIdToken(sessionId?: string, idToken?: string): Promise { await this.ensureInitialized(); - return this.asgardeo.getDecodedIdToken(sessionId as string); + return this.asgardeo.getDecodedIdToken(sessionId as string, idToken); } override getConfiguration(): T { diff --git a/packages/nextjs/src/server/actions/getAccessToken.ts b/packages/nextjs/src/server/actions/getAccessToken.ts new file mode 100644 index 00000000..0fe5da93 --- /dev/null +++ b/packages/nextjs/src/server/actions/getAccessToken.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +'use server'; + +import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies'; +import {cookies} from 'next/headers'; +import SessionManager from '../../utils/SessionManager'; + +/** + * Get the access token from the session cookie. + * + * @returns The access token if it exists, undefined otherwise + */ +const getAccessToken = async (): Promise => { + const cookieStore: ReadonlyRequestCookies = await cookies(); + + const sessionToken = cookieStore.get(SessionManager.getSessionCookieName())?.value; + + if (sessionToken) { + try { + const sessionPayload = await SessionManager.verifySessionToken(sessionToken); + + return sessionPayload['accessToken'] as string; + } catch (error) { + return undefined; + } + } + + return undefined; +}; + +export default getAccessToken; diff --git a/packages/nextjs/src/server/actions/getSessionId.ts b/packages/nextjs/src/server/actions/getSessionId.ts index d1f7da56..388e3c0a 100644 --- a/packages/nextjs/src/server/actions/getSessionId.ts +++ b/packages/nextjs/src/server/actions/getSessionId.ts @@ -18,7 +18,6 @@ 'use server'; -import {CookieConfig} from '@asgardeo/node'; import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies'; import {cookies} from 'next/headers'; import SessionManager from '../../utils/SessionManager'; diff --git a/packages/nextjs/src/server/actions/handleOAuthCallbackAction.ts b/packages/nextjs/src/server/actions/handleOAuthCallbackAction.ts index d85982ef..ecaf364d 100644 --- a/packages/nextjs/src/server/actions/handleOAuthCallbackAction.ts +++ b/packages/nextjs/src/server/actions/handleOAuthCallbackAction.ts @@ -93,11 +93,13 @@ const handleOAuthCallbackAction = async ( if (signInResult) { try { const idToken = await asgardeoClient.getDecodedIdToken(sessionId); + const accessToken: string = signInResult['access_token']; const userIdFromToken = idToken.sub || signInResult['sub'] || sessionId; const scopes = idToken['scope'] ? idToken['scope'].split(' ') : []; const organizationId = idToken['user_org'] || idToken['organization_id']; const sessionToken = await SessionManager.createSessionToken( + accessToken, userIdFromToken, sessionId, scopes, diff --git a/packages/nextjs/src/server/actions/signInAction.ts b/packages/nextjs/src/server/actions/signInAction.ts index ed4c9b4b..537dd6d2 100644 --- a/packages/nextjs/src/server/actions/signInAction.ts +++ b/packages/nextjs/src/server/actions/signInAction.ts @@ -118,10 +118,12 @@ const signInAction = async ( if (signInResult) { const idToken = await client.getDecodedIdToken(sessionId); const userIdFromToken = idToken['sub'] || signInResult['sub'] || sessionId; - const scopes = idToken['scope'] ? idToken['scope'].split(' ') : []; + const accessToken = signInResult['accessToken']; + const scopes = signInResult['scope']; const organizationId = idToken['user_org'] || idToken['organization_id']; const sessionToken = await SessionManager.createSessionToken( + accessToken, userIdFromToken, sessionId as string, scopes, diff --git a/packages/nextjs/src/server/actions/switchOrganization.ts b/packages/nextjs/src/server/actions/switchOrganization.ts index 38d7b8a3..3cd9c856 100644 --- a/packages/nextjs/src/server/actions/switchOrganization.ts +++ b/packages/nextjs/src/server/actions/switchOrganization.ts @@ -21,6 +21,9 @@ import {Organization, AsgardeoAPIError, TokenResponse} from '@asgardeo/node'; import getSessionId from './getSessionId'; import AsgardeoNextClient from '../../AsgardeoNextClient'; +import logger from '../../utils/logger'; +import SessionManager from '../../utils/SessionManager'; +import {cookies} from 'next/headers'; /** * Server action to switch organization. @@ -30,11 +33,10 @@ const switchOrganization = async ( sessionId: string | undefined, ): Promise => { try { + const cookieStore = await cookies(); const client: AsgardeoNextClient = AsgardeoNextClient.getInstance(); - const response: TokenResponse | Response = await client.switchOrganization( - organization, - sessionId ?? ((await getSessionId()) as string), - ); + const _sessionId: string = sessionId ?? ((await getSessionId()) as string); + const response: TokenResponse | Response = await client.switchOrganization(organization, _sessionId); // After switching organization, we need to refresh the page to get updated session data // This is because server components don't maintain state between function calls @@ -43,10 +45,30 @@ const switchOrganization = async ( // Revalidate the current path to refresh the component with new data revalidatePath('/'); + if (response) { + const idToken = await client.getDecodedIdToken(_sessionId, (response as TokenResponse).idToken); + const userIdFromToken = idToken['sub']; + const accessToken = (response as TokenResponse).accessToken; + const scopes = (response as TokenResponse).scope; + const organizationId = idToken['user_org'] || idToken['organization_id']; + + const sessionToken = await SessionManager.createSessionToken( + accessToken, + userIdFromToken as string, + _sessionId as string, + scopes, + organizationId, + ); + + logger.debug('[switchOrganization] Session token created successfully.'); + + cookieStore.set(SessionManager.getSessionCookieName(), sessionToken, SessionManager.getSessionCookieOptions()); + } + return response; } catch (error) { throw new AsgardeoAPIError( - `Failed to switch the organizations: ${error instanceof Error ? error.message : String(error)}`, + `Failed to switch the organizations: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, 'switchOrganization-ServerActionError-001', 'nextjs', error instanceof AsgardeoAPIError ? error.statusCode : undefined, diff --git a/packages/nextjs/src/utils/SessionManager.ts b/packages/nextjs/src/utils/SessionManager.ts index 82edb862..63a43c77 100644 --- a/packages/nextjs/src/utils/SessionManager.ts +++ b/packages/nextjs/src/utils/SessionManager.ts @@ -89,15 +89,17 @@ class SessionManager { * Create a session cookie with user information */ static async createSessionToken( + accessToken: string, userId: string, sessionId: string, - scopes: string[], + scopes: string, organizationId?: string, expirySeconds: number = this.DEFAULT_EXPIRY_SECONDS, ): Promise { const secret = this.getSecret(); const jwt = await new SignJWT({ + accessToken, sessionId, scopes, organizationId, diff --git a/packages/node/src/__legacy__/client.ts b/packages/node/src/__legacy__/client.ts index 3b66b8ac..fb3a12e1 100644 --- a/packages/node/src/__legacy__/client.ts +++ b/packages/node/src/__legacy__/client.ts @@ -242,8 +242,8 @@ export class AsgardeoNodeClient { * @memberof AsgardeoNodeClient * */ - public async getDecodedIdToken(userId?: string): Promise { - return this._authCore.getDecodedIdToken(userId); + public async getDecodedIdToken(userId?: string, idToken?: string): Promise { + return this._authCore.getDecodedIdToken(userId, idToken); } /** diff --git a/packages/node/src/__legacy__/core/authentication.ts b/packages/node/src/__legacy__/core/authentication.ts index 00e33fdb..1ee2d657 100644 --- a/packages/node/src/__legacy__/core/authentication.ts +++ b/packages/node/src/__legacy__/core/authentication.ts @@ -223,8 +223,8 @@ export class AsgardeoNodeCore { return this._auth.getOpenIDProviderEndpoints() as Promise; } - public async getDecodedIdToken(userId?: string): Promise { - return this._auth.getDecodedIdToken(userId); + public async getDecodedIdToken(userId?: string, idToken?: string): Promise { + return this._auth.getDecodedIdToken(userId, idToken); } public async getAccessToken(userId?: string): Promise { From 8632ae13cb0dd8c17fc220f2085a925ae82ea73b Mon Sep 17 00:00:00 2001 From: Brion Date: Mon, 14 Jul 2025 00:50:24 +0530 Subject: [PATCH 8/9] feat(nextjs): refactor asgardeo to a server-only singleton for API access and export it --- packages/nextjs/src/server/asgardeo.ts | 25 ++++++++++++++++++------- packages/nextjs/src/server/index.ts | 2 ++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/nextjs/src/server/asgardeo.ts b/packages/nextjs/src/server/asgardeo.ts index a919cd39..4fbc797b 100644 --- a/packages/nextjs/src/server/asgardeo.ts +++ b/packages/nextjs/src/server/asgardeo.ts @@ -17,11 +17,22 @@ */ import AsgardeoNextClient from '../AsgardeoNextClient'; +import getSessionIdAction from './actions/getSessionId'; -/** - * Server-only singleton for API access. - * Usage: - * import { asgardeo } from '@asgardeo/nextjs/server'; - * const token = await asgardeo.getAccessToken(); - */ -export const asgardeo = AsgardeoNextClient.getInstance(); +const asgardeo = async () => { + const getAccessToken = async (id: string) => { + const client: AsgardeoNextClient = AsgardeoNextClient.getInstance(); + return await client.getAccessToken(id); + }; + + const getSessionId = async () => { + return await getSessionIdAction(); + }; + + return { + getAccessToken, + getSessionId, + }; +}; + +export default asgardeo; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 82f0587a..67d03be4 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -18,6 +18,8 @@ // @asgardeo/nextjs/server exports. +export {default as asgardeo} from './asgardeo'; + export {default as AsgardeoProvider} from './AsgardeoProvider'; export * from './AsgardeoProvider'; From 7e8ea1ca9219c1c95404933e8261b2abfbcad767 Mon Sep 17 00:00:00 2001 From: Brion Date: Mon, 14 Jul 2025 09:11:57 +0530 Subject: [PATCH 9/9] =?UTF-8?q?chore:=20add=20changeset=20=F0=9F=A6=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/fair-spiders-dream.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/fair-spiders-dream.md diff --git a/.changeset/fair-spiders-dream.md b/.changeset/fair-spiders-dream.md new file mode 100644 index 00000000..4a4467e6 --- /dev/null +++ b/.changeset/fair-spiders-dream.md @@ -0,0 +1,8 @@ +--- +'@asgardeo/javascript': patch +'@asgardeo/nextjs': patch +'@asgardeo/react': patch +'@asgardeo/node': patch +--- + +Fix `getAccessToken` imperative usage