Accessing Protected APIs
~5 minThis guide shows how to retrieve the user's access token from the ThunderID Node.js SDK and use it to call protected downstream APIs.
Accessing Protected APIs
Prerequisites
- Complete the [Handling Authentication](../handling-authentication) guide to set up the client and session management. - The user must be signed in before you retrieve an access token.
Retrieve the Access Token
Use the `getAccessToken` method to retrieve the access token for the current user session.
export async function fetchUserProfile(sessionId: string) {const accessToken = await auth.getAccessToken(sessionId)const response = await fetch('https://localhost:8090/users/<user_id>', {headers: {Authorization: `Bearer ${accessToken}`,'Content-Type': 'application/json',},})if (!response.ok) {throw new Error(`API request failed: ${response.status}`)}return response.json()}
Call a Protected API from a Route
Use the service function in a route handler that already requires authentication.
app.get('/profile', requireAuth, async (req, res) => {const sessionId = req.cookies[CookieConfig.SESSION_COOKIE_NAME]try {const profile = await fetchUserProfile(sessionId)res.render('profile', { profile })} catch (error) {res.status(500).render('error', { message: 'Failed to load profile.' })}})
Send Multiple Requests in Parallel
Use `Promise.all` to send multiple authenticated requests concurrently.
export async function fetchDashboardData(sessionId: string) {const accessToken = await auth.getAccessToken(sessionId)const headers = {Authorization: `Bearer ${accessToken}`,'Content-Type': 'application/json',}const [profile, organizations] = await Promise.all([fetch('https://localhost:8090/users/<user_id>', { headers }).then(r => r.json()),fetch('https://localhost:8090/organizations', { headers }).then(r => r.json()),])return { organizations, profile }}
Handle Token Expiry
The SDK refreshes the access token automatically when `isSignedIn` is called (for example, in your authentication middleware). This means that by the time a route handler executes, the access token is already up to date. If you call `getAccessToken` outside a middleware-protected route, verify the session first:
const signedIn = await auth.isSignedIn(sessionId)if (!signedIn) {throw new Error('User is not signed in.')}const accessToken = await auth.getAccessToken(sessionId)
Use the Token Exchange Flow
For advanced scenarios such as impersonation or delegated access, use `exchangeToken` to get a token with a different scope or for a different subject.
const newTokens = await auth.exchangeToken({attachToken: false,data: {client_id: '<your-client-id>',grant_type: 'account_switch',scope: 'openid profile',token: currentAccessToken,},id: 'account-switch',returnResponse: true,returnsSession: true,signInRequired: true,},sessionId,)
Revoke an Access Token
To revoke the access token when the user signs out or when you want to invalidate a session immediately, call `revokeAccessToken`.
await auth.revokeAccessToken(sessionId)
Next Steps
- [Protecting Routes](../protecting-routes): Restrict access to routes based on authentication state - [`ThunderIDNodeClient`](../../apis/clients/thunderid-node-client): Base client class reference