2023-03-13 08:24:20 +00:00
|
|
|
import * as Hapi from "@hapi/hapi";
|
|
|
|
|
import * as Hoek from "@hapi/hoek";
|
2023-06-02 14:05:20 +00:00
|
|
|
import Joi from "joi";
|
2023-03-13 08:24:20 +00:00
|
|
|
|
2023-06-02 14:05:20 +00:00
|
|
|
import { NextAuthPluginOptions } from "./types.js";
|
|
|
|
|
import * as Routes from "./routes.js";
|
2023-03-13 08:24:20 +00:00
|
|
|
|
|
|
|
|
const minimumProfileSchema = Joi.object()
|
|
|
|
|
.keys({
|
|
|
|
|
email: Joi.string().email().required(),
|
|
|
|
|
})
|
|
|
|
|
.unknown(true);
|
|
|
|
|
|
|
|
|
|
const minimumUserSchema = Joi.object()
|
|
|
|
|
.keys({
|
2023-06-06 10:28:29 +00:00
|
|
|
userId: Joi.string().required(),
|
2023-03-13 08:24:20 +00:00
|
|
|
email: Joi.string().email().required(),
|
|
|
|
|
})
|
|
|
|
|
.unknown(true);
|
|
|
|
|
|
|
|
|
|
const minimumSessionSchema = Joi.object()
|
|
|
|
|
.keys({
|
|
|
|
|
id: Joi.string().required(),
|
|
|
|
|
userId: Joi.string().required(),
|
|
|
|
|
expires: Joi.number().required(),
|
|
|
|
|
sessionToken: Joi.string().required(),
|
|
|
|
|
accessToken: Joi.string().required(),
|
|
|
|
|
})
|
|
|
|
|
.unknown(true);
|
|
|
|
|
|
|
|
|
|
const defaultOptions = {
|
|
|
|
|
basePath: "/api/nextauth",
|
|
|
|
|
validators: {
|
|
|
|
|
userId: Joi.string().required(),
|
|
|
|
|
profile: minimumProfileSchema,
|
|
|
|
|
user: minimumUserSchema,
|
|
|
|
|
session: minimumSessionSchema,
|
|
|
|
|
},
|
|
|
|
|
tags: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const validateAuth = (sharedSecret) => (request, username, password) => {
|
|
|
|
|
// we follow stripe's lead here for authenticating with basic auth
|
|
|
|
|
// the shared secret should be bassed as the basic auth username, the password should be empty
|
|
|
|
|
if (password !== "") {
|
|
|
|
|
console.error(
|
|
|
|
|
"hapi-nextauth: attempted authentication with basic auth password. only the username should be defined."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return { isValid: false, credentials: {} };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isValid = username === sharedSecret;
|
|
|
|
|
const credentials = {
|
|
|
|
|
id: "nextauth-frontend",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return { isValid, credentials };
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-26 08:27:16 +00:00
|
|
|
const register = async (
|
2023-03-13 08:24:20 +00:00
|
|
|
server: Hapi.Server,
|
2023-05-26 08:27:16 +00:00
|
|
|
pluginOpts?: any
|
2023-03-13 08:24:20 +00:00
|
|
|
): Promise<void> => {
|
2023-05-26 08:27:16 +00:00
|
|
|
const options: any =
|
2023-03-13 09:34:36 +00:00
|
|
|
Hoek.applyToDefaults(
|
|
|
|
|
// a little type gymnastics here to workaround poor typing
|
2023-05-26 08:27:16 +00:00
|
|
|
defaultOptions as any,
|
2023-03-13 09:34:36 +00:00
|
|
|
pluginOpts
|
2023-05-26 08:27:16 +00:00
|
|
|
) as any;
|
2023-03-13 08:24:20 +00:00
|
|
|
|
|
|
|
|
if (!options.nextAuthAdapterFactory) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
"You must pass a NextAuthAdapterFactory instance to hapi-nextauth."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server.validator(Joi);
|
|
|
|
|
let auth = "hapi-nextauth";
|
|
|
|
|
if (options.sharedSecret) {
|
|
|
|
|
server.dependency(["@hapi/basic"]);
|
|
|
|
|
server.auth.strategy(auth, "basic", {
|
|
|
|
|
validate: validateAuth(options.sharedSecret),
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.warn(
|
|
|
|
|
"hapi-nextauth: AUTHENTICATION OF FRONTEND TO NEXTAUTH ENDPOINTS DISABLED!"
|
|
|
|
|
);
|
|
|
|
|
auth = undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await Routes.register(server, options, auth);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const nextAuthPlugin = {
|
|
|
|
|
register,
|
|
|
|
|
name: "@digiresilience/hapi-nextauth",
|
|
|
|
|
version: "0.0.3",
|
|
|
|
|
};
|
|
|
|
|
|
2023-06-02 14:05:20 +00:00
|
|
|
export * from "./types.js";
|
2023-03-13 08:24:20 +00:00
|
|
|
export default nextAuthPlugin;
|