-
Notifications
You must be signed in to change notification settings - Fork 61
Enhance authentication and UserProfile component for AsgardeoV2 #499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
862328f
646b437
057699d
2e7f004
87bdf39
169eb1b
a399a6d
4d2a0e9
ecc4211
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@asgardeo/javascript': minor | ||
| '@asgardeo/nuxt': minor | ||
| '@asgardeo/vue': minor | ||
| --- | ||
|
|
||
| Add AsgardeoV2 platform support with Basic Auth token requests, platform-aware sign-out/profile handling in Nuxt, and updated component exports in Vue |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /** | ||
| * 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. | ||
| */ | ||
|
|
||
| /** | ||
| * OAuth 2.0 client authentication method used at the token endpoint. | ||
| * Corresponds to the `token_endpoint_auth_method` parameter in OIDC Discovery. | ||
| * | ||
| * - `client_secret_basic` — HTTP Basic authentication: credentials are sent in the | ||
| * `Authorization: Basic base64(client_id:client_secret)` header (RFC 6749 §2.3.1). | ||
| * Required for AsgardeoV2 (Thunder) by default. | ||
| * - `client_secret_post` — Credentials are sent as `client_id` / `client_secret` | ||
| * parameters in the POST body (RFC 6749 §2.3.1). Default for all other platforms. | ||
| * - `none` — No client authentication (public clients that have no client secret). | ||
| */ | ||
| export type TokenEndpointAuthMethod = 'client_secret_basic' | 'client_secret_post' | 'none'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /** | ||
| * Copyright (c) 2026, 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 * as jose from 'jose'; | ||
|
|
||
| /** | ||
| * Encodes a string to standard base64 using `jose` (already a package dependency). | ||
| * | ||
| * `jose.base64url.encode` is environment-agnostic (browser, Node.js, Deno, Bun, | ||
| * edge/service-worker runtimes). It produces base64url output, which is then | ||
| * converted to standard base64 by restoring the `+`/`/` characters and adding | ||
| * `=` padding. | ||
| * | ||
| * @param value - The UTF-8 string to encode. | ||
| * @returns The standard base64-encoded string (with `+`, `/`, and `=` padding). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * base64Encode('clientId:clientSecret'); // "Y2xpZW50SWQ6Y2xpZW50U2VjcmV0" | ||
| * ``` | ||
| */ | ||
| const base64Encode = (value: string): string => { | ||
| const b64url: string = jose.base64url.encode(new TextEncoder().encode(value)); | ||
| const rem: number = b64url.length % 4; | ||
| const padded: string = rem === 0 ? b64url : b64url + '='.repeat(4 - rem); | ||
|
|
||
| return padded.replace(/-/g, '+').replace(/_/g, '/'); | ||
| }; | ||
|
|
||
| export default base64Encode; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| import { | ||
| AsgardeoNodeClient, | ||
| LegacyAsgardeoNodeClient, | ||
| Platform, | ||
| type AuthClientConfig, | ||
| type IdToken, | ||
| type Organization, | ||
|
|
@@ -117,7 +118,9 @@ class AsgardeoNuxtClient extends AsgardeoNodeClient<AsgardeoNuxtConfig> { | |
| clientId: config.clientId as string, | ||
| clientSecret: config.clientSecret || undefined, | ||
| enablePKCE: true, | ||
| platform: config.platform, | ||
| scopes: config.scopes || ['openid', 'profile'], | ||
| tokenRequest: config.tokenRequest, | ||
| } as AuthClientConfig<AsgardeoNuxtConfig>; | ||
|
|
||
| const result: boolean = await this.legacy.initialize(authConfig, storage); | ||
|
|
@@ -300,9 +303,22 @@ class AsgardeoNuxtClient extends AsgardeoNodeClient<AsgardeoNuxtConfig> { | |
| /** | ||
| * Clears the session and returns the RP-Initiated Logout URL. | ||
| * Accepts either `(sessionId: string)` or `(options?, sessionId?, callback?)`. | ||
| * | ||
| * For AsgardeoV2 (Thunder), RP-Initiated Logout is not yet supported by the platform. | ||
| * Skip the /oidc/logout call and return afterSignOutUrl directly — the caller | ||
| * (signout.post.ts) is responsible for clearing session cookies. | ||
| */ | ||
| override async signOut(...args: any[]): Promise<string> { | ||
| const sessionId: string = typeof args[0] === 'string' ? args[0] : (args[1] as string); | ||
|
|
||
| const configData: AuthClientConfig<AsgardeoNuxtConfig> | undefined = (await this.legacy.getConfigData?.()) as | ||
| | AuthClientConfig<AsgardeoNuxtConfig> | ||
| | undefined; | ||
|
|
||
| if ((configData as any)?.platform === Platform.AsgardeoV2) { | ||
| return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/'; | ||
| } | ||
|
|
||
| return this.legacy.signOut(sessionId); | ||
| } | ||
|
Comment on lines
311
to
323
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm what legacy.signOut actually does so we know what we’re skipping for V2.
ast-grep --pattern 'public async signOut($_) {
$$$
}'
echo '--- legacy signOut ---'
rg -nP -C5 '\bsignOut\s*\(' --type=ts -g '!**/test/**' | head -200Repository: asgardeo/javascript Length of output: 16291 🏁 Script executed: # First, let's locate and read the specific file
find . -name "AsgardeoNuxtClient.ts" -type fRepository: asgardeo/javascript Length of output: 120 🏁 Script executed: # Get the file size first
wc -l packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript Length of output: 122 🏁 Script executed: # Read the signOut method and surrounding context
sed -n '300,330p' packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript Length of output: 1267 🏁 Script executed: # Check the full file structure to understand inheritance and imports
head -50 packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript Length of output: 1498 🏁 Script executed: # Find where LegacyAsgardeoNodeClient is defined
fd LegacyAsgardeoNodeClientRepository: asgardeo/javascript Length of output: 45 🏁 Script executed: # Search for the signOut implementation in legacy client
rg -n "signOut" packages/js-sdk/src/client/legacy/ -t ts -A 10 | head -100Repository: asgardeo/javascript Length of output: 138 🏁 Script executed: # Check AuthClientConfig type definition
rg -n "interface AuthClientConfig|type AuthClientConfig" -t tsRepository: asgardeo/javascript Length of output: 281 🏁 Script executed: # Look for getStorageManager implementation
rg -n "getStorageManager" -t ts -A 5 | head -100Repository: asgardeo/javascript Length of output: 7714 🏁 Script executed: # Find and read the legacy client signOut implementation
fd "client.ts" packages/node/src/__legacy__/ | xargs grep -n "signOut" -A 15Repository: asgardeo/javascript Length of output: 1013 🏁 Script executed: # Read AuthClientConfig type definition
cat packages/javascript/src/__legacy__/models/client-config.ts | head -150Repository: asgardeo/javascript Length of output: 3317 🏁 Script executed: # Search for MemoryCacheStore
rg -n "MemoryCacheStore" -t ts -B 3 -A 10 | head -100Repository: asgardeo/javascript Length of output: 6992 🏁 Script executed: # Search for session cleanup in signOut
rg -n "signOut" packages/node/src/__legacy__/core/authentication.ts -A 20 | head -150Repository: asgardeo/javascript Length of output: 1257 🏁 Script executed: # Check StorageManager interface and methods
rg -n "interface StorageManager|class StorageManager" -t ts -A 20 | head -150Repository: asgardeo/javascript Length of output: 1683 🏁 Script executed: # Check if there's a removeSessionData or clearSession method
rg -n "removeSessionData|clearSession|deleteSessionData" -t ts -B 2 -A 5 | head -100Repository: asgardeo/javascript Length of output: 8319 🏁 Script executed: # Check what getSignOutUrl does in the auth client
rg -n "getSignOutUrl" -t ts -A 15 | head -100Repository: asgardeo/javascript Length of output: 8726 🏁 Script executed: # Look for how session cleanup is handled in the javascript SDK
rg -n "signOut" packages/javascript/src/__legacy__/client/ -t ts -A 15 | head -150Repository: asgardeo/javascript Length of output: 146 🏁 Script executed: # Check if clearSession exists on AsgardeoNodeClient
rg -n "clearSession" packages/node/src -t ts -B 2 -A 10 | head -150Repository: asgardeo/javascript Length of output: 45 🏁 Script executed: # Check the javascript SDK clearSession implementation
rg -n "clearSession" packages/javascript/src/__legacy__/client -t ts -B 2 -A 10 | head -150Repository: asgardeo/javascript Length of output: 145 🏁 Script executed: # Check if AsgardeoNuxtClient has clearSession
grep -n "clearSession" packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript Length of output: 45 🏁 Script executed: # Look at the full signOut implementation again with more context
sed -n '295,335p' packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript Length of output: 1683 🏁 Script executed: # Check the signout.post.ts route mentioned in the comments
find . -name "signout.post.ts" -o -name "signout.ts" | grep -i postRepository: asgardeo/javascript Length of output: 134 🏁 Script executed: # Look for any session cleanup in signout routes
rg -n "signout" packages/nuxt/src/runtime/server/routes/ -t ts | head -50Repository: asgardeo/javascript Length of output: 161 🏁 Script executed: # Verify what setSessionData does when passed empty object
rg -n "setSessionData" packages/javascript/src/StorageManager.ts -A 15Repository: asgardeo/javascript Length of output: 1518 🏁 Script executed: # Read the full signout.post.ts route to see what happens
cat packages/nuxt/src/runtime/server/routes/auth/session/signout.post.tsRepository: asgardeo/javascript Length of output: 2835 Remove unnecessary The Additionally, both V2 and non-V2 paths currently skip in-memory 🛡️ Suggested adjustment- if ((configData as any)?.platform === Platform.AsgardeoV2) {
- return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/';
- }
+ if (configData?.platform === Platform.AsgardeoV2) {
+ // Best-effort: drop the in-memory session entry so tokens are not reachable post sign-out.
+ try {
+ const storageManager = await this.legacy.getStorageManager();
+ await storageManager.setSessionData({} as any, sessionId);
+ } catch {
+ // ignore — cookie clearing is the source of truth
+ }
+ return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/';
+ }🤖 Prompt for AI Agents |
||
|
|
||
|
|
@@ -344,6 +360,12 @@ class AsgardeoNuxtClient extends AsgardeoNodeClient<AsgardeoNuxtConfig> { | |
| | undefined; | ||
| const baseUrl: string = (configData?.baseUrl ?? '') as string; | ||
|
|
||
| // AsgardeoV2 (Thunder) does not support SCIM2 — return ID token claims directly. | ||
| if ((configData as any)?.platform === Platform.AsgardeoV2) { | ||
| const user: User = await this.getUser(sessionId); | ||
| return {flattenedProfile: user, profile: user, schemas: []}; | ||
| } | ||
|
|
||
| try { | ||
| const authHeaders: Record<string, string> = {Authorization: `Bearer ${accessToken}`}; | ||
|
|
||
|
|
@@ -423,6 +445,11 @@ class AsgardeoNuxtClient extends AsgardeoNodeClient<AsgardeoNuxtConfig> { | |
| | undefined; | ||
| const baseUrl: string = (configData?.baseUrl ?? '') as string; | ||
|
|
||
| // AsgardeoV2 (Thunder) does not support SCIM2 profile updates. | ||
| if ((configData as any)?.platform === Platform.AsgardeoV2) { | ||
| throw new Error('Profile updates are not supported for the AsgardeoV2 (Thunder) platform.'); | ||
| } | ||
|
|
||
| return updateMeProfile({ | ||
| ...config, // pass-through, includes payload | ||
| baseUrl, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,7 +100,9 @@ export default defineNitroPlugin((nitro: {hooks: {hook: Function}}) => { | |
| baseUrl: publicConfig.baseUrl, | ||
| clientId: publicConfig.clientId, | ||
| clientSecret: privateConfig?.clientSecret || undefined, | ||
| platform: publicConfig.platform, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify that Platform is a string enum whose values equal its keys, so passing
# `keyof typeof Platform` directly is safe.
ast-grep --pattern 'export enum Platform { $$$ }'
ast-grep --pattern 'enum Platform { $$$ }'
# Also surface how AsgardeoNuxtClient.initialize types the platform parameter.
rg -nP -C3 '\bplatform\s*[?:]\s*' packages/nuxt/src/runtime/server/AsgardeoNuxtClient.ts packages/javascript/src/__legacy__/client.ts packages/javascript/src/models/config.ts || trueRepository: asgardeo/javascript Length of output: 2919 🏁 Script executed: rg -nP -C5 'platform.*==|Platform\.' packages/javascript/src/__legacy__/client.ts packages/nuxt/src/runtime/server/AsgardeoNuxtClient.ts | head -100Repository: asgardeo/javascript Length of output: 6171 Fix the type-value mismatch in the Platform enum before passing to runtime comparisons. The Platform enum has mismatched keys and values for all members except AsgardeoV2:
The type annotation at Either align the enum values to match their keys, or change the type annotation to accept 🤖 Prompt for AI Agents |
||
| scopes: publicConfig.scopes || ['openid', 'profile'], | ||
| tokenRequest: publicConfig.tokenRequest, | ||
| }); | ||
| } catch (err) { | ||
| log.error('Failed to initialize Asgardeo client:', err); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,9 +16,11 @@ | |
| * under the License. | ||
| */ | ||
|
|
||
| import {Platform} from '@asgardeo/node'; | ||
| import type {UpdateMeProfileConfig, User} from '@asgardeo/node'; | ||
| import {defineEventHandler, readBody, createError} from 'h3'; | ||
| import type {H3Event} from 'h3'; | ||
| import type {AsgardeoNuxtConfig} from '../../../../types'; | ||
| import AsgardeoNuxtClient from '../../../AsgardeoNuxtClient'; | ||
| import {verifyAndRehydrateSession} from '../../../utils/serverSession'; | ||
| import {useRuntimeConfig} from '#imports'; | ||
|
|
@@ -36,6 +38,14 @@ export default defineEventHandler( | |
| async (event: H3Event): Promise<{data: {user: User}; error: string; success: boolean}> => { | ||
| const config: ReturnType<typeof useRuntimeConfig> = useRuntimeConfig(); | ||
| const sessionSecret: string | undefined = config.asgardeo?.sessionSecret; | ||
| const publicConfig: AsgardeoNuxtConfig = config.public.asgardeo as AsgardeoNuxtConfig; | ||
|
|
||
| if ((publicConfig?.platform as any) === Platform.AsgardeoV2) { | ||
| throw createError({ | ||
| statusCode: 501, | ||
| statusMessage: 'Profile updates are not supported for the AsgardeoV2 (Thunder) platform.', | ||
| }); | ||
| } | ||
|
Comment on lines
+41
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Two safer alternatives:
- if ((publicConfig?.platform as any) === Platform.AsgardeoV2) {
+ if (publicConfig?.platform === 'AsgardeoV2') {
#!/bin/bash
# Check the actual shape of Platform to determine whether key === value.
ast-grep --pattern 'export enum Platform { $$$ }'
ast-grep --pattern 'enum Platform { $$$ }'
# Find every other site that compares against Platform.AsgardeoV2 the same way,
# so we can keep them consistent.
rg -nP -C2 'Platform\.AsgardeoV2'🤖 Prompt for AI Agents |
||
|
|
||
| const session: Awaited<ReturnType<typeof verifyAndRehydrateSession>> = await verifyAndRehydrateSession( | ||
| event, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: asgardeo/javascript
Length of output: 21680
🏁 Script executed:
cat packages/javascript/src/models/platforms.ts | head -50Repository: asgardeo/javascript
Length of output: 1515
🏁 Script executed:
cat packages/nuxt/src/runtime/types.ts | head -60Repository: asgardeo/javascript
Length of output: 2357
🏁 Script executed:
sed -n '310,325p' packages/nuxt/src/runtime/server/AsgardeoNuxtClient.tsRepository: asgardeo/javascript
Length of output: 726
Remove unnecessary
as anycasts; the comparison is type-safe without them.config.platformis already correctly typed askeyof typeof PlatforminAsgardeoNuxtConfig. SincePlatform.AsgardeoV2evaluates to the string'AsgardeoV2'(matching the key name), the comparison at lines 317, 363, and 448 works correctly. However, theas anycasts are unnecessary and obscure the type system. TypeconfigDataasAuthClientConfig<AsgardeoNuxtConfig>directly instead of casting toany:This maintains the same runtime behavior while improving type safety and clarity.
Note: The
signOutmethod (line 321) intentionally skipslegacy.signOut()for AsgardeoV2, bypassing in-memory session cleanup. This is acknowledged in existing doc comments but may warrant a code comment for future maintainers about token lifecycle implications.🤖 Prompt for AI Agents