Since next-auth doesn't use JWT anymore, remove the nextauth-jwt plugin
..and replace it with the Auth Bearer plugin.
This commit is contained in:
parent
24d52eef3d
commit
bdad5f551c
7 changed files with 52 additions and 112 deletions
|
|
@ -27,6 +27,7 @@
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"graphile-migrate": "^1.4.1",
|
"graphile-migrate": "^1.4.1",
|
||||||
"graphile-worker": "^0.13.0",
|
"graphile-worker": "^0.13.0",
|
||||||
|
"hapi-auth-bearer-token": "^8.0.0",
|
||||||
"hapi-auth-jwt2": "^10.4.0",
|
"hapi-auth-jwt2": "^10.4.0",
|
||||||
"hapi-postgraphile": "^0.11.0",
|
"hapi-postgraphile": "^0.11.0",
|
||||||
"hapi-swagger": "^16.0.1",
|
"hapi-swagger": "^16.0.1",
|
||||||
|
|
|
||||||
22
apps/metamigo-api/src/app/plugins/auth-bearer.ts
Normal file
22
apps/metamigo-api/src/app/plugins/auth-bearer.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import type * as Hapi from "@hapi/hapi";
|
||||||
|
import AuthBearer from "hapi-auth-bearer-token";
|
||||||
|
import { IAppConfig } from "@digiresilience/metamigo-config";
|
||||||
|
import { IMetamigoRepositories } from "@digiresilience/metamigo-common";
|
||||||
|
|
||||||
|
export const registerAuthBearer = async (
|
||||||
|
server: Hapi.Server,
|
||||||
|
config: IAppConfig
|
||||||
|
): Promise<void> => {
|
||||||
|
await server.register(AuthBearer);
|
||||||
|
|
||||||
|
server.auth.strategy("session-id-bearer-token", "bearer-access-token", {
|
||||||
|
allowQueryToken: false,
|
||||||
|
validate: async (request: Hapi.Request, token: string, h: Hapi.ResponseToolkit) => {
|
||||||
|
const repos = request.db() as IMetamigoRepositories;
|
||||||
|
const session = await repos.sessions.findBy({ sessionToken: token });
|
||||||
|
const isValid = !!session;
|
||||||
|
const credentials = { token };
|
||||||
|
return { isValid, credentials };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -7,8 +7,8 @@ import type { IAppConfig } from "../../config";
|
||||||
import { dbInitOptions, IRepositories } from "@digiresilience/metamigo-db";
|
import { dbInitOptions, IRepositories } from "@digiresilience/metamigo-db";
|
||||||
import { registerNextAuth } from "./hapi-nextauth.js";
|
import { registerNextAuth } from "./hapi-nextauth.js";
|
||||||
import { registerSwagger } from "./swagger.js";
|
import { registerSwagger } from "./swagger.js";
|
||||||
import { registerNextAuthJwt } from "./nextauth-jwt.js";
|
|
||||||
import { registerCloudflareAccessJwt } from "./cloudflare-jwt.js";
|
import { registerCloudflareAccessJwt } from "./cloudflare-jwt.js";
|
||||||
|
import { registerAuthBearer } from "./auth-bearer.js";
|
||||||
import pg from "pg-promise/typescript/pg-subset";
|
import pg from "pg-promise/typescript/pg-subset";
|
||||||
|
|
||||||
export const register = async (
|
export const register = async (
|
||||||
|
|
@ -34,6 +34,6 @@ export const register = async (
|
||||||
|
|
||||||
await registerNextAuth(server, config);
|
await registerNextAuth(server, config);
|
||||||
await registerSwagger(server);
|
await registerSwagger(server);
|
||||||
await registerNextAuthJwt(server, config);
|
|
||||||
await registerCloudflareAccessJwt(server, config);
|
await registerCloudflareAccessJwt(server, config);
|
||||||
|
await registerAuthBearer(server, config);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
import * as Hoek from "@hapi/hoek";
|
|
||||||
import * as Hapi from "@hapi/hapi";
|
|
||||||
import type { IAppConfig } from "../../config";
|
|
||||||
|
|
||||||
// hapi-auth-jwt2 expects the key to be a raw key
|
|
||||||
const jwkToHapiAuthJwt2 = (jwkString) => {
|
|
||||||
try {
|
|
||||||
const jwk = JSON.parse(jwkString);
|
|
||||||
return Buffer.from(jwk.k, "base64");
|
|
||||||
} catch {
|
|
||||||
throw new Error(
|
|
||||||
"Failed to parse key for JWT verification. This is probably an application configuration error."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const jwtDefaults = {
|
|
||||||
jwkeysB64: undefined,
|
|
||||||
validate: undefined,
|
|
||||||
strategyName: "nextauth-jwt",
|
|
||||||
};
|
|
||||||
|
|
||||||
const jwtRegister = async (server: Hapi.Server, options): Promise<void> => {
|
|
||||||
server.dependency(["hapi-auth-jwt2"]);
|
|
||||||
const settings = Hoek.applyToDefaults(jwtDefaults, options);
|
|
||||||
const key = settings.jwkeysB64.map((k) => jwkToHapiAuthJwt2(k));
|
|
||||||
|
|
||||||
if (!settings.strategyName) {
|
|
||||||
throw new Error("Missing strategy name in nextauth-jwt pluginsettings!");
|
|
||||||
}
|
|
||||||
|
|
||||||
server.auth.strategy(settings.strategyName, "jwt", {
|
|
||||||
key,
|
|
||||||
cookieKey: false,
|
|
||||||
urlKey: false,
|
|
||||||
validate: settings.validate,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const registerNextAuthJwt = async (
|
|
||||||
server: Hapi.Server,
|
|
||||||
config: IAppConfig
|
|
||||||
): Promise<void> => {
|
|
||||||
if (config.nextAuth.signingKey) {
|
|
||||||
await server.register({
|
|
||||||
plugin: {
|
|
||||||
name: "nextauth-jwt",
|
|
||||||
version: "0.0.2",
|
|
||||||
register: jwtRegister,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
jwkeysB64: [config.nextAuth.signingKey],
|
|
||||||
async validate(decoded, request: Hapi.Request) {
|
|
||||||
const { email, name, role } = decoded;
|
|
||||||
const user = await request.db().users.findBy({ email });
|
|
||||||
if (!config.isProd) {
|
|
||||||
server.logger.info(
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
name,
|
|
||||||
role,
|
|
||||||
},
|
|
||||||
"nextauth-jwt authorizing request"
|
|
||||||
);
|
|
||||||
// server.logger.info({ user }, "nextauth-jwt user result");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isValid: Boolean(user && user.isActive),
|
|
||||||
// this credentials object is made available in every request
|
|
||||||
// at `request.auth.credentials`
|
|
||||||
credentials: { email, name, role },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (config.isProd) {
|
|
||||||
throw new Error("Missing nextauth.signingKey configuration value.");
|
|
||||||
} else {
|
|
||||||
server.log(
|
|
||||||
["warn"],
|
|
||||||
"Missing nextauth.signingKey configuration value. Authentication of nextauth endpoints disabled!"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// @hapi/jwt expects the key in its own format
|
|
||||||
/* UNUSED
|
|
||||||
const _jwkToHapiJwt = (jwkString) => {
|
|
||||||
try {
|
|
||||||
const jwk = JSON.parse(jwkString);
|
|
||||||
const rawKey = Buffer.from(jwk.k, "base64");
|
|
||||||
return {
|
|
||||||
key: rawKey,
|
|
||||||
algorithms: [jwk.alg],
|
|
||||||
kid: jwk.kid,
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
throw new Error(
|
|
||||||
"Failed to parse key for JWT verification. This is probably an application configuration error."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Toys from "@hapipal/toys";
|
||||||
export const withDefaults = Toys.withRouteDefaults({
|
export const withDefaults = Toys.withRouteDefaults({
|
||||||
options: {
|
options: {
|
||||||
cors: true,
|
cors: true,
|
||||||
auth: "nextauth-jwt",
|
auth: "session-id-bearer-token",
|
||||||
validate: {
|
validate: {
|
||||||
failAction: Metamigo.validatingFailAction,
|
failAction: Metamigo.validatingFailAction,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,10 @@ const build = async (config: IAppConfig): Promise<Glue.Manifest> => {
|
||||||
route: {
|
route: {
|
||||||
path: "/graphql",
|
path: "/graphql",
|
||||||
options: {
|
options: {
|
||||||
auth: false,
|
auth: {
|
||||||
// auth: {
|
strategies: ["session-id-bearer-token"],
|
||||||
// strategies: ["nextauth-jwt"],
|
mode: "optional",
|
||||||
// mode: "optional",
|
},
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pgConfig: config.postgraphile.authConnection,
|
pgConfig: config.postgraphile.authConnection,
|
||||||
|
|
|
||||||
22
package-lock.json
generated
22
package-lock.json
generated
|
|
@ -137,6 +137,7 @@
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"graphile-migrate": "^1.4.1",
|
"graphile-migrate": "^1.4.1",
|
||||||
"graphile-worker": "^0.13.0",
|
"graphile-worker": "^0.13.0",
|
||||||
|
"hapi-auth-bearer-token": "^8.0.0",
|
||||||
"hapi-auth-jwt2": "^10.4.0",
|
"hapi-auth-jwt2": "^10.4.0",
|
||||||
"hapi-postgraphile": "^0.11.0",
|
"hapi-postgraphile": "^0.11.0",
|
||||||
"hapi-swagger": "^16.0.1",
|
"hapi-swagger": "^16.0.1",
|
||||||
|
|
@ -11597,6 +11598,27 @@
|
||||||
"topo": "3.x.x"
|
"topo": "3.x.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hapi-auth-bearer-token": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hapi-auth-bearer-token/-/hapi-auth-bearer-token-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-1YeUlwhhky8tnNx9bOQPB/TvsEwbgcYwAZ6DAvHlK+tHRiMbXU+2HNE8qpRia+oj21W2K/omaxyZIB5dOzTPoA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@hapi/hoek": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@hapi/boom": ">=7.x.x",
|
||||||
|
"@hapi/hapi": ">=19.x.x",
|
||||||
|
"joi": ">=17.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hapi-auth-bearer-token/node_modules/@hapi/hoek": {
|
||||||
|
"version": "9.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||||
|
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
||||||
|
},
|
||||||
"node_modules/hapi-auth-jwt2": {
|
"node_modules/hapi-auth-jwt2": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/hapi-auth-jwt2/-/hapi-auth-jwt2-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/hapi-auth-jwt2/-/hapi-auth-jwt2-10.4.0.tgz",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue