Node.js Quickstart
Use this guide to authenticate a Node.js service to ThunderID using the @thunderid/node SDK. Unlike the other quickstarts in this section, there's no user and no browser sign-in: the service authenticates as itself with the OAuth 2.0 client_credentials grant, then uses the resulting access token to call another piece of business logic. Use this pattern for background jobs, cron tasks, or one backend service calling another, where there's no human in the loop to redirect through a login page.
What You Will Learn
- Create a Node.js project
- Install the
@thunderid/nodepackage
- Authenticate a service with the
client_credentialsgrant
- Use the resulting access token to call business logic
Prerequisites
- About 10 minutes
- Node.js 18+ installed on your system
- npm, yarn, or pnpm
- Your preferred code editor
Check out the complete Node.js Quickstart Sample in the ThunderID repository.
Run ThunderID
Start a local ThunderID instance. Pick the method that works best for you:
Requires Node.js 18+
Full install guide →Once it's running, the console is available at https://localhost:8090/console.
Create an Agent
Agents are ThunderID's machine identities, distinct from user-facing applications. A background service like this one authenticates as an agent, not as an application.
-
Sign in to the Console.
Test UserIf you used the default setup, sign in to the Console as
adminwith the password generated during setup and printed to the setup output (unless you supplied your own). -
Navigate to Agents and click Add Agent.
-
Enter an Agent name (e.g.
node-service-quickstart) and select an Owner, then click Create agent. -
ThunderID displays the agent's Client Secret once. Copy it now; it cannot be retrieved again.
-
Open the agent's Advanced Settings tab, enable the
client_credentialsgrant type, and set the Client authentication method toclient_secret_basic. -
Copy the Client ID from the General tab.
Create a Node.js Project
Initialize a new Node.js project:
npm
Yarn
pnpm
mkdir my-node-service
cd my-node-service
npm init -y
mkdir my-node-service
cd my-node-service
yarn init -y
mkdir my-node-service
cd my-node-service
pnpm init
Install @thunderid/node
Install the ThunderID Node.js SDK:
npm
Yarn
pnpm
npm install @thunderid/node
yarn add @thunderid/node
pnpm add @thunderid/node
Authenticate as the Service
Create an index.mjs file and initialize ThunderIDNodeClient with grantType: 'client_credentials'. With that set, getAccessToken() authenticates as the service itself, no session and no sign-in, and transparently fetches, caches, and refreshes the token.
import { ThunderIDNodeClient } from '@thunderid/node';
const client = new ThunderIDNodeClient();
await client.initialize({
baseUrl: 'https://localhost:8090',
clientId: '<your-agent-client-id>',
clientSecret: '<your-agent-client-secret>',
grantType: 'client_credentials',
});
const accessToken = await client.getAccessToken();
const { scope } = await client.decodeJwtToken(accessToken);
console.log(`Authenticated with scope: ${scope}`);
Replace <your-agent-client-id> and <your-agent-client-secret> with the Client ID and Client Secret from your ThunderID agent.
Configuration Parameters
| Parameter | Description |
|---|---|
baseUrl | Your ThunderID instance URL (e.g., https://localhost:8090) |
clientId | The Client ID from your ThunderID agent |
clientSecret | The Client Secret from your ThunderID agent |
grantType | Set to 'client_credentials' to authenticate the service as itself, with no user and no browser redirect |
Call Business Logic With the Token
A real backend keeps authentication in its own module instead of scattering it across business logic. Update index.mjs so the rest of the app asks for a token only when it needs one, instead of handling getAccessToken() and the Authorization header directly:
import { ThunderIDNodeClient } from '@thunderid/node';
const client = new ThunderIDNodeClient();
await client.initialize({
baseUrl: 'https://localhost:8090',
clientId: '<your-agent-client-id>',
clientSecret: '<your-agent-client-secret>',
grantType: 'client_credentials',
});
async function getStock(sku) {
const accessToken = await client.getAccessToken();
// A real backend would attach this as `Authorization: Bearer ${accessToken}`
// on a request to a separate inventory service. This quickstart just proves
// the token was obtained before answering.
console.log(`Requesting ${sku} with a valid access token`);
return { sku, inStock: true };
}
const item = await getStock('SKU-100');
console.log(item);
Run Your Service
Start the script:
npm
Yarn
pnpm
node index.mjs
yarn node index.mjs
pnpm node index.mjs
The script authenticates once, prints the scope it received, then calls getStock() and prints the result before exiting.