@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.
164 lines • 6.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFirebaseTokenGenerator = exports.handleCryptoSignerError = exports.FirebaseTokenGenerator = exports.EmulatedSigner = exports.BLACKLISTED_CLAIMS = void 0;
const crypto_signer_1 = require("./jwt/crypto-signer");
const utils_1 = require("./jwt/utils");
const validator_1 = require("./validator");
const error_1 = require("./error");
const firebase_1 = require("./firebase");
const ALGORITHM_NONE = "none";
const ONE_HOUR_IN_SECONDS = 60 * 60;
exports.BLACKLISTED_CLAIMS = [
"acr",
"amr",
"at_hash",
"aud",
"auth_time",
"azp",
"cnf",
"c_hash",
"exp",
"iat",
"iss",
"jti",
"nbf",
"nonce",
];
const FIREBASE_AUDIENCE = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
class EmulatedSigner {
constructor() {
this.algorithm = ALGORITHM_NONE;
}
async sign(token) {
return (0, utils_1.stringToBase64)(token);
}
getAccountId() {
return Promise.resolve("firebase-auth-emulator@example.com");
}
}
exports.EmulatedSigner = EmulatedSigner;
class FirebaseTokenGenerator {
constructor(signer, tenantId) {
this.tenantId = tenantId;
if (!(0, validator_1.isNonNullObject)(signer)) {
throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREDENTIAL, "INTERNAL ASSERT: Must provide a CryptoSigner to use FirebaseTokenGenerator.");
}
if (typeof this.tenantId !== "undefined" &&
!(0, validator_1.isNonEmptyString)(this.tenantId)) {
throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "`tenantId` argument must be a non-empty string.");
}
this.signer = signer;
}
createCustomToken(uid, developerClaims) {
let errorMessage;
if (!(0, validator_1.isNonEmptyString)(uid)) {
errorMessage = "`uid` argument must be a non-empty string uid.";
}
else if (uid.length > 128) {
errorMessage =
"`uid` argument must a uid with less than or equal to 128 characters.";
}
else if (!FirebaseTokenGenerator.isDeveloperClaimsValid_(developerClaims)) {
errorMessage =
"`developerClaims` argument must be a valid, non-null object containing the developer claims.";
}
if (errorMessage) {
throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, errorMessage);
}
const claims = {};
if (typeof developerClaims !== "undefined") {
for (const key in developerClaims) {
if (Object.prototype.hasOwnProperty.call(developerClaims, key)) {
if (exports.BLACKLISTED_CLAIMS.indexOf(key) !== -1) {
throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `Developer claim "${key}" is reserved and cannot be specified.`);
}
claims[key] = developerClaims[key];
}
}
}
return this.signer
.getAccountId()
.then(async (account) => {
const header = {
alg: this.signer.algorithm,
typ: "JWT",
};
const iat = Math.floor(Date.now() / 1000);
const body = {
aud: FIREBASE_AUDIENCE,
iat,
exp: iat + ONE_HOUR_IN_SECONDS,
iss: account,
sub: account,
uid,
};
if (this.tenantId) {
body.tenant_id = this.tenantId;
}
if (Object.keys(claims).length > 0) {
body.claims = claims;
}
const token = `${FirebaseTokenGenerator.encodeSegment(header)}.${FirebaseTokenGenerator.encodeSegment(body)}`;
const signPromise = await this.signer.sign(token);
return Promise.all([token, signPromise]);
})
.then(([token, signature]) => {
return `${token}.${signature}`;
})
.catch((err) => {
throw handleCryptoSignerError(err);
});
}
static encodeSegment(segment) {
if (typeof segment === "object") {
return (0, utils_1.objectToBase64)(segment);
}
return (0, utils_1.stringToBase64)(segment);
}
static isDeveloperClaimsValid_(developerClaims) {
if (typeof developerClaims === "undefined") {
return true;
}
return (0, validator_1.isNonNullObject)(developerClaims);
}
}
exports.FirebaseTokenGenerator = FirebaseTokenGenerator;
function handleCryptoSignerError(err) {
var _a;
if (!(err instanceof crypto_signer_1.CryptoSignerError)) {
return err;
}
if (err.code === crypto_signer_1.CryptoSignerErrorCode.SERVER_ERROR &&
(0, validator_1.isNonNullObject)(err.cause)) {
return new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, "Error returned from server: " +
((_a = err.cause) === null || _a === void 0 ? void 0 : _a.message) +
". Additionally, an " +
"internal error occurred while attempting to extract the " +
"errorcode from the error.");
}
return new error_1.FirebaseAuthError(mapToAuthClientErrorCode(err.code), err.message);
}
exports.handleCryptoSignerError = handleCryptoSignerError;
function mapToAuthClientErrorCode(code) {
switch (code) {
case crypto_signer_1.CryptoSignerErrorCode.INVALID_CREDENTIAL:
return error_1.AuthClientErrorCode.INVALID_CREDENTIAL;
case crypto_signer_1.CryptoSignerErrorCode.INVALID_ARGUMENT:
return error_1.AuthClientErrorCode.INVALID_ARGUMENT;
default:
return error_1.AuthClientErrorCode.INTERNAL_ERROR;
}
}
function createFirebaseTokenGenerator(credential, tenantId) {
try {
const signer = (0, firebase_1.useEmulator)()
? new EmulatedSigner()
: new crypto_signer_1.ServiceAccountSigner(credential);
return new FirebaseTokenGenerator(signer, tenantId);
}
catch (err) {
throw handleCryptoSignerError(err);
}
}
exports.createFirebaseTokenGenerator = createFirebaseTokenGenerator;
//# sourceMappingURL=token-generator.js.map