Using Client-Side APIs
The starter example uses the inMemoryPersistence (opens in a new tab) strategy, relying entirely on server-side tokens. This avoids consistency issues on the client side.
While this approach is recommended, it can lead to a few challenges:
- Stale tokens: In long-running client sessions, server-side tokens may expire, requiring the user to refresh the page to get a valid token. This typically happens if the user reopens a tab after about an hour.
- Unauthenticated Firebase Client SDK environment: With
inMemoryPersistence
,currentUser
will often benull
when using client-side APIs (opens in a new tab), preventing the use of Firebase’s client-side SDKs.
However, next-firebase-auth-edge
includes several features that address these issues:
Enable Refresh Token API Endpoint in Auth Middleware
In long-running client sessions (e.g., when a user reopens a tab after an hour), the server-side token may expire. This can cause problems when validating external API calls or when using the customToken
with Firebase's signInWithCustomToken
.
To resolve this, you can expose an endpoint via authMiddleware
to refresh client-side tokens when the server-side token has expired.
To enable this endpoint, define the refreshTokenPath
option in the middleware.
export async function middleware(request: NextRequest) {
return authMiddleware(request, {
loginPath: '/api/login',
logoutPath: '/api/logout',
refreshTokenPath: '/api/refresh-token'
// other options...
});
}
export const config = {
// Make sure to include the path in `matcher`
matcher: [
'/api/login',
'/api/logout',
'/api/refresh-token',
'/',
'/((?!_next|favicon.ico|api|.*\\.).*)'
]
};
Calling /api/refresh-token
will:
- Check if the current token has expired. If it has, it regenerates the token and updates the cookies with a
Set-Cookie
header containing the fresh token. - Return JSON with a valid
idToken
. It can also returncustomToken
, ifenableCustomToken
is set totrue
inauthMiddleware
.
getValidIdToken
The getValidIdToken
function works in conjunction with the refresh token endpoint to ensure you have the latest valid ID token. This is helpful if you use tokens to authorize external API calls.
It requires serverIdToken
, which is the token
returned by the getTokens function in server components.
The function is optimized to be fast and safe for repeated calls. The /api/refresh-token
endpoint will only be called if the token has expired.
Example usage:
import {getValidIdToken} from 'next-firebase-auth-edge/lib/next/client';
export async function fetchSomethingFromExternalApi(serverIdToken: string) {
const idToken = await getValidIdToken({
serverIdToken,
refreshTokenUrl: '/api/refresh-token'
});
return fetch('https://some-external-api.com/api/example', {
method: 'GET',
headers: {
Authorization: `Bearer ${idToken}`
}
});
}
getValidCustomToken
Please note that since v1.8 custom token is disabled by default. In order to enable custom cookies, pass enableCustomToken: true
option to authMiddleware
.
Custom token introduces significant footprint on authentication cookie and is not required for most use-cases.
If you want to avoid cookie size issues, learn how to split session into multiple cookies
Similar to getValidIdToken
, the getValidCustomToken
function works with the refresh token endpoint to provide a valid custom token. This is useful when using the custom token with Firebase’s signInWithCustomToken (opens in a new tab) method.
It requires serverCustomToken
, which is the customToken
returned by the getTokens function in server components.
Like getValidIdToken
, this function is designed to be efficient and only calls the /api/refresh-token
endpoint if necessary.
Example usage:
export async function signInWithServerCustomToken(serverCustomToken: string) {
const auth = getAuth(getFirebaseApp());
const customToken = await getValidCustomToken({
serverCustomToken,
refreshTokenUrl: '/api/refresh-token'
});
if (!customToken) {
throw new Error('Invalid custom token');
}
return signInWithCustomToken(auth, customToken);
}
Using Firebase Client SDKs
The Firebase Client SDK exposes the signInWithCustomToken (opens in a new tab) method, which allows you to access the current user using a custom token.
You can obtain a custom token by calling the getTokens function in server components.
import {signInWithCustomToken} from 'firebase/auth';
import {getValidCustomToken} from 'next-firebase-auth-edge/lib/next/client';
import {doc, getDoc, getFirestore, updateDoc, setDoc} from 'firebase/firestore';
export async function doSomethingWithFirestoreClient(
serverCustomToken: string
) {
const auth = getAuth(getFirebaseApp());
// See https://next-firebase-auth-edge-docs.vercel.app/docs/usage/client-side-apis#getvalidcustomtoken
const customToken = await getValidCustomToken({
serverCustomToken,
refreshTokenUrl: '/api/refresh-token'
});
if (!customToken) {
throw new Error('Invalid custom token');
}
const {user: firebaseUser} = await signInWithCustomToken(auth, customToken);
// Use client-side firestore instance
const db = getFirestore(getApp());
}