| name | integrate-node |
| description | Add ThunderID authentication to a Node.js application using the official @thunderid/node SDK. Use when asked to "add ThunderID to my Node.js app", "integrate ThunderID with Fastify", "add ThunderID to Hono", or any Node.js server framework without a dedicated ThunderID SDK. For Express specifically, prefer /integrate-express. Use when this capability is needed. |
| metadata | {"author":"brionmario"} |
ThunderID — Node.js Integration
Assumes ThunderID is running at https://localhost:8090. If not, run /setup-thunderid first.
@thunderid/node is the generic Node.js SDK — use it with Fastify, Hono, Koa, or any other Node.js framework. For Express, use /integrate-express instead.
Step 1 — Register an Application
- Open
https://localhost:8090/console and sign in as admin / admin
- Navigate to Applications → New Application
- Fill in:
- Name: your app name
- Type: Web Application
- Authorized Redirect URL:
http://localhost:3000/callback
- Copy the Client ID and Client Secret
Via the API
First obtain a system API token from the ThunderID console, then:
curl -kL -X POST https://localhost:8090/applications \
-H 'Authorization: Bearer <your-system-token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "my-node-app",
"inboundAuthConfig": [{
"type": "oauth2",
"config": {
"grantTypes": ["authorization_code", "refresh_token"],
"responseTypes": ["code"],
"redirectUris": ["http://localhost:3000/callback"],
"tokenEndpointAuthMethod": "client_secret_basic",
"publicClient": false,
"pkceRequired": true
}
}]
}'
Step 2 — Install
Detect the package manager from lockfiles: pnpm-lock.yaml → pnpm add, yarn.lock → yarn add, bun.lockb → bun add, else npm install.
npm install @thunderid/node
Step 3 — Create a Client
import { createThunderID } from '@thunderid/node'
export const thunderid = createThunderID({
clientId: '<your-client-id>',
clientSecret: '<your-client-secret>',
baseUrl: 'https://localhost:8090',
redirectUri: 'http://localhost:3000/callback',
})
Step 4 — Wire Up Auth Routes
Login route — redirect the user to ThunderID:
app.get('/login', async (req, reply) => {
const { url, codeVerifier, state } = await thunderid.createAuthorizationUrl()
req.session.codeVerifier = codeVerifier
req.session.oauthState = state
reply.redirect(url)
})
Callback route — exchange the code for tokens:
app.get('/callback', async (req, reply) => {
const { code, state } = req.query as { code: string; state: string }
const tokens = await thunderid.exchangeCode({
code,
codeVerifier: req.session.codeVerifier,
state,
expectedState: req.session.oauthState,
})
req.session.user = await thunderid.getUserInfo(tokens.accessToken)
reply.redirect('/')
})
Logout route:
app.get('/logout', async (req, reply) => {
req.session.destroy()
const logoutUrl = thunderid.createLogoutUrl({ postLogoutRedirectUri: 'http://localhost:3000' })
reply.redirect(logoutUrl)
})
Step 5 — Protect Routes
Verify a token from an Authorization header (API routes):
app.get('/api/profile', async (req, reply) => {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return reply.code(401).send({ error: 'Unauthorized' })
const user = await thunderid.verifyToken(token)
reply.send({ user })
})
Check session (web routes):
app.get('/dashboard', async (req, reply) => {
if (!req.session.user) return reply.redirect('/login')
reply.send(`Welcome, ${req.session.user.name}`)
})
Troubleshooting
Certificate error — Set NODE_TLS_REJECT_UNAUTHORIZED=0 in .env for local development (remove before deploying).
invalid_grant — The codeVerifier in the callback must match what was stored during the login redirect.
invalid_client — Double-check the Client ID and Client Secret.
Source: brionmario/thunderid-skills-poc — distributed by TomeVault.