Skip to content

Instantly share code, notes, and snippets.

@tohagan
Last active October 22, 2022 09:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tohagan/13822a52ce48b59e0a41ba67af1d93ac to your computer and use it in GitHub Desktop.
Save tohagan/13822a52ce48b59e0a41ba67af1d93ac to your computer and use it in GitHub Desktop.
Firebase Middleware for GRPC Callable functions
/*
* Middleware for Firebase GRPC Callable functions
*
* Reference: https://stackoverflow.com/a/70057694/365261
*
* Example:
* export const myCallableFunction = functions.https.onCall(
* return withMiddlewares([assertAppCheck, assertAuthenticated], async (data, context) => {
* // Your callable function handler
* })
* );
*/
import * as functions from 'firebase-functions'
import { https } from 'firebase-functions'
type CallableContext = functions.https.CallableContext
export type Handler<TRequest = unknown, TResponse = unknown> = (
data: TRequest,
context: CallableContext
) => Promise<TResponse>
export type Middleware<TRequest = unknown, TResponse = unknown> = (
data: TRequest,
context: CallableContext,
next: Handler<TResponse>
) => Promise<TResponse>
export const withMiddlewares =
(middlewares: Middleware[], handler: Handler) => (data: unknown, context: CallableContext) => {
const chainMiddlewares = ([firstMiddleware, ...restOfMiddlewares]: Middleware[]) => {
if (firstMiddleware) {
return (data: unknown, context: CallableContext): Promise<unknown> => {
try {
return firstMiddleware(data, context, chainMiddlewares(restOfMiddlewares))
} catch (error) {
return Promise.reject(error)
}
}
}
return handler
}
return chainMiddlewares(middlewares)(data, context)
}
/**
* Ensures request passes App Check
*
* @param {unknown} data
* @param {CallableContext} context
* @param {Handler} next
* @return {Promise<unknown>}
*/
export const assertAppCheck: Middleware = (data, context, next) => {
if (context.app === undefined && process.env.FUNCTIONS_EMULATOR !== "true") {
throw new https.HttpsError('failed-precondition', 'Failed App Check.')
}
return next(data, context)
}
/**
* Ensures user is authenticated
*
* @param {unknown} data
* @param {CallableContext} context
* @param {Handler} next
* @return {Promise<unknown>}
*/
export const assertAuthenticated: Middleware = (data, context, next) => {
if (!context.auth?.uid) {
throw new https.HttpsError('unauthenticated', 'Unauthorized.')
}
return next(data, context)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment