Accessing Protected APIs
When building applications with ThunderID, you'll often need to call protected APIs that require authentication. This guide shows you how to make authenticated HTTP requests using the ThunderID React SDK.
Using SDK Built-in HTTP Client
When your application is wrapped with ThunderIDProvider, you can use the useThunderID hook to access the authenticated http module. This module has the following features:
- Includes the necessary authentication headers (Bearer token)
- Handles token refresh when tokens expire
- Provides methods like
request()andrequestAll()for making API calls
You can access the http client in two ways:
-
Inside a component: Use the
useThunderIDhook to get thehttpinstanceconst { http } = useThunderID() -
Outside a component (e.g., in utility functions or services): Import
httpdirectlyimport { http } from '@thunderid/react'
Basic API Request
The following examples show how to use the ThunderID SDK's http module to call a protected API endpoint.
Using the Hook Inside a Component
import React, { useEffect, useState } from 'react'
import { useThunderID } from '@thunderid/react'
export default function UserProfile() {
const { http, isSignedIn } = useThunderID()
const [userData, setUserData] = useState(null)
useEffect(() => {
if (!isSignedIn) {
return
}
(async () => {
try {
const response = await http.request({
url: 'https://localhost:8090/users/<user_id>',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'GET',
})
setUserData(response.data)
} catch (error) {
console.error('Error fetching user data:', error)
}
})()
}, [http, isSignedIn])
if (!isSignedIn) {
return <div>Please sign in to view your profile.</div>
}
return (
<div>
<h2>User Profile</h2>
{userData && <pre>{JSON.stringify(userData, null, 2)}</pre>}
</div>
)
}
Using Direct Import Outside a Component
import { http } from '@thunderid/react'
export async function fetchUser(userId) {
try {
const response = await http.request({
url: `https://localhost:8090/users/${userId}`,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'GET',
})
return response.data
} catch (error) {
console.error('Error fetching user:', error)
throw error
}
}
import React, { useEffect, useState } from 'react'
import { useThunderID } from '@thunderid/react'
import { fetchUser } from './services/userService'
export default function UserProfile() {
const { isSignedIn } = useThunderID()
const [userData, setUserData] = useState(null)
useEffect(() => {
if (!isSignedIn) {
return
}
(async () => {
try {
const data = await fetchUser('<user_id>')
setUserData(data)
} catch (error) {
console.error('Error loading user data:', error)
}
})()
}, [isSignedIn])
if (!isSignedIn) {
return <div>Please sign in to view your profile.</div>
}
return (
<div>
<h2>User Profile</h2>
{userData && <pre>{JSON.stringify(userData, null, 2)}</pre>}
</div>
)
}
Replace <user_id> with the actual user ID you want to fetch. The ThunderID API server runs on https://localhost:8090 by default.
The storage type must be set to webWorker for the token to be automatically attached. If it's set to sessionStorage or localStorage, you may implement your own function for attaching the access token to the network request.
Note that you don't need to manually specify the Authorization header, as the http function intercepts the request and attaches the access token automatically. The final request config sent by the http function would be:
Parallel API Requests
To send multiple API requests in parallel, use the httpRequestAll function. It triggers parallel network requests and returns responses after all requests are completed.
The following code snippet shows how to send multiple network requests in parallel:
import React, { useEffect, useState } from 'react'
import { useThunderID } from '@thunderid/react'
export default function UserProfile() {
const { http, isSignedIn } = useThunderID()
const [ userData, setUserData ] = useState({
profile: null,
applications: [],
})
useEffect(() => {
if (!isSignedIn) {
return
}
const requests = []
requests.push({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'GET',
url: 'https://localhost:8090/users/<user_id>',
})
requests.push({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'GET',
url: 'https://localhost:8090/applications',
})
(async () => {
try {
const response = await http.requestAll(requests)
setUserData({
profile: response[0].data,
applications: response[1].data,
})
} catch (error) {
console.error('Error fetching data:', error)
}
})()
}, [http, isSignedIn])
return <pre>{JSON.stringify(userData, null, 4)}</pre>
}
Using a GraphQL Client
If you are not using webWorker as the storage type, use the getAccessToken function to fetch the access token. Then manually attach the token to the GraphQL client's authentication headers.
This approach is not available when the storage type is set to webWorker. The SDK automatically manages the access token and does not expose it to the main thread.
The following example shows how to configure a GraphQL client with authentication using the access token:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
export function createAuthenticatedClient(accessToken) {
const httpLink = createHttpLink({
uri: 'https://localhost:8090/graphql',
})
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : '',
},
}
})
return new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
}
import React, { useEffect, useState } from 'react'
import { useThunderID } from '@thunderid/react'
import { gql } from '@apollo/client'
import { createAuthenticatedClient } from './graphql/client'
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
email
username
firstName
lastName
}
}
`
export default function UserProfile() {
const { isSignedIn, getAccessToken } = useThunderID()
const [userData, setUserData] = useState(null)
useEffect(() => {
if (!isSignedIn) {
return
}
(async () => {
try {
const accessToken = await getAccessToken()
const client = createAuthenticatedClient(accessToken)
const { data } = await client.query({
query: GET_USER,
variables: { id: '<user_id>' },
})
setUserData(data.user)
} catch (error) {
console.error('Error fetching user data:', error)
}
})()
}, [isSignedIn, getAccessToken])
if (!isSignedIn) {
return <div>Please sign in to view your profile.</div>
}
return (
<div>
<h2>User Profile</h2>
{userData && <pre>{JSON.stringify(userData, null, 2)}</pre>}
</div>
)
}
Replace <user_id> with the actual user ID you want to fetch. This example uses Apollo Client, but you can apply the same pattern with other GraphQL clients like urql or graphql-request.