I've setup a simple Nuxt3 server api route that calls a third party api. The third party api required a access_token that is refreshed by a refresh_token saved in the .env file. To not expose it to the client application, the client application should call the server route /api/products which then calls the third party backend like this:
/api/products.get.ts
export default defineEventHandler(async (event) => {
const apiUrl = `https://www.xxx.eu/Products?fields=Product_Name&per_page=1`;
const data = await $fetch(apiUrl, {
method: "GET"
}).catch((err) => {
throw createError({
statusCode: err.status,
statusMessage: "Error fetching data from API",
data: err,
});
});
return data;
});
Because the third party API needs an access token authorization that shouldn't be exposed to the client side and might need to be refreshed upon expiry, I added a server middleware and a server utility:
middleware/checkAuth.ts
export default defineEventHandler(async (event) => {
const apiRoute = event.node.req.url?.startsWith("/api");
if (apiRoute) {
let access_token = await useStorage().getItem("auth:access_token");
const expiry: number|null = await useStorage().getItem("auth:expiry");
const now = Date.now();
if (!expiry || now > expiry - 60 * 1000) { // Fetch new token before expiry
console.log("refreshing token");
access_token = await refreshToken();
}
event.node.req.headers.Authorization = `Bearer ${access_token}`;
}
});
utils/refreshToken.ts
interface AuthData {
access_token: string;
api_domain: string;
token_type: string;
expires_in: number;
}
export default async function () {
const apiUrl = `https://xxx.eu/oauth/v2/token?` +
`client_id=${process.env.CLIENT_ID}` +
`&client_secret=${process.env.CLIENT_SECRET}` +
`&refresh_token=${process.env.REFRESH_TOKEN}` +
`&grant_type=refresh_token`;
const authData = (await $fetch(apiUrl, {
method: "POST"
}).catch((err) => {
throw createError({
statusCode: err.status,
message: "Error",
statusMessage: "Error refreshing access token from API",
data: err,
});
})) as AuthData | any;
await useStorage().setItem("auth:access_token", authData.access_token);
await useStorage().setItem("auth:expiry", Date.now() + 3600 * 1000); // 60 minutes expiry
return authData.access_token;
}
For some crazy reason event.node.req.headers.Authorization = `Bearer ${access_token}`;
does not add that header to the request. Whaaat am I doing wrong? I'm aware that I can set the token directly in the $fetch options but it should work in the middleware as well, right? Please help.