diff --git a/samples/browser/quickstart/.env.example b/samples/browser/quickstart/.env.example
index bd724177..6a999c07 100644
--- a/samples/browser/quickstart/.env.example
+++ b/samples/browser/quickstart/.env.example
@@ -1,2 +1,5 @@
-VITE_THUNDERID_CLIENT_ID=your-client-id-here
+# Base URL of your ThunderID deployment (org tenant).
VITE_THUNDERID_BASE_URL=https://localhost:8090
+
+# OAuth2 Client ID for this app. ThunderID Console -> your application -> Overview.
+VITE_THUNDERID_CLIENT_ID=your-client-id-here
diff --git a/samples/browser/quickstart/README.md b/samples/browser/quickstart/README.md
index 4dd7245a..b17367fc 100644
--- a/samples/browser/quickstart/README.md
+++ b/samples/browser/quickstart/README.md
@@ -1,6 +1,6 @@
# ThunderID Browser Quickstart
-[](https://stackblitz.com/fork/github/thunder-id/javascript-sdks/tree/main/samples/browser/quickstart?file=.env&terminal=dev)
+
- This quickstart can't reach ThunderID yet. Set the following
- environment variable(s), then restart the dev server.
+ This quickstart can't reach ThunderID yet. Follow the steps below,
+ then restart the dev server.
- Copy
+ Copy
+ Sign-in requests from this origin will be blocked by the browser
+ until it's added to your ThunderID deployment's allowed CORS
+ origins. In the ThunderID Console, go to
+ Settings → CORS → Allowed origins and
+ add it.
+
+ This origin also doubles as this app's Authorized redirect URI
+ and Post-Logout Redirect URI. In the ThunderID
+ Console, open this application and go to
+ Advanced Settings → OAuth2 Configuration,
+ then add it to both fields below.
+
+ Need more info? Take a look at the
+ Browser quickstart guide.
A minimal Vite + vanilla JS app demonstrating sign-in and sign-out with the ThunderID JavaScript SDK (`@thunderid/browser`).
diff --git a/samples/browser/quickstart/scripts/prepare-dev.cjs b/samples/browser/quickstart/scripts/prepare-dev.cjs
index 6cd6ed3d..6a08904f 100644
--- a/samples/browser/quickstart/scripts/prepare-dev.cjs
+++ b/samples/browser/quickstart/scripts/prepare-dev.cjs
@@ -9,7 +9,12 @@ const root = path.join(__dirname, '..');
const envExample = path.join(root, '.env.example');
const envTarget = path.join(root, '.env');
if (fs.existsSync(envExample) && !fs.existsSync(envTarget)) {
- fs.copyFileSync(envExample, envTarget);
+ // Blank placeholder values (e.g. `your-client-id-here`) so the copied .env
+ // still trips the app's missing-env-var check until real values are filled in.
+ const envContent = fs
+ .readFileSync(envExample, 'utf8')
+ .replace(/^([A-Z0-9_]+)=(your-\S*|generate-with-\S*)$/gm, '$1=');
+ fs.writeFileSync(envTarget, envContent);
}
const pkgPath = path.join(root, 'package.json');
diff --git a/samples/browser/quickstart/src/main.js b/samples/browser/quickstart/src/main.js
index fac06fd1..f9822a4f 100644
--- a/samples/browser/quickstart/src/main.js
+++ b/samples/browser/quickstart/src/main.js
@@ -2,7 +2,7 @@ import './style.css'
import auth, { missingEnvVars } from './auth.js'
import { renderSignedOutNav, renderSignedInNav, attachNavHandlers, attachSignedOutNavHandlers } from './components/nav.js'
import { renderProfileDialog, attachProfileDialogHandlers } from './components/profileDialog.js'
-import { renderSignedOut, renderHome, renderConfigNeeded, startCountdown, attachSignedOutHandlers } from './pages/home.js'
+import { renderSignedOut, renderHome, renderConfigNeeded, startCountdown, attachSignedOutHandlers, attachConfigNeededHandlers } from './pages/home.js'
import { renderTokenDebug, attachTokenHandlers } from './pages/token.js'
let isDark = false
@@ -68,6 +68,7 @@ async function renderApp() {
if (missingEnvVars.length > 0) {
app.innerHTML = renderSignedOutNav({ isDark, hideSignIn: true }) + renderConfigNeeded(missingEnvVars)
attachSignedOutNavHandlers({ auth })
+ attachConfigNeededHandlers()
return
}
diff --git a/samples/browser/quickstart/src/pages/home.js b/samples/browser/quickstart/src/pages/home.js
index 7e23c672..24be3a57 100644
--- a/samples/browser/quickstart/src/pages/home.js
+++ b/samples/browser/quickstart/src/pages/home.js
@@ -139,6 +139,8 @@ export function attachSignedOutHandlers({ auth }) {
}
export function renderConfigNeeded(missing) {
+ const origin = typeof window !== 'undefined' ? window.location.origin : ''
+
return `
Configuration needed
- ${missing.map(key => `
- .env.example to .env.local, fill in the
- values from your ThunderID application, then run npm run dev again.
+
+
+ ${missing.map(key => `
+ .env.example to .env, fill in the
+ values from your ThunderID application, then run npm run dev again.
+ ${escapeHtml(origin)}
+
+ ${escapeHtml(origin)}
+
+ ${escapeHtml(origin)}
+
+
A minimal Express.js **API** protected by ThunderID access tokens, using the ThunderID JavaScript
SDK (`@thunderid/express`). Unlike the browser-focused quickstarts, this sample doesn't have a
diff --git a/samples/express/quickstart/index.mjs b/samples/express/quickstart/index.mjs
index 178014ed..7b8a0fb0 100644
--- a/samples/express/quickstart/index.mjs
+++ b/samples/express/quickstart/index.mjs
@@ -69,13 +69,39 @@ function renderConfigNeeded() {
This quickstart can't reach ThunderID yet. Set the following environment - variable(s), then restart the server.
-Copy .env.example to .env, fill in the values from
- your ThunderID application, then run npm run dev again.
This quickstart can't reach ThunderID yet. Follow the steps below, then + restart the server.
+ +Copy .env.example to .env, fill in the values from
+ your ThunderID application, then run npm run dev again.
Sign-in and sign-out are handled by this app's server, so no CORS + configuration is needed. In the ThunderID Console, open this application and go to + Advanced Settings → OAuth2 Configuration, then add the exact URIs below.
+http://localhost:3000/login
+ http://localhost:3000/logout
+ Need more info? Take a look at the + Express quickstart guide.
`, }); diff --git a/samples/express/quickstart/public/styles.css b/samples/express/quickstart/public/styles.css index d16c159f..26fffc19 100644 --- a/samples/express/quickstart/public/styles.css +++ b/samples/express/quickstart/public/styles.css @@ -671,6 +671,82 @@ button { font-size: 13px; } +.config-step { + width: 100%; + max-width: 420px; + margin: 0 auto 28px; +} + +.config-step-label { + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.09em; + color: var(--color-muted); + margin-bottom: 10px; + text-align: left; +} + +.config-step .config-list, +.config-step .config-hint { + margin-left: 0; + margin-right: 0; +} + +.config-box { + text-align: left; + background: var(--color-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-card); + padding: 16px; +} + +.config-box-body { + font-size: 13px; + color: var(--color-muted); + line-height: 1.6; + margin-bottom: 14px; +} + +.config-value-group { + display: flex; + flex-direction: column; + gap: 12px; +} + +.config-value-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-muted); + margin-bottom: 4px; +} + +.config-value { + display: block; + overflow-x: auto; + white-space: nowrap; + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-text); + background: rgba(54, 136, 255, 0.08); + border-radius: 4px; + padding: 8px 10px; +} + +.config-docs-note { + font-size: 13px; + color: var(--color-muted); + max-width: 420px; + margin: 0 auto; +} + +.config-docs-note a { + color: var(--color-primary); + font-weight: 600; +} + /* ── Token debug page ── */ .token-header { display: flex; diff --git a/samples/express/quickstart/scripts/prepare-dev.cjs b/samples/express/quickstart/scripts/prepare-dev.cjs index 6cd6ed3d..6a08904f 100644 --- a/samples/express/quickstart/scripts/prepare-dev.cjs +++ b/samples/express/quickstart/scripts/prepare-dev.cjs @@ -9,7 +9,12 @@ const root = path.join(__dirname, '..'); const envExample = path.join(root, '.env.example'); const envTarget = path.join(root, '.env'); if (fs.existsSync(envExample) && !fs.existsSync(envTarget)) { - fs.copyFileSync(envExample, envTarget); + // Blank placeholder values (e.g. `your-client-id-here`) so the copied .env + // still trips the app's missing-env-var check until real values are filled in. + const envContent = fs + .readFileSync(envExample, 'utf8') + .replace(/^([A-Z0-9_]+)=(your-\S*|generate-with-\S*)$/gm, '$1='); + fs.writeFileSync(envTarget, envContent); } const pkgPath = path.join(root, 'package.json'); diff --git a/samples/nextjs/quickstart/.env.example b/samples/nextjs/quickstart/.env.example index d1bf3e3d..43ae09b6 100644 --- a/samples/nextjs/quickstart/.env.example +++ b/samples/nextjs/quickstart/.env.example @@ -1,9 +1,27 @@ +# Base URL of your ThunderID deployment (org tenant). NEXT_PUBLIC_THUNDERID_BASE_URL=https://localhost:8090 -NEXT_PUBLIC_THUNDERID_CLIENT_ID=your-client-id-here + +# OAuth2 Client Secret for this app. Shown once when the application is created; +# if lost, regenerate it from the app's Credentials tab (under Edit). Server-only, never expose to the browser. +THUNDERID_CLIENT_SECRET=your-client-secret-here + +# Secret used to encrypt this app's session cookie. Generate locally, not from the console. +THUNDERID_SECRET=generate-with-openssl-rand-base64-32 + +# ── Native flow (default) ─────────────────────────────────────────────── +# Sign-in/sign-up render inline on this app's own routes below, with no +# redirect to ThunderID's hosted pages. Requires the three vars below. + +# Application ID (spId) for this app. ThunderID Console -> your application -> Overview. NEXT_PUBLIC_THUNDERID_APPLICATION_ID=your-application-id-here +# Local app route that renders the sign-in page. Not from the console. NEXT_PUBLIC_THUNDERID_SIGN_IN_URL=/signin +# Local app route that renders the sign-up page. Not from the console. NEXT_PUBLIC_THUNDERID_SIGN_UP_URL=/signup -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 + +# ── Redirect-based flow (opt-in) ──────────────────────────────────────── +# Uncomment to send the user to ThunderID's hosted sign-in page instead of +# the native flow above. Requires registering a redirect URI (see the app's +# config notice for the exact value), and replaces the three native-flow +# vars above entirely. +# NEXT_PUBLIC_THUNDERID_CLIENT_ID=your-client-id-here diff --git a/samples/nextjs/quickstart/README.md b/samples/nextjs/quickstart/README.md index dde93510..d45dea30 100644 --- a/samples/nextjs/quickstart/README.md +++ b/samples/nextjs/quickstart/README.md @@ -1,6 +1,6 @@ # ThunderID Next.js Quickstart -[](https://stackblitz.com/fork/github/thunder-id/javascript-sdks/tree/main/samples/nextjs/quickstart?file=.env.local&terminal=dev) +- This quickstart can't reach ThunderID yet. Set the following - environment variable(s), then restart the dev server. + This quickstart can't reach ThunderID yet. Follow the steps + below, then restart the dev server.
-
- Copy .env.example to .env.local, fill in the
- values from your ThunderID application, then run npm run dev again.
+
+ Copy .env.example to .env, fill in the
+ values from your ThunderID application, then run npm run dev again.
+
+ Sign-in and sign-out are handled by this app's server, so + no CORS configuration is needed. In the{' '} + ThunderID Console, open this application and go + to Advanced Settings → OAuth2 Configuration, + then add the exact URIs below. +
+ +http://localhost:3000
+ http://localhost:3000
+ + Need more info? Take a look at the{' '} + + Next.js quickstart guide. +
diff --git a/samples/nextjs/quickstart/app/globals.css b/samples/nextjs/quickstart/app/globals.css index 62bbaf09..918b551c 100644 --- a/samples/nextjs/quickstart/app/globals.css +++ b/samples/nextjs/quickstart/app/globals.css @@ -383,6 +383,82 @@ button:focus-visible { font-size: 13px; } +.config-step { + width: 100%; + max-width: 420px; + margin: 0 auto 28px; +} + +.config-step-label { + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.09em; + color: var(--color-text-muted); + margin-bottom: 10px; + text-align: left; +} + +.config-step .config-list, +.config-step .config-hint { + margin-left: 0; + margin-right: 0; +} + +.config-box { + text-align: left; + background: var(--color-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-card); + padding: 16px; +} + +.config-box-body { + font-size: 13px; + color: var(--color-text-muted); + line-height: 1.6; + margin-bottom: 14px; +} + +.config-value-group { + display: flex; + flex-direction: column; + gap: 12px; +} + +.config-value-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-text-muted); + margin-bottom: 4px; +} + +.config-value { + display: block; + overflow-x: auto; + white-space: nowrap; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + color: var(--color-text); + background: rgba(54, 136, 255, 0.1); + border-radius: 4px; + padding: 8px 10px; +} + +.config-docs-note { + font-size: 13px; + color: var(--color-text-muted); + max-width: 420px; + margin: 0 auto; +} + +.config-docs-note a { + color: var(--color-primary); + font-weight: 600; +} + /* ── User / welcome card ── */ .user-card { display: flex; diff --git a/samples/nextjs/quickstart/app/layout.tsx b/samples/nextjs/quickstart/app/layout.tsx index 77535c2a..d8172a5c 100644 --- a/samples/nextjs/quickstart/app/layout.tsx +++ b/samples/nextjs/quickstart/app/layout.tsx @@ -10,11 +10,19 @@ export const metadata: Metadata = { description: 'ThunderID authentication with Next.js', } +// Redirect-based flow (NEXT_PUBLIC_THUNDERID_CLIENT_ID set) sends the user to +// ThunderID's hosted pages and needs a registered redirect URI. The default, +// native flow renders sign-in/sign-up inline via the app's own routes and +// needs an application ID instead — no redirect URI or CORS setup required. +const isRedirectFlow = Boolean(process.env.NEXT_PUBLIC_THUNDERID_CLIENT_ID) + const REQUIRED_ENV_VARS = [ 'NEXT_PUBLIC_THUNDERID_BASE_URL', - 'NEXT_PUBLIC_THUNDERID_CLIENT_ID', 'THUNDERID_CLIENT_SECRET', 'THUNDERID_SECRET', + ...(isRedirectFlow + ? ['NEXT_PUBLIC_THUNDERID_CLIENT_ID'] + : ['NEXT_PUBLIC_THUNDERID_APPLICATION_ID', 'NEXT_PUBLIC_THUNDERID_SIGN_IN_URL', 'NEXT_PUBLIC_THUNDERID_SIGN_UP_URL']), ] export default function RootLayout({ @@ -28,7 +36,7 @@ export default function RootLayout({ {missingEnvVars.length > 0 ? ( -- This quickstart can't reach ThunderID yet. Set the following - environment variable(s), then restart the dev server. + This quickstart can't reach ThunderID yet. Follow the steps below, + then restart the dev server.
-
+ Copy .env.example to .env, fill in the
+ values from your ThunderID application, then run npm run dev again.
+
+ Sign-in requests from this origin will be blocked by the browser + until it's added to your ThunderID deployment's allowed CORS + origins. In the ThunderID Console, go to + Settings → CORS → Allowed origins and + add it. +
+ +{{ origin }}
+
+ + This origin also doubles as this app's Authorized redirect URI + and Post-Logout Redirect URI. In the ThunderID + Console, open this application and go to + Advanced Settings → OAuth2 Configuration, + then add it to both fields below. +
+ +{{ origin }}
+
+ {{ origin }}
+
+
- Copy .env.example to .env.local, fill in the
- values from your ThunderID application, then run npm run dev again.
+
+ Need more info? Take a look at the + Vue quickstart guide.
diff --git a/samples/vue/quickstart/src/style.css b/samples/vue/quickstart/src/style.css index d0a9d2db..745f1636 100644 --- a/samples/vue/quickstart/src/style.css +++ b/samples/vue/quickstart/src/style.css @@ -332,6 +332,89 @@ body { font-size: 13px; } +.config-step { + width: 100%; + max-width: 420px; + margin: 0 auto 28px; +} + +.config-step-label { + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.09em; + color: var(--color-muted); + margin-bottom: 10px; + text-align: left; +} + +.config-step .config-list, +.config-step .config-hint { + margin-left: 0; + margin-right: 0; +} + +.config-box { + text-align: left; + background: var(--color-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-card); + padding: 16px; +} + +.config-box-body { + font-size: 13px; + color: var(--color-muted); + line-height: 1.6; + margin-bottom: 14px; +} + +.config-value-row { + display: flex; + align-items: center; + gap: 8px; +} + +.config-value { + flex: 1; + min-width: 0; + overflow-x: auto; + white-space: nowrap; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + color: var(--color-text); + background: rgba(54, 136, 255, 0.08); + border-radius: 4px; + padding: 8px 10px; +} + +.config-value-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-muted); + margin-bottom: 4px; +} + +.config-value-group { + display: flex; + flex-direction: column; + gap: 12px; +} + +.config-docs-note { + font-size: 13px; + color: var(--color-muted); + max-width: 420px; + margin: 0 auto; +} + +.config-docs-note a { + color: var(--color-primary); + font-weight: 600; +} + /* ─── Signed-in home page ───────────────────────────────────────────── */ .home-shell { min-height: calc(100vh - var(--nav-height));