firebase-auth-cloudflare-workers
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -0,1 +1,2 @@ | ||
import type { Credential } from './credential'; | ||
import type { EmulatorEnv } from './emulator'; | ||
@@ -7,3 +8,6 @@ import type { KeyStorer } from './key-store'; | ||
protected readonly idTokenVerifier: FirebaseTokenVerifier; | ||
constructor(projectId: string, keyStore: KeyStorer); | ||
protected readonly sessionCookieVerifier: FirebaseTokenVerifier; | ||
private readonly _authApiClient?; | ||
constructor(projectId: string, keyStore: KeyStorer, credential?: Credential); | ||
private get authApiClient(); | ||
/** | ||
@@ -22,2 +26,59 @@ * Verifies a Firebase ID token (JWT). If the token is valid, the promise is | ||
verifyIdToken(idToken: string, env?: EmulatorEnv): Promise<FirebaseIdToken>; | ||
/** | ||
* Creates a new Firebase session cookie with the specified options. The created | ||
* JWT string can be set as a server-side session cookie with a custom cookie | ||
* policy, and be used for session management. The session cookie JWT will have | ||
* the same payload claims as the provided ID token. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies | Manage Session Cookies} | ||
* for code samples and detailed documentation. | ||
* | ||
* @param idToken - The Firebase ID token to exchange for a session | ||
* cookie. | ||
* @param sessionCookieOptions - The session | ||
* cookie options which includes custom session duration. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise that resolves on success with the | ||
* created session cookie. | ||
*/ | ||
createSessionCookie(idToken: string, sessionCookieOptions: SessionCookieOptions, env?: EmulatorEnv): Promise<string>; | ||
/** | ||
* Verifies a Firebase session cookie. Returns a Promise with the cookie claims. | ||
* Rejects the promise if the cookie could not be verified. | ||
* | ||
* If `checkRevoked` is set to true, first verifies whether the corresponding | ||
* user is disabled: If yes, an `auth/user-disabled` error is thrown. If no, | ||
* verifies if the session corresponding to the session cookie was revoked. | ||
* If the corresponding user's session was invalidated, an | ||
* `auth/session-cookie-revoked` error is thrown. If not specified the check | ||
* is not performed. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions | | ||
* Verify Session Cookies} | ||
* for code samples and detailed documentation | ||
* | ||
* @param sessionCookie - The session cookie to verify. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise fulfilled with the | ||
* session cookie's decoded claims if the session cookie is valid; otherwise, | ||
* a rejected promise. | ||
*/ | ||
verifySessionCookie(sessionCookie: string, env?: EmulatorEnv): Promise<FirebaseIdToken>; | ||
} | ||
/** | ||
* Interface representing the session cookie options needed for the | ||
* {@link BaseAuth.createSessionCookie} method. | ||
*/ | ||
export interface SessionCookieOptions { | ||
/** | ||
* The session cookie custom expiration in milliseconds. The minimum allowed is | ||
* 5 minutes and the maxium allowed is 2 weeks. | ||
*/ | ||
expiresIn: number; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.BaseAuth = void 0; | ||
const auth_api_requests_1 = require("./auth-api-requests"); | ||
const emulator_1 = require("./emulator"); | ||
const errors_1 = require("./errors"); | ||
const token_verifier_1 = require("./token-verifier"); | ||
const validator_1 = require("./validator"); | ||
class BaseAuth { | ||
constructor(projectId, keyStore) { | ||
/** @internal */ | ||
idTokenVerifier; | ||
sessionCookieVerifier; | ||
_authApiClient; | ||
constructor(projectId, keyStore, credential) { | ||
this.idTokenVerifier = (0, token_verifier_1.createIdTokenVerifier)(projectId, keyStore); | ||
this.sessionCookieVerifier = (0, token_verifier_1.createSessionCookieVerifier)(projectId, keyStore); | ||
if (credential) { | ||
this._authApiClient = new auth_api_requests_1.AuthApiClient(projectId, credential); | ||
} | ||
} | ||
get authApiClient() { | ||
if (this._authApiClient) { | ||
return this._authApiClient; | ||
} | ||
throw new errors_1.FirebaseAppError(errors_1.AppErrorCodes.INVALID_CREDENTIAL, 'Service account must be required in initialization.'); | ||
} | ||
/** | ||
@@ -26,3 +43,58 @@ * Verifies a Firebase ID token (JWT). If the token is valid, the promise is | ||
} | ||
/** | ||
* Creates a new Firebase session cookie with the specified options. The created | ||
* JWT string can be set as a server-side session cookie with a custom cookie | ||
* policy, and be used for session management. The session cookie JWT will have | ||
* the same payload claims as the provided ID token. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies | Manage Session Cookies} | ||
* for code samples and detailed documentation. | ||
* | ||
* @param idToken - The Firebase ID token to exchange for a session | ||
* cookie. | ||
* @param sessionCookieOptions - The session | ||
* cookie options which includes custom session duration. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise that resolves on success with the | ||
* created session cookie. | ||
*/ | ||
async createSessionCookie(idToken, sessionCookieOptions, env) { | ||
// Return rejected promise if expiresIn is not available. | ||
if (!(0, validator_1.isNonNullObject)(sessionCookieOptions) || !(0, validator_1.isNumber)(sessionCookieOptions.expiresIn)) { | ||
throw new errors_1.FirebaseAuthError(errors_1.AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); | ||
} | ||
return await this.authApiClient.createSessionCookie(idToken, sessionCookieOptions.expiresIn, env); | ||
} | ||
/** | ||
* Verifies a Firebase session cookie. Returns a Promise with the cookie claims. | ||
* Rejects the promise if the cookie could not be verified. | ||
* | ||
* If `checkRevoked` is set to true, first verifies whether the corresponding | ||
* user is disabled: If yes, an `auth/user-disabled` error is thrown. If no, | ||
* verifies if the session corresponding to the session cookie was revoked. | ||
* If the corresponding user's session was invalidated, an | ||
* `auth/session-cookie-revoked` error is thrown. If not specified the check | ||
* is not performed. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions | | ||
* Verify Session Cookies} | ||
* for code samples and detailed documentation | ||
* | ||
* @param sessionCookie - The session cookie to verify. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise fulfilled with the | ||
* session cookie's decoded claims if the session cookie is valid; otherwise, | ||
* a rejected promise. | ||
*/ | ||
verifySessionCookie(sessionCookie, env) { | ||
const isEmulator = (0, emulator_1.useEmulator)(env); | ||
return this.sessionCookieVerifier.verifyJWT(sessionCookie, isEmulator); | ||
} | ||
} | ||
exports.BaseAuth = BaseAuth; |
export declare const decodeBase64Url: (str: string) => Uint8Array; | ||
export declare const encodeBase64Url: (buf: ArrayBufferLike) => string; | ||
export declare const encodeBase64: (buf: ArrayBufferLike) => string; | ||
export declare const decodeBase64: (str: string) => Uint8Array; | ||
export declare const encodeObjectBase64Url: (obj: any) => string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.encodeBase64 = exports.encodeBase64Url = exports.decodeBase64Url = void 0; | ||
exports.encodeObjectBase64Url = exports.decodeBase64 = exports.encodeBase64 = exports.encodeBase64Url = exports.decodeBase64Url = void 0; | ||
const utf8_1 = require("./utf8"); | ||
const decodeBase64Url = (str) => { | ||
return decodeBase64(str.replace(/_|-/g, m => ({ _: '/', '-': '+' })[m] ?? m)); | ||
return (0, exports.decodeBase64)(str.replace(/_|-/g, m => ({ _: '/', '-': '+' })[m] ?? m)); | ||
}; | ||
@@ -28,1 +29,5 @@ exports.decodeBase64Url = decodeBase64Url; | ||
}; | ||
exports.decodeBase64 = decodeBase64; | ||
const jsonUTF8Stringify = (obj) => utf8_1.utf8Encoder.encode(JSON.stringify(obj)); | ||
const encodeObjectBase64Url = (obj) => (0, exports.encodeBase64Url)(jsonUTF8Stringify(obj)); | ||
exports.encodeObjectBase64Url = encodeObjectBase64Url; |
@@ -30,2 +30,6 @@ /** | ||
static INVALID_CREDENTIAL: string; | ||
static INTERNAL_ERROR: string; | ||
static NETWORK_ERROR: string; | ||
static NETWORK_TIMEOUT: string; | ||
static UNABLE_TO_PARSE_RESPONSE: string; | ||
} | ||
@@ -48,2 +52,6 @@ /** | ||
}; | ||
static INVALID_ID_TOKEN: { | ||
code: string; | ||
message: string; | ||
}; | ||
static ID_TOKEN_REVOKED: { | ||
@@ -65,2 +73,10 @@ code: string; | ||
}; | ||
static SESSION_COOKIE_EXPIRED: { | ||
code: string; | ||
message: string; | ||
}; | ||
static INVALID_SESSION_COOKIE_DURATION: { | ||
code: string; | ||
message: string; | ||
}; | ||
} | ||
@@ -158,1 +174,11 @@ /** | ||
} | ||
/** | ||
* Firebase App error code structure. This extends PrefixedFirebaseError. | ||
* | ||
* @param code - The error code. | ||
* @param message - The error message. | ||
* @constructor | ||
*/ | ||
export declare class FirebaseAppError extends PrefixedFirebaseError { | ||
constructor(code: string, message: string); | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FirebaseAuthError = exports.PrefixedFirebaseError = exports.FirebaseError = exports.AuthClientErrorCode = exports.AppErrorCodes = exports.JwtErrorCode = exports.JwtError = void 0; | ||
exports.FirebaseAppError = exports.FirebaseAuthError = exports.PrefixedFirebaseError = exports.FirebaseError = exports.AuthClientErrorCode = exports.AppErrorCodes = exports.JwtErrorCode = exports.JwtError = void 0; | ||
/** | ||
@@ -12,2 +12,4 @@ * Jwt error code structure. | ||
class JwtError extends Error { | ||
code; | ||
message; | ||
constructor(code, message) { | ||
@@ -38,5 +40,9 @@ super(message); | ||
class AppErrorCodes { | ||
static INVALID_CREDENTIAL = 'invalid-credential'; | ||
static INTERNAL_ERROR = 'internal-error'; | ||
static NETWORK_ERROR = 'network-error'; | ||
static NETWORK_TIMEOUT = 'network-timeout'; | ||
static UNABLE_TO_PARSE_RESPONSE = 'unable-to-parse-response'; | ||
} | ||
exports.AppErrorCodes = AppErrorCodes; | ||
AppErrorCodes.INVALID_CREDENTIAL = 'invalid-credential'; | ||
/** | ||
@@ -46,32 +52,44 @@ * Auth client error codes and their default messages. | ||
class AuthClientErrorCode { | ||
static INVALID_ARGUMENT = { | ||
code: 'argument-error', | ||
message: 'Invalid argument provided.', | ||
}; | ||
static INVALID_CREDENTIAL = { | ||
code: 'invalid-credential', | ||
message: 'Invalid credential object provided.', | ||
}; | ||
static ID_TOKEN_EXPIRED = { | ||
code: 'id-token-expired', | ||
message: 'The provided Firebase ID token is expired.', | ||
}; | ||
static INVALID_ID_TOKEN = { | ||
code: 'invalid-id-token', | ||
message: 'The provided ID token is not a valid Firebase ID token.', | ||
}; | ||
static ID_TOKEN_REVOKED = { | ||
code: 'id-token-revoked', | ||
message: 'The Firebase ID token has been revoked.', | ||
}; | ||
static INTERNAL_ERROR = { | ||
code: 'internal-error', | ||
message: 'An internal error has occurred.', | ||
}; | ||
static USER_NOT_FOUND = { | ||
code: 'user-not-found', | ||
message: 'There is no user record corresponding to the provided identifier.', | ||
}; | ||
static USER_DISABLED = { | ||
code: 'user-disabled', | ||
message: 'The user record is disabled.', | ||
}; | ||
static SESSION_COOKIE_EXPIRED = { | ||
code: 'session-cookie-expired', | ||
message: 'The Firebase session cookie is expired.', | ||
}; | ||
static INVALID_SESSION_COOKIE_DURATION = { | ||
code: 'invalid-session-cookie-duration', | ||
message: 'The session cookie duration must be a valid number in milliseconds ' + 'between 5 minutes and 2 weeks.', | ||
}; | ||
} | ||
exports.AuthClientErrorCode = AuthClientErrorCode; | ||
AuthClientErrorCode.INVALID_ARGUMENT = { | ||
code: 'argument-error', | ||
message: 'Invalid argument provided.', | ||
}; | ||
AuthClientErrorCode.INVALID_CREDENTIAL = { | ||
code: 'invalid-credential', | ||
message: 'Invalid credential object provided.', | ||
}; | ||
AuthClientErrorCode.ID_TOKEN_EXPIRED = { | ||
code: 'id-token-expired', | ||
message: 'The provided Firebase ID token is expired.', | ||
}; | ||
AuthClientErrorCode.ID_TOKEN_REVOKED = { | ||
code: 'id-token-revoked', | ||
message: 'The Firebase ID token has been revoked.', | ||
}; | ||
AuthClientErrorCode.INTERNAL_ERROR = { | ||
code: 'internal-error', | ||
message: 'An internal error has occurred.', | ||
}; | ||
AuthClientErrorCode.USER_NOT_FOUND = { | ||
code: 'user-not-found', | ||
message: 'There is no user record corresponding to the provided identifier.', | ||
}; | ||
AuthClientErrorCode.USER_DISABLED = { | ||
code: 'user-disabled', | ||
message: 'The user record is disabled.', | ||
}; | ||
/** | ||
@@ -84,2 +102,3 @@ * Firebase error code structure. This extends Error. | ||
class FirebaseError extends Error { | ||
errorInfo; | ||
constructor(errorInfo) { | ||
@@ -120,2 +139,3 @@ super(errorInfo.message); | ||
class PrefixedFirebaseError extends FirebaseError { | ||
codePrefix; | ||
constructor(codePrefix, code, message) { | ||
@@ -165,1 +185,19 @@ super({ | ||
exports.FirebaseAuthError = FirebaseAuthError; | ||
/** | ||
* Firebase App error code structure. This extends PrefixedFirebaseError. | ||
* | ||
* @param code - The error code. | ||
* @param message - The error message. | ||
* @constructor | ||
*/ | ||
class FirebaseAppError extends PrefixedFirebaseError { | ||
constructor(code, message) { | ||
super('app', code, message); | ||
/* tslint:disable:max-line-length */ | ||
// Set the prototype explicitly. See the following link for more details: | ||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | ||
/* tslint:enable:max-line-length */ | ||
this.__proto__ = FirebaseAppError.prototype; | ||
} | ||
} | ||
exports.FirebaseAppError = FirebaseAppError; |
/// <reference types="@cloudflare/workers-types" /> | ||
import { BaseAuth } from './auth'; | ||
import type { Credential } from './credential'; | ||
import type { KeyStorer } from './key-store'; | ||
import { WorkersKVStore } from './key-store'; | ||
export { type Credential, ServiceAccountCredential } from './credential'; | ||
export { emulatorHost, useEmulator } from './emulator'; | ||
@@ -12,3 +14,3 @@ export type { KeyStorer }; | ||
private constructor(); | ||
static getOrInitialize(projectId: string, keyStore: KeyStorer): Auth; | ||
static getOrInitialize(projectId: string, keyStore: KeyStorer, credential?: Credential): Auth; | ||
} | ||
@@ -15,0 +17,0 @@ export declare class WorkersKVStoreSingle extends WorkersKVStore { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WorkersKVStoreSingle = exports.Auth = exports.useEmulator = exports.emulatorHost = void 0; | ||
exports.WorkersKVStoreSingle = exports.Auth = exports.useEmulator = exports.emulatorHost = exports.ServiceAccountCredential = void 0; | ||
const auth_1 = require("./auth"); | ||
const key_store_1 = require("./key-store"); | ||
var credential_1 = require("./credential"); | ||
Object.defineProperty(exports, "ServiceAccountCredential", { enumerable: true, get: function () { return credential_1.ServiceAccountCredential; } }); | ||
var emulator_1 = require("./emulator"); | ||
@@ -10,8 +12,9 @@ Object.defineProperty(exports, "emulatorHost", { enumerable: true, get: function () { return emulator_1.emulatorHost; } }); | ||
class Auth extends auth_1.BaseAuth { | ||
constructor(projectId, keyStore) { | ||
super(projectId, keyStore); | ||
static instance; | ||
constructor(projectId, keyStore, credential) { | ||
super(projectId, keyStore, credential); | ||
} | ||
static getOrInitialize(projectId, keyStore) { | ||
static getOrInitialize(projectId, keyStore, credential) { | ||
if (!Auth.instance) { | ||
Auth.instance = new Auth(projectId, keyStore); | ||
Auth.instance = new Auth(projectId, keyStore, credential); | ||
} | ||
@@ -23,2 +26,3 @@ return Auth.instance; | ||
class WorkersKVStoreSingle extends key_store_1.WorkersKVStore { | ||
static instance; | ||
constructor(cacheKey, cfKVNamespace) { | ||
@@ -25,0 +29,0 @@ super(cacheKey, cfKVNamespace); |
@@ -6,2 +6,6 @@ import type { JsonWebKeyWithKid } from './jwt-decoder'; | ||
} | ||
interface JWKMetadata { | ||
keys: Array<JsonWebKeyWithKid>; | ||
} | ||
export declare const isJWKMetadata: (value: any) => value is JWKMetadata; | ||
/** | ||
@@ -31,1 +35,2 @@ * Class to fetch public keys from a client certificates URL. | ||
} | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.HTTPFetcher = exports.parseMaxAge = exports.UrlKeyFetcher = void 0; | ||
exports.HTTPFetcher = exports.parseMaxAge = exports.UrlKeyFetcher = exports.isJWKMetadata = void 0; | ||
const validator_1 = require("./validator"); | ||
const isJWKMetadata = (value) => (0, validator_1.isNonNullObject)(value) && !!value.keys && (0, validator_1.isArray)(value.keys); | ||
const isJWKMetadata = (value) => { | ||
if (!(0, validator_1.isNonNullObject)(value) || !value.keys) { | ||
return false; | ||
} | ||
const keys = value.keys; | ||
if (!Array.isArray(keys)) { | ||
return false; | ||
} | ||
const filtered = keys.filter((key) => (0, validator_1.isObject)(key) && !!key.kid && typeof key.kid === 'string'); | ||
return keys.length === filtered.length; | ||
}; | ||
exports.isJWKMetadata = isJWKMetadata; | ||
/** | ||
@@ -10,2 +21,4 @@ * Class to fetch public keys from a client certificates URL. | ||
class UrlKeyFetcher { | ||
fetcher; | ||
keyStorer; | ||
constructor(fetcher, keyStorer) { | ||
@@ -35,3 +48,3 @@ this.fetcher = fetcher; | ||
const publicKeys = await resp.json(); | ||
if (!isJWKMetadata(publicKeys)) { | ||
if (!(0, exports.isJWKMetadata)(publicKeys)) { | ||
throw new Error(`The public keys are not an object or null: "${publicKeys}`); | ||
@@ -67,2 +80,3 @@ } | ||
class HTTPFetcher { | ||
clientCertUrl; | ||
constructor(clientCertUrl) { | ||
@@ -69,0 +83,0 @@ this.clientCertUrl = clientCertUrl; |
@@ -19,2 +19,3 @@ "use strict"; | ||
class PublicKeySignatureVerifier { | ||
keyFetcher; | ||
constructor(keyFetcher) { | ||
@@ -21,0 +22,0 @@ this.keyFetcher = keyFetcher; |
@@ -9,2 +9,4 @@ "use strict"; | ||
class RS256Token { | ||
rawToken; | ||
decodedToken; | ||
constructor(rawToken, decodedToken) { | ||
@@ -11,0 +13,0 @@ this.rawToken = rawToken; |
@@ -8,2 +8,4 @@ "use strict"; | ||
class WorkersKVStore { | ||
cacheKey; | ||
cfKVNamespace; | ||
constructor(cacheKey, cfKVNamespace) { | ||
@@ -10,0 +12,0 @@ this.cacheKey = cacheKey; |
@@ -205,2 +205,21 @@ import type { ErrorInfo } from './errors'; | ||
*/ | ||
export declare function createFirebaseTokenVerifier(signatureVerifier: SignatureVerifier, projectID: string): FirebaseTokenVerifier; | ||
export declare function baseCreateIdTokenVerifier(signatureVerifier: SignatureVerifier, projectID: string): FirebaseTokenVerifier; | ||
/** | ||
* User facing token information related to the Firebase session cookie. | ||
* | ||
* @internal | ||
*/ | ||
export declare const SESSION_COOKIE_INFO: FirebaseTokenInfo; | ||
/** | ||
* Creates a new FirebaseTokenVerifier to verify Firebase session cookies. | ||
* | ||
* @internal | ||
* @param app - Firebase app instance. | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
export declare function createSessionCookieVerifier(projectID: string, keyStorer: KeyStorer): FirebaseTokenVerifier; | ||
/** | ||
* @internal | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
export declare function baseCreateSessionCookieVerifier(signatureVerifier: SignatureVerifier, projectID: string): FirebaseTokenVerifier; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createFirebaseTokenVerifier = exports.createIdTokenVerifier = exports.ID_TOKEN_INFO = exports.FirebaseTokenVerifier = exports.FIREBASE_AUDIENCE = void 0; | ||
exports.baseCreateSessionCookieVerifier = exports.createSessionCookieVerifier = exports.SESSION_COOKIE_INFO = exports.baseCreateIdTokenVerifier = exports.createIdTokenVerifier = exports.ID_TOKEN_INFO = exports.FirebaseTokenVerifier = exports.FIREBASE_AUDIENCE = void 0; | ||
const errors_1 = require("./errors"); | ||
@@ -18,2 +18,7 @@ const jws_verifier_1 = require("./jws-verifier"); | ||
class FirebaseTokenVerifier { | ||
signatureVerifier; | ||
projectId; | ||
issuer; | ||
tokenInfo; | ||
shortNameArticle; | ||
constructor(signatureVerifier, projectId, issuer, tokenInfo) { | ||
@@ -182,3 +187,3 @@ this.signatureVerifier = signatureVerifier; | ||
const signatureVerifier = jws_verifier_1.PublicKeySignatureVerifier.withCertificateUrl(CLIENT_JWK_URL, keyStorer); | ||
return createFirebaseTokenVerifier(signatureVerifier, projectID); | ||
return baseCreateIdTokenVerifier(signatureVerifier, projectID); | ||
} | ||
@@ -190,5 +195,39 @@ exports.createIdTokenVerifier = createIdTokenVerifier; | ||
*/ | ||
function createFirebaseTokenVerifier(signatureVerifier, projectID) { | ||
function baseCreateIdTokenVerifier(signatureVerifier, projectID) { | ||
return new FirebaseTokenVerifier(signatureVerifier, projectID, 'https://securetoken.google.com/', exports.ID_TOKEN_INFO); | ||
} | ||
exports.createFirebaseTokenVerifier = createFirebaseTokenVerifier; | ||
exports.baseCreateIdTokenVerifier = baseCreateIdTokenVerifier; | ||
// URL containing the public keys for Firebase session cookies. | ||
const SESSION_COOKIE_CERT_URL = 'https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys'; | ||
/** | ||
* User facing token information related to the Firebase session cookie. | ||
* | ||
* @internal | ||
*/ | ||
exports.SESSION_COOKIE_INFO = { | ||
url: 'https://firebase.google.com/docs/auth/admin/manage-cookies', | ||
verifyApiName: 'verifySessionCookie()', | ||
jwtName: 'Firebase session cookie', | ||
shortName: 'session cookie', | ||
expiredErrorCode: errors_1.AuthClientErrorCode.SESSION_COOKIE_EXPIRED, | ||
}; | ||
/** | ||
* Creates a new FirebaseTokenVerifier to verify Firebase session cookies. | ||
* | ||
* @internal | ||
* @param app - Firebase app instance. | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
function createSessionCookieVerifier(projectID, keyStorer) { | ||
const signatureVerifier = jws_verifier_1.PublicKeySignatureVerifier.withCertificateUrl(SESSION_COOKIE_CERT_URL, keyStorer); | ||
return baseCreateSessionCookieVerifier(signatureVerifier, projectID); | ||
} | ||
exports.createSessionCookieVerifier = createSessionCookieVerifier; | ||
/** | ||
* @internal | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
function baseCreateSessionCookieVerifier(signatureVerifier, projectID) { | ||
return new FirebaseTokenVerifier(signatureVerifier, projectID, 'https://session.firebase.google.com/', exports.SESSION_COOKIE_INFO); | ||
} | ||
exports.baseCreateSessionCookieVerifier = baseCreateSessionCookieVerifier; |
@@ -30,9 +30,2 @@ /** | ||
/** | ||
* Validates that a value is an array. | ||
* | ||
* @param value - The value to validate. | ||
* @returns Whether the value is an array or not. | ||
*/ | ||
export declare function isArray<T>(value: any): value is T[]; | ||
/** | ||
* | ||
@@ -39,0 +32,0 @@ /** |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isNonNullObject = exports.isObject = exports.isArray = exports.isNonEmptyString = exports.isString = exports.isNumber = exports.isURL = void 0; | ||
exports.isNonNullObject = exports.isObject = exports.isNonEmptyString = exports.isString = exports.isNumber = exports.isURL = void 0; | ||
/** | ||
@@ -78,12 +78,2 @@ * Validates that a string is a valid web URL. | ||
/** | ||
* Validates that a value is an array. | ||
* | ||
* @param value - The value to validate. | ||
* @returns Whether the value is an array or not. | ||
*/ | ||
function isArray(value) { | ||
return Array.isArray(value); | ||
} | ||
exports.isArray = isArray; | ||
/** | ||
* | ||
@@ -97,3 +87,3 @@ /** | ||
function isObject(value) { | ||
return typeof value === 'object' && !isArray(value); | ||
return typeof value === 'object' && !Array.isArray(value); | ||
} | ||
@@ -100,0 +90,0 @@ exports.isObject = isObject; |
@@ -0,9 +1,24 @@ | ||
import { AuthApiClient } from './auth-api-requests'; | ||
import { useEmulator } from './emulator'; | ||
import { createIdTokenVerifier } from './token-verifier'; | ||
import { AppErrorCodes, AuthClientErrorCode, FirebaseAppError, FirebaseAuthError } from './errors'; | ||
import { createIdTokenVerifier, createSessionCookieVerifier } from './token-verifier'; | ||
import { isNonNullObject, isNumber } from './validator'; | ||
export class BaseAuth { | ||
/** @internal */ | ||
idTokenVerifier; | ||
constructor(projectId, keyStore) { | ||
sessionCookieVerifier; | ||
_authApiClient; | ||
constructor(projectId, keyStore, credential) { | ||
this.idTokenVerifier = createIdTokenVerifier(projectId, keyStore); | ||
this.sessionCookieVerifier = createSessionCookieVerifier(projectId, keyStore); | ||
if (credential) { | ||
this._authApiClient = new AuthApiClient(projectId, credential); | ||
} | ||
} | ||
get authApiClient() { | ||
if (this._authApiClient) { | ||
return this._authApiClient; | ||
} | ||
throw new FirebaseAppError(AppErrorCodes.INVALID_CREDENTIAL, 'Service account must be required in initialization.'); | ||
} | ||
/** | ||
@@ -25,2 +40,57 @@ * Verifies a Firebase ID token (JWT). If the token is valid, the promise is | ||
} | ||
/** | ||
* Creates a new Firebase session cookie with the specified options. The created | ||
* JWT string can be set as a server-side session cookie with a custom cookie | ||
* policy, and be used for session management. The session cookie JWT will have | ||
* the same payload claims as the provided ID token. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies | Manage Session Cookies} | ||
* for code samples and detailed documentation. | ||
* | ||
* @param idToken - The Firebase ID token to exchange for a session | ||
* cookie. | ||
* @param sessionCookieOptions - The session | ||
* cookie options which includes custom session duration. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise that resolves on success with the | ||
* created session cookie. | ||
*/ | ||
async createSessionCookie(idToken, sessionCookieOptions, env) { | ||
// Return rejected promise if expiresIn is not available. | ||
if (!isNonNullObject(sessionCookieOptions) || !isNumber(sessionCookieOptions.expiresIn)) { | ||
throw new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); | ||
} | ||
return await this.authApiClient.createSessionCookie(idToken, sessionCookieOptions.expiresIn, env); | ||
} | ||
/** | ||
* Verifies a Firebase session cookie. Returns a Promise with the cookie claims. | ||
* Rejects the promise if the cookie could not be verified. | ||
* | ||
* If `checkRevoked` is set to true, first verifies whether the corresponding | ||
* user is disabled: If yes, an `auth/user-disabled` error is thrown. If no, | ||
* verifies if the session corresponding to the session cookie was revoked. | ||
* If the corresponding user's session was invalidated, an | ||
* `auth/session-cookie-revoked` error is thrown. If not specified the check | ||
* is not performed. | ||
* | ||
* See {@link https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions | | ||
* Verify Session Cookies} | ||
* for code samples and detailed documentation | ||
* | ||
* @param sessionCookie - The session cookie to verify. | ||
* @param env - An optional parameter specifying the environment in which the function is running. | ||
* If the function is running in an emulator environment, this should be set to `EmulatorEnv`. | ||
* If not specified, the function will assume it is running in a production environment. | ||
* | ||
* @returns A promise fulfilled with the | ||
* session cookie's decoded claims if the session cookie is valid; otherwise, | ||
* a rejected promise. | ||
*/ | ||
verifySessionCookie(sessionCookie, env) { | ||
const isEmulator = useEmulator(env); | ||
return this.sessionCookieVerifier.verifyJWT(sessionCookie, isEmulator); | ||
} | ||
} |
@@ -0,1 +1,2 @@ | ||
import { utf8Encoder } from './utf8'; | ||
export const decodeBase64Url = (str) => { | ||
@@ -12,3 +13,3 @@ return decodeBase64(str.replace(/_|-/g, m => ({ _: '/', '-': '+' })[m] ?? m)); | ||
// atob does not support utf-8 characters. So we need a little bit hack. | ||
const decodeBase64 = (str) => { | ||
export const decodeBase64 = (str) => { | ||
const binary = atob(str); | ||
@@ -23,1 +24,3 @@ const bytes = new Uint8Array(new ArrayBuffer(binary.length)); | ||
}; | ||
const jsonUTF8Stringify = (obj) => utf8Encoder.encode(JSON.stringify(obj)); | ||
export const encodeObjectBase64Url = (obj) => encodeBase64Url(jsonUTF8Stringify(obj)); |
@@ -36,2 +36,6 @@ /** | ||
static INVALID_CREDENTIAL = 'invalid-credential'; | ||
static INTERNAL_ERROR = 'internal-error'; | ||
static NETWORK_ERROR = 'network-error'; | ||
static NETWORK_TIMEOUT = 'network-timeout'; | ||
static UNABLE_TO_PARSE_RESPONSE = 'unable-to-parse-response'; | ||
} | ||
@@ -54,2 +58,6 @@ /** | ||
}; | ||
static INVALID_ID_TOKEN = { | ||
code: 'invalid-id-token', | ||
message: 'The provided ID token is not a valid Firebase ID token.', | ||
}; | ||
static ID_TOKEN_REVOKED = { | ||
@@ -71,2 +79,10 @@ code: 'id-token-revoked', | ||
}; | ||
static SESSION_COOKIE_EXPIRED = { | ||
code: 'session-cookie-expired', | ||
message: 'The Firebase session cookie is expired.', | ||
}; | ||
static INVALID_SESSION_COOKIE_DURATION = { | ||
code: 'invalid-session-cookie-duration', | ||
message: 'The session cookie duration must be a valid number in milliseconds ' + 'between 5 minutes and 2 weeks.', | ||
}; | ||
} | ||
@@ -158,1 +174,18 @@ /** | ||
} | ||
/** | ||
* Firebase App error code structure. This extends PrefixedFirebaseError. | ||
* | ||
* @param code - The error code. | ||
* @param message - The error message. | ||
* @constructor | ||
*/ | ||
export class FirebaseAppError extends PrefixedFirebaseError { | ||
constructor(code, message) { | ||
super('app', code, message); | ||
/* tslint:disable:max-line-length */ | ||
// Set the prototype explicitly. See the following link for more details: | ||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | ||
/* tslint:enable:max-line-length */ | ||
this.__proto__ = FirebaseAppError.prototype; | ||
} | ||
} |
import { BaseAuth } from './auth'; | ||
import { WorkersKVStore } from './key-store'; | ||
export { ServiceAccountCredential } from './credential'; | ||
export { emulatorHost, useEmulator } from './emulator'; | ||
export class Auth extends BaseAuth { | ||
static instance; | ||
constructor(projectId, keyStore) { | ||
super(projectId, keyStore); | ||
constructor(projectId, keyStore, credential) { | ||
super(projectId, keyStore, credential); | ||
} | ||
static getOrInitialize(projectId, keyStore) { | ||
static getOrInitialize(projectId, keyStore, credential) { | ||
if (!Auth.instance) { | ||
Auth.instance = new Auth(projectId, keyStore); | ||
Auth.instance = new Auth(projectId, keyStore, credential); | ||
} | ||
@@ -13,0 +14,0 @@ return Auth.instance; |
@@ -1,3 +0,13 @@ | ||
import { isArray, isNonNullObject, isURL } from './validator'; | ||
const isJWKMetadata = (value) => isNonNullObject(value) && !!value.keys && isArray(value.keys); | ||
import { isNonNullObject, isObject, isURL } from './validator'; | ||
export const isJWKMetadata = (value) => { | ||
if (!isNonNullObject(value) || !value.keys) { | ||
return false; | ||
} | ||
const keys = value.keys; | ||
if (!Array.isArray(keys)) { | ||
return false; | ||
} | ||
const filtered = keys.filter((key) => isObject(key) && !!key.kid && typeof key.kid === 'string'); | ||
return keys.length === filtered.length; | ||
}; | ||
/** | ||
@@ -4,0 +14,0 @@ * Class to fetch public keys from a client certificates URL. |
@@ -182,3 +182,3 @@ import { AuthClientErrorCode, FirebaseAuthError, JwtError, JwtErrorCode } from './errors'; | ||
const signatureVerifier = PublicKeySignatureVerifier.withCertificateUrl(CLIENT_JWK_URL, keyStorer); | ||
return createFirebaseTokenVerifier(signatureVerifier, projectID); | ||
return baseCreateIdTokenVerifier(signatureVerifier, projectID); | ||
} | ||
@@ -189,4 +189,36 @@ /** | ||
*/ | ||
export function createFirebaseTokenVerifier(signatureVerifier, projectID) { | ||
export function baseCreateIdTokenVerifier(signatureVerifier, projectID) { | ||
return new FirebaseTokenVerifier(signatureVerifier, projectID, 'https://securetoken.google.com/', ID_TOKEN_INFO); | ||
} | ||
// URL containing the public keys for Firebase session cookies. | ||
const SESSION_COOKIE_CERT_URL = 'https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys'; | ||
/** | ||
* User facing token information related to the Firebase session cookie. | ||
* | ||
* @internal | ||
*/ | ||
export const SESSION_COOKIE_INFO = { | ||
url: 'https://firebase.google.com/docs/auth/admin/manage-cookies', | ||
verifyApiName: 'verifySessionCookie()', | ||
jwtName: 'Firebase session cookie', | ||
shortName: 'session cookie', | ||
expiredErrorCode: AuthClientErrorCode.SESSION_COOKIE_EXPIRED, | ||
}; | ||
/** | ||
* Creates a new FirebaseTokenVerifier to verify Firebase session cookies. | ||
* | ||
* @internal | ||
* @param app - Firebase app instance. | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
export function createSessionCookieVerifier(projectID, keyStorer) { | ||
const signatureVerifier = PublicKeySignatureVerifier.withCertificateUrl(SESSION_COOKIE_CERT_URL, keyStorer); | ||
return baseCreateSessionCookieVerifier(signatureVerifier, projectID); | ||
} | ||
/** | ||
* @internal | ||
* @returns FirebaseTokenVerifier | ||
*/ | ||
export function baseCreateSessionCookieVerifier(signatureVerifier, projectID) { | ||
return new FirebaseTokenVerifier(signatureVerifier, projectID, 'https://session.firebase.google.com/', SESSION_COOKIE_INFO); | ||
} |
@@ -71,11 +71,2 @@ /** | ||
/** | ||
* Validates that a value is an array. | ||
* | ||
* @param value - The value to validate. | ||
* @returns Whether the value is an array or not. | ||
*/ | ||
export function isArray(value) { | ||
return Array.isArray(value); | ||
} | ||
/** | ||
* | ||
@@ -89,3 +80,3 @@ /** | ||
export function isObject(value) { | ||
return typeof value === 'object' && !isArray(value); | ||
return typeof value === 'object' && !Array.isArray(value); | ||
} | ||
@@ -92,0 +83,0 @@ /** |
{ | ||
"name": "firebase-auth-cloudflare-workers", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Zero-dependencies firebase auth library for Cloudflare Workers.", | ||
@@ -15,33 +15,21 @@ "author": "codehex", | ||
], | ||
"scripts": { | ||
"test": "vitest run", | ||
"build": "run-p build:*", | ||
"build:main": "tsc -p tsconfig.main.json", | ||
"build:module": "tsc -p tsconfig.module.json", | ||
"start-firebase-emulator": "firebase emulators:start --project example-project12345", | ||
"start-example": "wrangler dev example/index.ts --config=example/wrangler.toml --local=true", | ||
"prettier": "prettier --write --list-different \"**/*.ts\"", | ||
"prettier:check": "prettier --check \"**/*.ts\"", | ||
"lint": "eslint --ext .ts .", | ||
"lint-fix": "eslint --fix --ext .ts .", | ||
"prepublish": "run-p build:*" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@cloudflare/workers-types": "^4.20231025.0", | ||
"@typescript-eslint/eslint-plugin": "^6.10.0", | ||
"@typescript-eslint/parser": "^6.10.0", | ||
"eslint": "^8.53.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"@cloudflare/workers-types": "^4.20240208.0", | ||
"@typescript-eslint/eslint-plugin": "^6.21.0", | ||
"@typescript-eslint/parser": "^6.21.0", | ||
"eslint": "^8.56.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-define-config": "^1.24.1", | ||
"eslint-import-resolver-typescript": "^3.6.1", | ||
"eslint-plugin-eslint-comments": "^3.2.0", | ||
"eslint-plugin-import": "^2.29.0", | ||
"firebase-tools": "^12.8.1", | ||
"miniflare": "^3.20231025.1", | ||
"eslint-plugin-import": "^2.29.1", | ||
"firebase-tools": "^13.3.0", | ||
"hono": "^4.0.4", | ||
"miniflare": "^3.20240129.3", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^3.0.3", | ||
"typescript": "^5.2.2", | ||
"vitest": "^0.34.6", | ||
"wrangler": "^3.15.0" | ||
"prettier": "^3.2.5", | ||
"typescript": "^5.3.3", | ||
"undici": "^6.6.2", | ||
"vitest": "^1.3.0", | ||
"wrangler": "^3.28.3" | ||
}, | ||
@@ -58,3 +46,18 @@ "keywords": [ | ||
"url": "https://github.com/Code-Hex/firebase-auth-cloudflare-workers/issues" | ||
}, | ||
"scripts": { | ||
"test": "vitest run", | ||
"test-with-emulator": "firebase emulators:exec --project project12345 'vitest run'", | ||
"build": "run-p build:*", | ||
"build:main": "tsc -p tsconfig.main.json", | ||
"build:module": "tsc -p tsconfig.module.json", | ||
"start-firebase-emulator": "firebase emulators:start --project project12345", | ||
"start-example": "wrangler dev example/index.ts --config=example/wrangler.toml --local=true", | ||
"prettier": "prettier --write --list-different \"**/*.ts\"", | ||
"prettier:check": "prettier --check \"**/*.ts\"", | ||
"lint": "eslint --ext .ts .", | ||
"lint-fix": "eslint --fix --ext .ts .", | ||
"prepublish": "run-p build:*", | ||
"wrangler": "wrangler" | ||
} | ||
} | ||
} |
@@ -113,3 +113,3 @@ # firebase-auth-cloudflare-workers | ||
### `Auth.getOrInitialize(projectId: string, keyStore: KeyStorer): Auth` | ||
### `Auth.getOrInitialize(projectId: string, keyStore: KeyStorer, credential?: Credential): Auth` | ||
@@ -120,2 +120,3 @@ Auth is created as a singleton object. This is because the Module Worker syntax only use environment variables at the time of request. | ||
- `keyStore` is used to cache the public key used to validate the Firebase ID token (JWT). | ||
- `credential` is an optional. This is used to utilize Admin APIs such as `createSessionCookie`. Currently, you can specify `ServiceAccountCredential` class, which allows you to use a service account. | ||
@@ -130,2 +131,3 @@ See official document for project ID: https://firebase.google.com/docs/projects/learn-more#project-identifiers | ||
- `idToken` The ID token to verify. | ||
- `env` is an optional parameter. but this is using to detect should use emulator or not. | ||
@@ -144,2 +146,21 @@ | ||
### `createSessionCookie(idToken: string, sessionCookieOptions: SessionCookieOptions, env?: EmulatorEnv): Promise<string>` | ||
Creates a new Firebase session cookie with the specified options. The created JWT string can be set as a server-side session cookie with a custom cookie policy, and be used for session management. The session cookie JWT will have the same payload claims as the provided ID token. See [Manage Session Cookies](https://firebase.google.com/docs/auth/admin/manage-cookies) for code samples and detailed documentation. | ||
- `idToken` The Firebase ID token to exchange for a session cookie. | ||
- `sessionCookieOptions` The session cookie options which includes custom session duration. | ||
- `env` is an optional parameter. but this is using to detect should use emulator or not. | ||
**Required** service acccount credential to use this API. You need to set the credentials with `Auth.getOrInitialize`. | ||
### `verifySessionCookie(sessionCookie: string, env?: EmulatorEnv): Promise<FirebaseIdToken>` | ||
Verifies a Firebase session cookie. Returns a Promise with the cookie claims. Rejects the promise if the cookie could not be verified. | ||
See [Verify Session Cookies](https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions) for code samples and detailed documentation. | ||
- `sessionCookie` The session cookie to verify. | ||
- `env` is an optional parameter. but this is using to detect should use emulator or not. | ||
### `emulatorHost(env?: EmulatorEnv): string | undefined` | ||
@@ -192,10 +213,16 @@ | ||
1. Clone this repository and change your directory to it. | ||
2. Install dev dependencies as `yarn` command. | ||
3. Run firebase auth emulator by `$ yarn start-firebase-emulator` | ||
2. Install dev dependencies as `pnpm` command. | ||
3. Run firebase auth emulator by `$ pnpm start-firebase-emulator` | ||
4. Access to Emulator UI in your favorite browser. | ||
5. Create a new user on Emulator UI. (email: `test@example.com` password: `test1234`) | ||
6. Run example code on local (may serve as `localhost:8787`) by `$ yarn start-example` | ||
6. Run example code on local (may serve as `localhost:8787`) by `$ pnpm start-example` | ||
7. Get jwt for created user by `$ curl -s http://localhost:8787/get-jwt | jq .idToken -r` | ||
8. Try authorization with user jwt `$ curl http://localhost:8787/ -H 'Authorization: Bearer PASTE-JWT-HERE'` | ||
### for Session Cookie | ||
You can try session cookie with your browser. | ||
Access to `/admin/login` after started up Emulator and created an account (email: `test@example.com` password: `test1234`). | ||
## Todo | ||
@@ -202,0 +229,0 @@ |
152900
51
3552
232
18
6