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
NextResponse
with Modified Request Headers (opens in a new tab). Passing the modified request headers ensures thatgetTokens
will return the fresh token within a single request. - It updates the
NextResponse
withSet-Cookie
headers that contain the latest credentials.
Here’s the function signature:
async function refreshCredentials(
request: NextRequest,
options: SetAuthCookiesOptions,
responseFactory: (options: {
headers: Headers;
tokens: VerifiedTokens;
}) => 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 VerifiedTokens object, containing the values for idToken , refreshToken , and decodedIdToken . It also returns customToken if you passed enableCustomToken flag to authMiddleware . |
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);
}