Move packages/apps back
This commit is contained in:
parent
6eaaf8e9be
commit
5535d6b575
348 changed files with 0 additions and 0 deletions
114
apps/metamigo-api/app/plugins/cloudflare-jwt.ts
Normal file
114
apps/metamigo-api/app/plugins/cloudflare-jwt.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import * as Boom from "@hapi/boom";
|
||||
import * as Hoek from "@hapi/hoek";
|
||||
import * as Hapi from "@hapi/hapi";
|
||||
import { promisify } from "util";
|
||||
import jwt from "jsonwebtoken";
|
||||
import jwksClient, { hapiJwt2KeyAsync } from "jwks-rsa";
|
||||
import type { IAppConfig } from "../../config";
|
||||
|
||||
const CF_JWT_HEADER_NAME = "cf-access-jwt-assertion";
|
||||
const CF_JWT_ALGOS = ["RS256"];
|
||||
|
||||
const verifyToken = (settings: any) => {
|
||||
const { audience, issuer } = settings;
|
||||
const client = jwksClient({
|
||||
jwksUri: `${issuer}/cdn-cgi/access/certs`,
|
||||
});
|
||||
|
||||
return async (token: any) => {
|
||||
const getKey = (header: any, callback: any) => {
|
||||
client.getSigningKey(header.kid, (err, key) => {
|
||||
if (err)
|
||||
throw Boom.serverUnavailable(
|
||||
"failed to fetch cloudflare access jwks"
|
||||
);
|
||||
callback(undefined, key?.getPublicKey());
|
||||
});
|
||||
};
|
||||
|
||||
const opts = {
|
||||
algorithms: CF_JWT_ALGOS,
|
||||
audience,
|
||||
issuer,
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (promisify(jwt.verify) as any)(token, getKey, opts);
|
||||
};
|
||||
};
|
||||
|
||||
const handleCfJwt = (verify: any) => async (
|
||||
request: Hapi.Request,
|
||||
h: Hapi.ResponseToolkit
|
||||
) => {
|
||||
const token = request.headers[CF_JWT_HEADER_NAME];
|
||||
if (token) {
|
||||
try {
|
||||
await verify(token);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return Boom.unauthorized("invalid cloudflare access token");
|
||||
}
|
||||
}
|
||||
|
||||
return h.continue;
|
||||
};
|
||||
|
||||
const defaultOpts = {
|
||||
issuer: undefined,
|
||||
audience: undefined,
|
||||
strategyName: "clouflareaccess",
|
||||
validate: undefined,
|
||||
};
|
||||
|
||||
const cfJwtRegister = async (server: Hapi.Server, options: any): Promise<void> => {
|
||||
server.dependency(["hapi-auth-jwt2"]);
|
||||
const settings = Hoek.applyToDefaults(defaultOpts, options);
|
||||
const verify = verifyToken(settings);
|
||||
|
||||
const { validate, strategyName, audience, issuer } = settings;
|
||||
server.ext("onPreAuth", handleCfJwt(verify));
|
||||
|
||||
server.auth.strategy(strategyName!, "jwt", {
|
||||
key: hapiJwt2KeyAsync({
|
||||
jwksUri: `${issuer}/cdn-cgi/access/certs`,
|
||||
}),
|
||||
cookieKey: false,
|
||||
urlKey: false,
|
||||
headerKey: CF_JWT_HEADER_NAME,
|
||||
validate,
|
||||
verifyOptions: {
|
||||
audience,
|
||||
issuer,
|
||||
algorithms: ["RS256"],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const registerCloudflareAccessJwt = async (
|
||||
server: Hapi.Server,
|
||||
config: IAppConfig
|
||||
): Promise<void> => {
|
||||
const { audience, domain } = config.cfaccess;
|
||||
// only enable this plugin if cloudflare access config is configured
|
||||
if (audience && domain) {
|
||||
server.log(["auth"], "cloudflare access authorization enabled");
|
||||
await server.register({
|
||||
plugin: {
|
||||
name: "cloudflare-jwt",
|
||||
version: "0.0.1",
|
||||
register: cfJwtRegister,
|
||||
},
|
||||
options: {
|
||||
issuer: `https://${domain}`,
|
||||
audience,
|
||||
validate: (decoded: any, _request: any) => {
|
||||
const { email, name } = decoded;
|
||||
return {
|
||||
isValid: true,
|
||||
credentials: { user: { email, name } },
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue