@pedwise/next-firebase-auth-edge
Version:
Next.js 13 Firebase Authentication for Edge and server runtimes. Dedicated for Next 13 server components. Compatible with Next.js middleware.
124 lines • 4.83 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFirebaseAdminTokenProvider = exports.ServiceAccountCredential = void 0;
const jwt_1 = require("./jwt");
const response_cache_1 = require("./response-cache");
const TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1000;
const GOOGLE_TOKEN_AUDIENCE = "https://accounts.google.com/o/oauth2/token";
const GOOGLE_AUTH_TOKEN_HOST = "accounts.google.com";
const GOOGLE_AUTH_TOKEN_PATH = "/o/oauth2/token";
const ONE_HOUR_IN_SECONDS = 60 * 60;
const JWT_ALGORITHM = "RS256";
class ServiceAccountCredential {
constructor(serviceAccount) {
this.projectId = serviceAccount.projectId;
this.privateKey = serviceAccount.privateKey;
this.clientEmail = serviceAccount.clientEmail;
this.cache = (0, response_cache_1.getResponseCache)();
}
async fetchAccessToken(url) {
const token = await this.createJwt();
const postData = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3A" +
"grant-type%3Ajwt-bearer&assertion=" +
token;
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Bearer ${token}`,
Accept: "application/json",
},
body: postData,
});
const clone = response.clone();
const data = await response.json();
if (!data.access_token || !data.expires_in) {
throw new Error(`Unexpected response while fetching access token: ${JSON.stringify(data)}`);
}
const body = JSON.stringify({
accessToken: data.access_token,
expirationTime: Date.now() + data.expires_in * 1000,
});
return new Response(body, clone);
}
async fetchAndCacheAccessToken(url) {
const response = await this.fetchAccessToken(url);
await this.cache.put(url, response.clone());
return response;
}
async getAccessToken(forceRefresh) {
const url = new URL(`https://${GOOGLE_AUTH_TOKEN_HOST}${GOOGLE_AUTH_TOKEN_PATH}`);
if (forceRefresh) {
return requestAccessToken(await this.fetchAndCacheAccessToken(url));
}
const cachedResponse = await this.cache.get(url);
if (!cachedResponse) {
return requestAccessToken(await this.fetchAndCacheAccessToken(url));
}
const response = await requestAccessToken(cachedResponse);
if (response.expirationTime - Date.now() <= TOKEN_EXPIRY_THRESHOLD_MILLIS) {
return requestAccessToken(await this.fetchAndCacheAccessToken(url));
}
return response;
}
async createJwt() {
const iat = Math.floor(Date.now() / 1000);
const payload = {
aud: GOOGLE_TOKEN_AUDIENCE,
iat,
exp: iat + ONE_HOUR_IN_SECONDS,
iss: this.clientEmail,
sub: this.clientEmail,
scope: [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/firebase.messaging",
"https://www.googleapis.com/auth/identitytoolkit",
"https://www.googleapis.com/auth/userinfo.email",
].join(" "),
};
return (0, jwt_1.sign)({
payload,
privateKey: this.privateKey,
algorithm: JWT_ALGORITHM,
});
}
}
exports.ServiceAccountCredential = ServiceAccountCredential;
async function requestAccessToken(res) {
if (!res.ok) {
const data = await res.json();
throw new Error(getErrorMessage(data));
}
const data = await res.json();
if (!data.accessToken || !data.expirationTime) {
throw new Error(`Unexpected response while fetching access token: ${JSON.stringify(data)}`);
}
return data;
}
function getErrorMessage(data) {
const detail = getDetailFromResponse(data);
return `Error fetching access token: ${detail}`;
}
function getDetailFromResponse(data) {
if (data === null || data === void 0 ? void 0 : data.error) {
const json = data;
let detail = json.error;
if (json.error_description) {
detail += " (" + json.error_description + ")";
}
return detail;
}
return "Missing error payload";
}
const getFirebaseAdminTokenProvider = (account) => {
const credential = new ServiceAccountCredential(account);
async function getToken(forceRefresh = false) {
return credential.getAccessToken(forceRefresh);
}
return {
getToken,
};
};
exports.getFirebaseAdminTokenProvider = getFirebaseAdminTokenProvider;
//# sourceMappingURL=credential.js.map