Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
"author": "WSO2",
"license": "Apache-2.0",
"type": "module",
"main": "dist/cjs/index.js",
"main": "dist/cjs/index.cjs",
"module": "dist/index.js",
"exports": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js"
"require": "./dist/cjs/index.cjs"
},
"files": [
"dist",
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/rolldown.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ await esmBundle.close();
const cjsBundle = await rolldown(commonOptions);
await cjsBundle.write({
banner: `const { Buffer } = require('buffer/index.js');\nif (typeof window !== 'undefined' && !window.Buffer) { window.Buffer = Buffer; }`,
file: 'dist/cjs/index.js',
file: 'dist/cjs/index.cjs',
format: 'cjs',
sourcemap: true,
});
Expand Down
4 changes: 2 additions & 2 deletions packages/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"commonjs": "dist/cjs/index.js",
"commonjs": "dist/cjs/index.cjs",
"exports": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js"
"require": "./dist/cjs/index.cjs"
},
"files": [
"dist",
Expand Down
56 changes: 49 additions & 7 deletions packages/javascript/src/ThunderIDJavaScriptClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,30 +342,72 @@ class ThunderIDJavaScriptClient<T = Config> implements ThunderIDClient<T> {
(discovery?.wellKnown?.enabled !== false && baseUrl ? `${baseUrl}${WELL_KNOWN_PATH}` : undefined);

if (resolvedWellKnownEndpoint) {
let response: Response;
let response: Response | undefined;

try {
response = await fetch(resolvedWellKnownEndpoint);
if (response.status !== 200 || !response.ok) {
throw new Error();
if (!response.ok || response.status !== 200) {
response = undefined;
}
} catch {
response = undefined;
}

if (response) {
let discoveryResolved = false;
try {
await this.storageManager.setOIDCProviderMetaData(
await this.authHelper.resolveEndpoints(await response.json()),
);
discoveryResolved = true;
} catch {
// Parsing or endpoint resolution failed; fall through to baseUrl fallback.
}

if (!discoveryResolved) {
if (baseUrl) {
try {
await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpointsByBaseURL());
} catch (error: unknown) {
throw new ThunderIDAuthException(
'JS-AUTH_CORE-GOPMD-IV02',
'Resolving endpoints failed.',
error instanceof Error ? error.message : 'Resolving endpoints by base url failed.',
);
}
} else {
throw new ThunderIDAuthException(
'JS-AUTH_CORE-GOPMD-HE01',
'Invalid well-known response',
'The well known endpoint response has been failed with an error.',
);
}
}
} else if (baseUrl) {
try {
await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpointsByBaseURL());
} catch (error: unknown) {
throw new ThunderIDAuthException(
'JS-AUTH_CORE-GOPMD-IV02',
'Resolving endpoints failed.',
error instanceof Error ? error.message : 'Resolving endpoints by base url failed.',
);
}
} else {
throw new ThunderIDAuthException(
'JS-AUTH_CORE-GOPMD-HE01',
'Invalid well-known response',
'The well known endpoint response has been failed with an error.',
);
}

await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpoints(await response.json()));
} else if (baseUrl) {
try {
await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpointsByBaseURL());
} catch (error: any) {
} catch (error: unknown) {
throw new ThunderIDAuthException(
'JS-AUTH_CORE-GOPMD-IV02',
'Resolving endpoints failed.',
error ?? 'Resolving endpoints by base url failed.',
error instanceof Error ? error.message : 'Resolving endpoints by base url failed.',
);
}
} else {
Expand Down
2 changes: 0 additions & 2 deletions packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,6 @@ export default defineNuxtModule<ThunderIDNuxtConfig>({
method: 'patch' as const,
route: '/api/auth/user/profile',
},
// ── Branding ──────────────────────────────────────────────────────
{handler: resolve('./runtime/server/routes/auth/branding/branding.get'), route: '/api/auth/branding'},
];

serverRoutes.forEach((sr: ServerRoute): void => {
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
"author": "WSO2",
"license": "Apache-2.0",
"type": "module",
"main": "dist/cjs/index.js",
"main": "dist/cjs/index.cjs",
"module": "dist/index.js",
"exports": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js"
"require": "./dist/cjs/index.cjs"
},
"files": [
"dist",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/rolldown.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ await esmBundle.close();

const cjsBundle = await rolldown(commonOptions);
await cjsBundle.write({
file: 'dist/cjs/index.js',
file: 'dist/cjs/index.cjs',
format: 'cjs',
sourcemap: true,
});
Expand Down
4 changes: 2 additions & 2 deletions packages/tanstack-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
"author": "WSO2",
"license": "Apache-2.0",
"type": "module",
"main": "dist/cjs/index.js",
"main": "dist/cjs/index.cjs",
"module": "dist/index.js",
"exports": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js"
"require": "./dist/cjs/index.cjs"
},
"files": [
"dist",
Expand Down
2 changes: 1 addition & 1 deletion packages/tanstack-router/rolldown.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ await esmBundle.close();

const cjsBundle = await rolldown(commonOptions);
await cjsBundle.write({
file: 'dist/cjs/index.js',
file: 'dist/cjs/index.cjs',
format: 'cjs',
sourcemap: true,
});
Expand Down
2 changes: 1 addition & 1 deletion samples/browser/quickstart/src/pages/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function renderSignedOut() {
<span>v1.0 · Open source</span>
<span class="hero-badge-line"></span>
</div>
<h1 class="hero-title">Auth for the Modern Dev</h1>
<h1 class="hero-title">Auth for Modern Apps and Agents</h1>
<p class="hero-subtitle">
ThunderID gives you OAuth 2.0, PKCE, MFA, and JWT out of the box.
Clone the Quickstart and ship auth before lunch.
Expand Down
2 changes: 2 additions & 0 deletions samples/express/quickstart/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
THUNDERID_CLIENT_ID=your-client-id-here
THUNDERID_CLIENT_SECRET=your-client-secret-here
THUNDERID_BASE_URL=https://localhost:8090
# DANGER: Disables ALL TLS verification. Only for local development with self-signed certs. NEVER use in production.
NODE_TLS_REJECT_UNAUTHORIZED=0
3 changes: 3 additions & 0 deletions samples/nextjs/quickstart/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
NEXT_PUBLIC_THUNDERID_BASE_URL=https://localhost:8090
NEXT_PUBLIC_THUNDERID_CLIENT_ID=your-client-id-here
NEXT_PUBLIC_THUNDERID_APPLICATION_ID=your-application-id-here
THUNDERID_CLIENT_SECRET=your-client-secret-here
THUNDERID_SECRET=generate-with-openssl-rand-base64-32
# DANGER: Disables ALL TLS verification. Only for local development with self-signed certs. NEVER use in production.
NODE_TLS_REJECT_UNAUTHORIZED=0
2 changes: 1 addition & 1 deletion samples/nextjs/quickstart/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export default function HomePage() {
<span className="hero-badge-line" />
</div>

<h1 className="hero-title">Auth for the Modern Dev</h1>
<h1 className="hero-title">Auth for Modern Apps and Agents</h1>

<p className="hero-subtitle">
ThunderID gives you OAuth&nbsp;2.0, PKCE, MFA, and JWT out of
Expand Down
2 changes: 2 additions & 0 deletions samples/node/quickstart/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
THUNDERID_CLIENT_ID=your-client-id-here
THUNDERID_CLIENT_SECRET=your-client-secret-here
THUNDERID_BASE_URL=https://localhost:8090
# DANGER: Disables ALL TLS verification. Only for local development with self-signed certs. NEVER use in production.
NODE_TLS_REJECT_UNAUTHORIZED=0
2 changes: 2 additions & 0 deletions samples/nuxt/quickstart/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ NUXT_PUBLIC_THUNDERID_BASE_URL=https://localhost:8090
NUXT_PUBLIC_THUNDERID_CLIENT_ID=your-client-id-here
THUNDERID_CLIENT_SECRET=your-client-secret-here
THUNDERID_SESSION_SECRET=generate-with-openssl-rand-base64-32
# DANGER: Disables ALL TLS verification. Only for local development with self-signed certs. NEVER use in production.
NODE_TLS_REJECT_UNAUTHORIZED=0
2 changes: 1 addition & 1 deletion samples/nuxt/quickstart/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ onUnmounted(() => {
<span class="hero-badge-line" />
</div>

<h1 class="hero-title">Auth for the Modern Dev</h1>
<h1 class="hero-title">Auth for Modern Apps and Agents</h1>

<p class="hero-subtitle">
ThunderID gives you OAuth&nbsp;2.0, PKCE, MFA, and JWT out of
Expand Down
3 changes: 1 addition & 2 deletions samples/react/quickstart/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createBrowserRouter, RouterProvider } from 'react-router'
import { ProtectedRoute, CallbackRoute } from '@thunderid/react-router'
import { ProtectedRoute } from '@thunderid/react-router'
import Nav from './components/Nav'
import HomePage from './pages/HomePage'
import ProfilePage from './pages/ProfilePage'
Expand All @@ -13,7 +13,6 @@ const router = createBrowserRouter([
{ path: '/', element: <HomePage /> },
{ path: '/profile', element: <ProtectedRoute><ProfilePage /></ProtectedRoute> },
{ path: '/token', element: <ProtectedRoute><TokenDebugPage /></ProtectedRoute> },
{ path: '/callback', element: <CallbackRoute /> },
],
},
])
Expand Down
1 change: 0 additions & 1 deletion samples/react/quickstart/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ createRoot(document.getElementById('root')).render(
<ThunderIDProvider
clientId={import.meta.env.VITE_THUNDERID_CLIENT_ID}
baseUrl={import.meta.env.VITE_THUNDERID_BASE_URL}
afterSignInUrl={`${window.location.origin}/callback`}
>
<App />
</ThunderIDProvider>
Expand Down
2 changes: 1 addition & 1 deletion samples/react/quickstart/src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export default function HomePage() {
<span className="hero-badge-line" />
</div>

<h1 className="hero-title">Auth for the Modern Dev</h1>
<h1 className="hero-title">Auth for Modern Apps and Agents</h1>

<p className="hero-subtitle">
ThunderID gives you OAuth 2.0, PKCE, MFA, and JWT out of the box.
Expand Down
2 changes: 1 addition & 1 deletion samples/vue/quickstart/src/pages/HomePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ onUnmounted(() => {
<span class="hero-badge-line"></span>
</div>

<h1 class="hero-title">Auth for the Modern Dev</h1>
<h1 class="hero-title">Auth for Modern Apps and Agents</h1>

<p class="hero-subtitle">
ThunderID gives you OAuth 2.0, PKCE, MFA, and JWT out of the box.
Expand Down
2 changes: 1 addition & 1 deletion templates/nextjs/nextjs-template/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Home() {
<main>
<SignedOut>
<h1>Welcome</h1>
<SignInButton />
<SignInButton>Sign In</SignInButton>
</SignedOut>
<SignedIn>
<User>{(user) => <h1>Hello, {user.given_name || user.username}.</h1>}</User>
Expand Down
12 changes: 10 additions & 2 deletions templates/react/vite-react-template/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { createBrowserRouter, RouterProvider } from 'react-router'
import { CallbackRoute } from '@thunderid/react-router'
import { ProtectedRoute } from '@thunderid/react-router'
import Home from './Home.jsx'
import Dashboard from './Dashboard.jsx'

const router = createBrowserRouter([
{ path: '/', element: <Home /> },
{ path: '/callback', element: <CallbackRoute /> },
{
path: '/dashboard',
element: (
<ProtectedRoute redirectTo="/">
<Dashboard />
</ProtectedRoute>
),
},
])

export default function App() {
Expand Down
9 changes: 9 additions & 0 deletions templates/react/vite-react-template/src/Dashboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UserDropdown } from '@thunderid/react'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the standard Apache header.

This new file is missing the repository’s required copyright banner, so the lint step will keep failing until it is added.

🧰 Tools
🪛 ESLint

[error] 1-10: Missing WSO2 Apache 2.0 copyright header

(@thunderid/copyright-header)


[error] 1-1: Unable to resolve path to module '@thunderid/react'.

(import-x/no-unresolved)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/react/vite-react-template/src/Dashboard.jsx` at line 1, The new
Dashboard.jsx file is missing the repository’s standard Apache copyright banner,
which will cause lint to fail. Add the required Apache header comment at the top
of the file, above the existing UserDropdown import, matching the template used
by other files in this repository.

Source: Linters/SAST tools


export default function Dashboard() {
return (
<main>
<UserDropdown />
</main>
)
}
11 changes: 2 additions & 9 deletions templates/react/vite-react-template/src/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { SignedIn, SignedOut, SignInButton, SignOutButton, User } from '@thunderid/react'
import { SignInButton } from '@thunderid/react'

export default function Home() {
return (
<main>
<SignedOut>
<h1>Welcome</h1>
<SignInButton />
</SignedOut>
<SignedIn>
<User>{(user) => <h1>Hello, {user.given_name || user.username}.</h1>}</User>
<SignOutButton />
</SignedIn>
<SignInButton>Sign In</SignInButton>
</main>
)
}
Comment on lines +1 to 9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

@thunderid/react SignInButton component behavior when user is already signed in

💡 Result:

In the @thunderid/react SDK, the SignInButton component is designed to be used in conjunction with conditional rendering components like SignedIn and SignedOut [1][2]. When a user is already signed in, the SignInButton component is not rendered because developers typically wrap it inside the SignedOut component [1][3][4]. The SignedOut component automatically hides its children when an authentication session is detected [1][4]. If you were to manually render the SignInButton while a user is already signed in, the SDK's internal authentication state management would recognize the active session, and clicking the button would typically result in no action, or it would simply redirect the user back to the application or your configured post-login redirect URI, as the authentication flow is already satisfied [4]. To implement this correctly, use the provided conditional rendering components as shown in the official patterns: import { SignedIn, SignedOut, SignInButton, SignOutButton } from '@thunderid/react'; function App { return ( <> Sign In Sign Out </>); } By following this pattern, the SignInButton is physically removed from the DOM when the user has an active session [1][4].

Citations:


🏁 Script executed:

git ls-files | rg '^(templates/react/vite-react-template|templates/react|.*react.*template).*'

Repository: thunder-id/javascript-sdks

Length of output: 656


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file list ==\n'
git ls-files templates/react/vite-react-template/src templates/react/vite-react-template/README.md templates/react/vite-react-template/package.json

printf '\n== main.jsx ==\n'
cat -n templates/react/vite-react-template/src/main.jsx

printf '\n== App.jsx ==\n'
cat -n templates/react/vite-react-template/src/App.jsx

printf '\n== Home.jsx ==\n'
cat -n templates/react/vite-react-template/src/Home.jsx

printf '\n== Dashboard.jsx ==\n'
cat -n templates/react/vite-react-template/src/Dashboard.jsx

printf '\n== README.md (relevant lines) ==\n'
rg -n "dashboard|route|react-router|SignedIn|SignedOut|SignInButton|SignOutButton" templates/react/vite-react-template/README.md

Repository: thunder-id/javascript-sdks

Length of output: 2305


Restore the signed-in Home state
templates/react/vite-react-template/src/Home.jsx now always shows SignInButton, so authenticated users no longer get any dashboard affordance on /. Bring back the SignedIn/SignedOut split or add a link/redirect to /dashboard.

🧰 Tools
🪛 ESLint

[error] 1-10: Missing WSO2 Apache 2.0 copyright header

(@thunderid/copyright-header)


[error] 1-1: Unable to resolve path to module '@thunderid/react'.

(import-x/no-unresolved)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/react/vite-react-template/src/Home.jsx` around lines 1 - 9, The
Home component currently always renders SignInButton, so authenticated users
never get the signed-in experience on the root route. Update Home in Home.jsx to
restore the SignedIn/SignedOut split using the existing auth components, or
otherwise add a dashboard link/redirect for signed-in users so / still provides
dashboard access. Keep the fix localized to the Home component and preserve the
current unauthenticated sign-in behavior.

1 change: 0 additions & 1 deletion templates/react/vite-react-template/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ createRoot(document.getElementById('root')).render(
<ThunderIDProvider
clientId={import.meta.env.VITE_THUNDERID_CLIENT_ID}
baseUrl={import.meta.env.VITE_THUNDERID_BASE_URL}
afterSignInUrl={`${window.location.origin}/callback`}
>
<App />
</ThunderIDProvider>
Expand Down
2 changes: 1 addition & 1 deletion templates/vue/vite-vue-template/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const baseUrl = import.meta.env.VITE_THUNDERID_BASE_URL
<main>
<SignedOut>
<h1>Welcome</h1>
<SignInButton />
<SignInButton>Sign In</SignInButton>
</SignedOut>
<SignedIn>
<User v-slot="{ user }">
Expand Down
Loading