Auth middleware in HttpRpcRouter.toHttpApp
Morning all
I would like to add some middleware to my RPC server to decode Firebase tokens and somehow make them available to the services that eventually handle the requests.
I have this:
and some AI generated code for the middleware itself:
(which does not compile,
But I am unsure of the pattern. I am not able to find anything resembling a request context, so how might my services, invoked by the following router, obtain the decoded token?
I would like to add some middleware to my RPC server to decode Firebase tokens and somehow make them available to the services that eventually handle the requests.
I have this:
const HttpLive = HttpRouter.empty.pipe(
HttpRouter.use(decodeAuthTokenMiddleware),
HttpRouter.post('/rpc', toHttpApp(appRouter)),
HttpServer.serve(HttpMiddleware.cors({
allowedHeaders: ['*'],
allowedMethods: ['*']
})),
HttpServer.withLogAddress,
Layer.provide(BunHttpServer.layer({ port: 8080 })),
Layer.provide(searchServiceLayer)
);
BunRuntime.runMain(Layer.launch(HttpLive));const HttpLive = HttpRouter.empty.pipe(
HttpRouter.use(decodeAuthTokenMiddleware),
HttpRouter.post('/rpc', toHttpApp(appRouter)),
HttpServer.serve(HttpMiddleware.cors({
allowedHeaders: ['*'],
allowedMethods: ['*']
})),
HttpServer.withLogAddress,
Layer.provide(BunHttpServer.layer({ port: 8080 })),
Layer.provide(searchServiceLayer)
);
BunRuntime.runMain(Layer.launch(HttpLive));and some AI generated code for the middleware itself:
export const firebaseAuthMiddleware = HttpMiddleware.make(
(app) =>
Effect.gen(function* () {
// Get the incoming request
const req = yield* HttpServerRequest.HttpServerRequest;
// Extract the Authorization header
const authorization = req.headers["authorization"];
// Check if the Authorization header exists and starts with "Bearer"
if (!authorization || !authorization.startsWith("Bearer ")) {
// If the token is missing or invalid, we fail the request
return yield* Effect.fail(new Error("Invalid or missing Authorization header"))
}
const token = authorization.split(" ")[1];
try {
// Verify the token using Firebase Admin SDK
const decodedToken = yield* Effect.promise(() => admin.auth().verifyIdToken(token));
// Attach decoded user data to the request context (or locals)
req.locals = {
...req.locals,
authData: decodedToken, // Add the decoded token info here
};
// Proceed with the next app/middleware in the pipeline
return yield* app;
} catch (error) {
// Authentication failed or token is invalid
return yield* _(
Effect.fail(new Error("Invalid or expired Firebase token"))
);
}
})
);export const firebaseAuthMiddleware = HttpMiddleware.make(
(app) =>
Effect.gen(function* () {
// Get the incoming request
const req = yield* HttpServerRequest.HttpServerRequest;
// Extract the Authorization header
const authorization = req.headers["authorization"];
// Check if the Authorization header exists and starts with "Bearer"
if (!authorization || !authorization.startsWith("Bearer ")) {
// If the token is missing or invalid, we fail the request
return yield* Effect.fail(new Error("Invalid or missing Authorization header"))
}
const token = authorization.split(" ")[1];
try {
// Verify the token using Firebase Admin SDK
const decodedToken = yield* Effect.promise(() => admin.auth().verifyIdToken(token));
// Attach decoded user data to the request context (or locals)
req.locals = {
...req.locals,
authData: decodedToken, // Add the decoded token info here
};
// Proceed with the next app/middleware in the pipeline
return yield* app;
} catch (error) {
// Authentication failed or token is invalid
return yield* _(
Effect.fail(new Error("Invalid or expired Firebase token"))
);
}
})
);(which does not compile,
req.localsreq.locals is not a thing)But I am unsure of the pattern. I am not able to find anything resembling a request context, so how might my services, invoked by the following router, obtain the decoded token?
export const appRouter = RpcRouter.make(
Rpc.effect(
Search, (request) => SearchService.pipe(Effect.andThen((search) => search.search(request)))
),
Rpc.effect(
Books, () => SearchService.pipe(Effect.andThen((search) => search.books()))
),
);export const appRouter = RpcRouter.make(
Rpc.effect(
Search, (request) => SearchService.pipe(Effect.andThen((search) => search.search(request)))
),
Rpc.effect(
Books, () => SearchService.pipe(Effect.andThen((search) => search.books()))
),
);