Refreshing Credentials
next-firebase-auth-edge provides three different functions for updating user credentials after changes to the user token structure (e.g., adding new user claims).
Refresh Credentials in Middleware
To refresh credentials in Next.js Middleware (opens in a new tab) after updating the user token, use the refreshCredentials function from next-firebase-auth-edge/lib/next/cookies.
import type {NextRequest} from 'next/server';
import {authMiddleware} from 'next-firebase-auth-edge';
import {refreshCredentials} from 'next-firebase-auth-edge/lib/next/cookies';
const commonOptions = {
apiKey: 'XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX',
cookieName: 'AuthToken',
cookieSignatureKeys: ['Key-Should-Be-at-least-32-bytes-in-length'],
cookieSerializeOptions: {
path: '/',
httpOnly: true,
secure: false, // Set this to true on HTTPS environments
sameSite: 'strict' as const,
maxAge: 12 * 60 * 60 * 24 // twelve days
},
serviceAccount: {
projectId: 'your-firebase-project-id',
clientEmail:
'firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n'
}
};
export async function middleware(request: NextRequest) {
return authMiddleware(request, {
handleValidToken: async ({decodedToken}, headers) => {
const shouldRefreshCredentials =
await makeSomeComputationsToDeduceIfUserCredentialsShouldBeUpdated(
decodedToken.uid
);
if (shouldRefreshCredentials) {
return refreshCredentials(
request,
commonOptions,
({headers, tokens}) => {
// Optionally perform additional verification on refreshed `tokens`...
return NextResponse.next({
request: {
headers
}
});
}
);
}
return NextResponse.next({
request: {
headers
}
});
},
...commonOptions
});
}
export const config = {
matcher: [
'/',
'/((?!_next|favicon.ico|api|.*\\.).*)',
'/api/login',
'/api/logout'
]
};refreshCredentials
The refreshCredentials function is useful when you need to update user credentials after some asynchronous action that affects the user token structure (e.g., a cron job or event queue that updates custom claims).
In this example, makeSomeComputationsToDeduceIfUserCredentialsShouldBeUpdated is a fictional function used to quickly check if user credentials need updating. For example, it might check a distributed cache.
When you call refreshCredentials, it performs three actions:
- It generates a new token based on the existing credentials, including any updated claims.
- It allows the developer to create a new
NextResponsewith Modified Request Headers (opens in a new tab). Passing the modified request headers ensures thatgetTokenswill return the fresh token within a single request. - It updates the
NextResponsewithSet-Cookieheaders that contain the latest credentials.
Here’s the function signature:
async function refreshCredentials<Metadata extends object>(
request: NextRequest,
options: SetAuthCookiesOptions<Metadata>,
responseFactory: (options: {
headers: Headers;
tokens: VerifiedCookies;
metadata: Metadata;
}) => NextResponse | Promise<NextResponse>
): Promise<NextResponse>;Response Factory Options
| Name | Description |
|---|---|
| headers | Modified Request Headers (opens in a new tab). Passing these modified headers ensures that getTokens will return the updated token within a single request. |
| tokens | A VerifiedCookies object, containing the values for idToken, refreshToken, and decodedIdToken. It also returns customToken if you passed enableCustomToken flag to authMiddleware. |
| metadata | A Metadata object. Metadata is whatever getMetadata callback provided to authMiddleware resolves with. |
Refresh Auth Cookies in API Route Handlers
To refresh authentication cookies after updating a user token in API Route Handlers (opens in a new tab), use refreshNextResponseCookies from next-firebase-auth-edge/lib/next/cookies.
import {NextResponse} from 'next/server';
import type {NextRequest} from 'next/server';
import {refreshNextResponseCookies} from 'next-firebase-auth-edge/lib/next/cookies';
import {getFirebaseAuth, getTokens} from 'next-firebase-auth-edge';
const commonOptions = {
apiKey: 'XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX',
cookieName: 'AuthToken',
cookieSignatureKeys: ['Key-Should-Be-at-least-32-bytes-in-length'],
cookieSerializeOptions: {
path: '/',
httpOnly: true,
secure: false, // Set this to true on HTTPS environments
sameSite: 'strict' as const,
maxAge: 12 * 60 * 60 * 24 // twelve days
},
serviceAccount: {
projectId: 'your-firebase-project-id',
clientEmail:
'firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n'
}
};
const {setCustomUserClaims, getUser} = getFirebaseAuth({
serviceAccount: commonOptions.serviceAccount,
apiKey: commonOptions.apiKey
});
export async function POST(request: NextRequest) {
const tokens = await getTokens(request.cookies, commonOptions);
if (!tokens) {
throw new Error('Cannot update custom claims of unauthenticated user');
}
await setCustomUserClaims(tokens.decodedToken.uid, {
someCustomClaim: {
updatedAt: Date.now()
}
});
const user = await getUser(tokens.decodedToken.uid);
const response = new NextResponse(
JSON.stringify({
customClaims: user.customClaims
}),
{
status: 200,
headers: {'content-type': 'application/json'}
}
);
return refreshNextResponseCookies(request, response, commonOptions);
}Refresh Auth Cookies in API Route Handlers with an ID Token Extracted from the Authorization Header
To refresh authentication cookies using the token string extracted from the Authorization header, use refreshNextResponseCookiesWithToken from next-firebase-auth-edge/lib/next/cookies.
import type {NextRequest} from 'next/server';
import {refreshNextResponseCookiesWithToken} from 'next-firebase-auth-edge/lib/next/cookies';
const commonOptions = {
apiKey: 'XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX',
cookieName: 'AuthToken',
cookieSignatureKeys: ['Key-Should-Be-at-least-32-bytes-in-length'],
cookieSerializeOptions: {
path: '/',
httpOnly: true,
secure: false, // Set this to true on HTTPS environments
sameSite: 'strict' as const,
maxAge: 12 * 60 * 60 * 24 // twelve days
},
serviceAccount: {
projectId: 'your-firebase-project-id',
clientEmail:
'firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n'
}
};
export async function POST(request: NextRequest) {
const token = request.headers.get('Authorization')?.split(' ')[1] ?? '';
if (!token) {
throw new Error('Unauthenticated');
}
return refreshNextResponseCookiesWithToken(
token,
request,
response,
commonOptions
);
}Refresh Auth Cookies in Pages Router API Routes
To refresh authentication cookies after updating a user token in API Routes (opens in a new tab), use refreshApiResponseCookies from next-firebase-auth-edge/lib/next/api.
import {NextApiRequest, NextApiResponse} from 'next';
import {refreshApiResponseCookies} from 'next-firebase-auth-edge/lib/next/api';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await refreshApiResponseCookies(req, res, {
serviceAccount: {
projectId: 'your-firebase-project-id',
clientEmail:
'firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com',
privateKey:
'-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n'
},
apiKey: 'XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX',
cookieName: 'AuthToken',
cookieSignatureKeys: ['Key-Should-Be-at-least-32-bytes-in-length'],
cookieSerializeOptions: {
path: '/',
httpOnly: true,
secure: false, // Set this to true on HTTPS environments
sameSite: 'strict' as const,
maxAge: 12 * 60 * 60 * 24 // twelve days
}
});
res.status(200).json({example: true});
}Refresh Auth Cookies in Server Actions
To refresh authentication cookies after updating user credentials in Server Actions (opens in a new tab), use refreshServerCookies from next-firebase-auth-edge/lib/next/cookies.
'use server';
import {cookies, headers} from 'next/headers';
import {getTokens} from 'next-firebase-auth-edge';
import {refreshServerCookies} from 'next-firebase-auth-edge/lib/next/cookies';
const commonOptions = {
apiKey: 'XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX',
cookieName: 'AuthToken',
cookieSignatureKeys: ['Key-Should-Be-at-least-32-bytes-in-length'],
cookieSerializeOptions: {
path: '/',
httpOnly: true,
secure: false, // Set this to true on HTTPS environments
sameSite: 'strict' as const,
maxAge: 12 * 60 * 60 * 24 // twelve days
},
serviceAccount: {
projectId: 'your-firebase-project-id',
clientEmail:
'firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n'
}
};
export async function performServerAction() {
// Since Next.js 15, `cookies` function returns a Promise, so we need to precede it with `await`.
const tokens = await getTokens(await cookies(), commonOptions);
if (!tokens) {
throw new Error('Unauthenticated');
}
// headers() needs to be wrapped with new Headers() to work in Server Actions
// Since Next.js 15, `cookies` and `headers` functions return a Promise, so we need to precede them with `await`.
await refreshServerCookies(
await cookies(),
new Headers(await headers()),
commonOptions
);
}