metamigo: do nextauth v3 -> v4 upgrades
This commit is contained in:
parent
a33f80c497
commit
45f8cb1234
13 changed files with 158 additions and 123 deletions
|
|
@ -40,4 +40,9 @@ npm install --workspace=metamigo-common
|
|||
|
||||
```
|
||||
turbo run build --filter metamigo-cli
|
||||
```
|
||||
|
||||
|
||||
npm run dev -- --filter @digiresilience/metamigo-cli
|
||||
npm run migrate
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"@digiresilience/montar": "*",
|
||||
"@digiresilience/metamigo-config": "*",
|
||||
"@digiresilience/metamigo-common": "*",
|
||||
"@digiresilience/metamigo-db": "*",
|
||||
"@digiresilience/metamigo-api": "*",
|
||||
"@digiresilience/metamigo-worker": "*",
|
||||
|
|
@ -32,7 +33,9 @@
|
|||
"typescript": "^5.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"migrate": "NODE_ENV=development node --unhandled-rejections=strict build/main/index.js db -- migrate",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"dev": "NODE_ENV=development node --unhandled-rejections=strict build/main/index.js api",
|
||||
"cli": "NODE_ENV=development node --unhandled-rejections=strict build/main/index.js",
|
||||
"fix:lint": "eslint src --ext .ts --fix",
|
||||
"fmt": "prettier \"src/**/*.ts\" --write",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { IAppConfig, IAppConvict } from "@digiresilience/metamigo-config";
|
|||
import { loadConfigRaw } from "@digiresilience/metamigo-config";
|
||||
|
||||
export const genConf = async (): Promise<void> => {
|
||||
const c = await loadConfigRaw() as any;
|
||||
const c = (await loadConfigRaw()) as any;
|
||||
const generated = generateConfig(c) as any;
|
||||
console.log(generated);
|
||||
};
|
||||
|
|
@ -17,6 +17,6 @@ export const genSchema = async (): Promise<void> => {
|
|||
};
|
||||
|
||||
export const listConfig = async (): Promise<void> => {
|
||||
const c = await loadConfigRaw() as any;
|
||||
const c = (await loadConfigRaw()) as any;
|
||||
printConfigOptions(c);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import pg from "pg";
|
|||
import { loadConfig } from "@digiresilience/metamigo-config";
|
||||
import { getPostGraphileOptions } from "@digiresilience/metamigo-db";
|
||||
|
||||
const {Pool} = pg;
|
||||
const { Pool } = pg;
|
||||
|
||||
export const exportGraphqlSchema = async (): Promise<void> => {
|
||||
const config = await loadConfig();
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
METAMIGO_CONFIG=.metamigo.local.json
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
/* eslint-disable unicorn/no-null */
|
||||
/* eslint-disable max-params */
|
||||
import type { Adapter } from "next-auth/adapters";
|
||||
// @ts-expect-error: Missing export
|
||||
import type { AppOptions } from "next-auth";
|
||||
import type { Adapter, AdapterAccount, AdapterSession, AdapterUser } from "next-auth/adapters";
|
||||
import * as Wreck from "@hapi/wreck";
|
||||
import * as Boom from "@hapi/boom";
|
||||
|
||||
|
|
@ -70,7 +67,7 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
json: "force",
|
||||
});
|
||||
|
||||
async function getAdapter(_appOptions: AppOptions) {
|
||||
function getAdapter(): Adapter {
|
||||
async function createUser(profile: Profile) {
|
||||
try {
|
||||
if (!profile.createdBy) profile = { ...profile, createdBy: "nextauth" };
|
||||
|
|
@ -106,19 +103,17 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
}
|
||||
}
|
||||
|
||||
async function getUserByProviderAccountId(
|
||||
providerId: string,
|
||||
providerAccountId: string
|
||||
) {
|
||||
async function getUserByAccount({ providerAccountId, provider }: { providerAccountId: string, provider: string }) {
|
||||
try {
|
||||
const { payload } = await wreck.get(
|
||||
`getUserByProviderAccountId/${providerId}/${providerAccountId}`
|
||||
`getUserByAccount/${provider}/${providerAccountId}`
|
||||
);
|
||||
|
||||
return payload;
|
||||
} catch (error) {
|
||||
if (Boom.isBoom(error, 404)) return null;
|
||||
throw new Error("GET_USER_BY_PROVIDER_ACCOUNT_ID");
|
||||
console.log(error)
|
||||
throw new Error("GET_USER_BY_ACCOUNT");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,51 +130,39 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
}
|
||||
|
||||
async function linkAccount(
|
||||
userId: string,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken: string,
|
||||
accessToken: string,
|
||||
accessTokenExpires: number
|
||||
account: AdapterAccount
|
||||
) {
|
||||
try {
|
||||
const payload = {
|
||||
userId,
|
||||
providerId,
|
||||
providerType,
|
||||
providerAccountId: `${providerAccountId}`, // must be a string
|
||||
refreshToken,
|
||||
accessToken,
|
||||
accessTokenExpires,
|
||||
};
|
||||
await wreck.put("linkAccount", {
|
||||
payload,
|
||||
});
|
||||
} catch {
|
||||
await wreck.put("linkAccount", {payload: account} as any );
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
throw new Error("LINK_ACCOUNT_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
async function createSession(user: User) {
|
||||
try {
|
||||
const { payload } = await wreck.post("createSession", {
|
||||
const { payload }: {payload: AdapterSession} = await wreck.post("createSession", {
|
||||
payload: user,
|
||||
});
|
||||
|
||||
payload.expires = new Date(payload.expires)
|
||||
return payload;
|
||||
} catch {
|
||||
} catch(error) {
|
||||
console.log(error)
|
||||
throw new Error("CREATE_SESSION_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
async function getSession(sessionToken: string) {
|
||||
async function getSessionAndUser(sessionToken: string) {
|
||||
try {
|
||||
const { payload } = await wreck.get(`getSession/${sessionToken}`);
|
||||
return payload;
|
||||
const {payload}: {payload: any} = await wreck.get(`getSessionAndUser/${sessionToken}`);
|
||||
const { session, user }: {session: AdapterSession, user: AdapterUser} = payload;
|
||||
session.expires = new Date(session.expires)
|
||||
return {session, user}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
if (Boom.isBoom(error, 404)) return null;
|
||||
throw new Error("GET_SESSION_ERROR");
|
||||
throw new Error("GET_SESSION_AND_USER_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,21 +196,18 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
createUser,
|
||||
getUser,
|
||||
getUserByEmail,
|
||||
getUserByProviderAccountId,
|
||||
getUserByAccount,
|
||||
updateUser,
|
||||
// deleteUser,
|
||||
linkAccount,
|
||||
// unlinkAccount,
|
||||
createSession,
|
||||
getSession,
|
||||
getSessionAndUser,
|
||||
updateSession,
|
||||
deleteSession,
|
||||
// @ts-expect-error: Type error
|
||||
} as AdapterInstance<Profile, User, Session, unknown>;
|
||||
}
|
||||
|
||||
return {
|
||||
// @ts-expect-error: non-existent property
|
||||
getAdapter,
|
||||
};
|
||||
return getAdapter();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Cognito from "next-auth/providers/cognito";
|
|||
import { loadConfig, IAppConfig } from "@digiresilience/metamigo-config";
|
||||
import { MetamigoAdapter } from "../../../lib/nextauth-adapter";
|
||||
import { CloudflareAccessProvider } from "../../../lib/cloudflare";
|
||||
import { AdapterSession, AdapterUser } from "next-auth/adapters";
|
||||
|
||||
const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
||||
const { nextAuth, cfaccess } = config;
|
||||
|
|
@ -72,11 +73,6 @@ const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
|||
providers,
|
||||
adapter,
|
||||
callbacks: {
|
||||
async session(session: any, token: any) {
|
||||
// make the user id available in the react client
|
||||
session.user.id = token.userId;
|
||||
return session;
|
||||
},
|
||||
async jwt(token: any, user: any) {
|
||||
const isSignIn = Boolean(user);
|
||||
// Add auth_time to token on signin in
|
||||
|
|
|
|||
45
package-lock.json
generated
45
package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
|||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"dotenv-cli": "latest",
|
||||
"prettier": "^2.8.8"
|
||||
}
|
||||
},
|
||||
|
|
@ -8189,8 +8190,7 @@
|
|||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||
},
|
||||
"node_modules/comma-separated-tokens": {
|
||||
"version": "2.0.3",
|
||||
|
|
@ -8433,7 +8433,6 @@
|
|||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
|
|
@ -8964,6 +8963,30 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-cli": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.2.1.tgz",
|
||||
"integrity": "sha512-ODHbGTskqRtXAzZapDPvgNuDVQApu4oKX8lZW7Y0+9hKA6le1ZJlyRS687oU9FXjOVEDU/VFV6zI125HzhM1UQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^10.0.0",
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"dotenv": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
|
||||
"integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer2": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
|
||||
|
|
@ -10233,8 +10256,7 @@
|
|||
"node_modules/fast-copy": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz",
|
||||
"integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA=="
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
|
|
@ -10319,8 +10341,7 @@
|
|||
"node_modules/fast-safe-stringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.15.0",
|
||||
|
|
@ -11635,7 +11656,6 @@
|
|||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz",
|
||||
"integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^8.0.0",
|
||||
"readable-stream": "^3.6.0"
|
||||
|
|
@ -11645,7 +11665,6 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
|
|
@ -11654,7 +11673,6 @@
|
|||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
|
||||
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
|
|
@ -11673,7 +11691,6 @@
|
|||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
|
|
@ -11685,7 +11702,6 @@
|
|||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
|
@ -11699,7 +11715,6 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
|
|
@ -14216,7 +14231,6 @@
|
|||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
|
||||
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
|
|
@ -16980,7 +16994,6 @@
|
|||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.0.tgz",
|
||||
"integrity": "sha512-zKFjYXBzLaLTEAN1ayKpHXtL5UeRQC7R3lvhKe7fWs7hIVEjKGG/qIXwQt9HmeUp71ogUd/YcW+LmMwRp4KT6Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.7",
|
||||
"dateformat": "^4.6.3",
|
||||
|
|
@ -17005,7 +17018,6 @@
|
|||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz",
|
||||
"integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
|
|
@ -21976,6 +21988,7 @@
|
|||
"next-auth": "^4.22.1",
|
||||
"pg-promise": "^11.4.3",
|
||||
"pino": "^8.14.1",
|
||||
"pino-pretty": "^10.0.0",
|
||||
"prom-client": "^14.x.x",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "dotenv -- turbo run dev"
|
||||
"dev": "dotenv -- turbo run dev",
|
||||
"migrate": "dotenv -- npm run migrate --workspace=@digiresilience/metamigo-cli"
|
||||
},
|
||||
"packageManager": "npm@9.3.1",
|
||||
"workspaces": [
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
"url": "git+https://gitlab.com/digiresilience/link/link-stack.git"
|
||||
},
|
||||
"author": "Darren Clarke",
|
||||
"license": "ISC",
|
||||
"dlicense": "ISC",
|
||||
"overrides": {
|
||||
"@mui/styles": {
|
||||
"react": "18.2.0"
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^2.8.8"
|
||||
"prettier": "^2.8.8",
|
||||
"dotenv-cli": "latest"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const minimumProfileSchema = Joi.object()
|
|||
|
||||
const minimumUserSchema = Joi.object()
|
||||
.keys({
|
||||
id: Joi.string().required(),
|
||||
userId: Joi.string().required(),
|
||||
email: Joi.string().email().required(),
|
||||
})
|
||||
.unknown(true);
|
||||
|
|
|
|||
|
|
@ -93,13 +93,13 @@ export const register = async <TUser, TProfile>(
|
|||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${basePath}/getUserByProviderAccountId/{providerId}/{providerAccountId}`,
|
||||
path: `${basePath}/getUserByAccount/{provider}/{providerAccountId}`,
|
||||
options: {
|
||||
auth,
|
||||
tags,
|
||||
validate: {
|
||||
params: {
|
||||
providerId: Joi.string(),
|
||||
provider: Joi.string(),
|
||||
providerAccountId: Joi.string(),
|
||||
},
|
||||
},
|
||||
|
|
@ -107,10 +107,10 @@ export const register = async <TUser, TProfile>(
|
|||
request: Hapi.Request,
|
||||
h: ResponseToolkit
|
||||
): Promise<ResponseObject> {
|
||||
const { providerId, providerAccountId } = request.params;
|
||||
const { provider, providerAccountId } = request.params;
|
||||
const r = await opts
|
||||
.nextAuthAdapterFactory(request)
|
||||
.getUserByProviderAccountId(providerId, providerAccountId);
|
||||
.getUserByAccount(provider, providerAccountId);
|
||||
if (!r) return h.response().code(404);
|
||||
return h.response(r as object);
|
||||
},
|
||||
|
|
@ -148,14 +148,15 @@ export const register = async <TUser, TProfile>(
|
|||
tags,
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
userId,
|
||||
providerId: Joi.string(),
|
||||
providerType: Joi.string(),
|
||||
providerAccountId: Joi.string(),
|
||||
refreshToken: Joi.string().optional().allow(null),
|
||||
accessToken: Joi.string().optional().allow(null),
|
||||
accessTokenExpires: Joi.number().optional().allow(null),
|
||||
}).options({ presence: "required" }),
|
||||
// https://next-auth.js.org/getting-started/upgrade-v4#schema-changes
|
||||
userId: Joi.string().required(),
|
||||
provider: Joi.string().required(),
|
||||
type: Joi.string().required(),
|
||||
providerAccountId: Joi.string().required(),
|
||||
refresh_token: Joi.string().optional().allow(null),
|
||||
access_token: Joi.string().optional().allow(null),
|
||||
expires_at: Joi.number().optional().allow(null),
|
||||
}).unknown(true),
|
||||
},
|
||||
async handler(
|
||||
request: Hapi.Request,
|
||||
|
|
@ -193,7 +194,11 @@ export const register = async <TUser, TProfile>(
|
|||
auth,
|
||||
tags,
|
||||
validate: {
|
||||
payload: user,
|
||||
payload: Joi.object({
|
||||
userId: Joi.string().required(),
|
||||
sessionToken: Joi.string().required(),
|
||||
expires: Joi.string().isoDate().required(),
|
||||
}),
|
||||
},
|
||||
async handler(
|
||||
request: Hapi.Request,
|
||||
|
|
@ -210,7 +215,7 @@ export const register = async <TUser, TProfile>(
|
|||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${basePath}/getSession/{sessionToken}`,
|
||||
path: `${basePath}/getSessionAndUser/{sessionToken}`,
|
||||
options: {
|
||||
auth,
|
||||
tags,
|
||||
|
|
@ -226,7 +231,7 @@ export const register = async <TUser, TProfile>(
|
|||
const token = request.params.sessionToken;
|
||||
const r = await opts
|
||||
.nextAuthAdapterFactory(request)
|
||||
.getSession(token);
|
||||
.getSessionAndUser(token);
|
||||
if (!r) return h.response().code(404);
|
||||
return h.response(r as object);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
/* eslint-disable unicorn/no-null,max-params */
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import omit from "lodash/omit.js";
|
||||
import type { IMetamigoRepositories } from "../records/index.js";
|
||||
import { IMetamigoRepositories, idKeysOf } from "../records/index.js";
|
||||
import type { UnsavedAccount } from "../records/account.js";
|
||||
import type { UserId, UnsavedUser, SavedUser } from "../records/user.js";
|
||||
import type { UnsavedSession, SavedSession } from "../records/session.js";
|
||||
import {
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
} from "next-auth/adapters.js";
|
||||
import { ReadableStreamDefaultController } from "stream/web";
|
||||
|
||||
// Sessions expire after 30 days of being idle
|
||||
export const defaultSessionMaxAge = 30 * 24 * 60 * 60 * 1000;
|
||||
|
|
@ -23,7 +29,7 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories> {
|
|||
private repos: TRepositories,
|
||||
private readonly sessionMaxAge = defaultSessionMaxAge,
|
||||
private readonly sessionUpdateAge = defaulteSessionUpdateAge
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async createUser(profile: UnsavedUser): Promise<SavedUser> {
|
||||
// @ts-expect-error Typescript doesn't like lodash's omit()
|
||||
|
|
@ -56,12 +62,12 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories> {
|
|||
return user;
|
||||
}
|
||||
|
||||
async getUserByProviderAccountId(
|
||||
providerId: string,
|
||||
async getUserByAccount(
|
||||
provider: string,
|
||||
providerAccountId: string
|
||||
): Promise<SavedUser | null> {
|
||||
const account = await this.repos.accounts.findBy({
|
||||
compoundId: getCompoundId(providerId, providerAccountId),
|
||||
compoundId: getCompoundId(provider, providerAccountId),
|
||||
});
|
||||
if (!account) return null;
|
||||
|
||||
|
|
@ -72,15 +78,16 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories> {
|
|||
return this.repos.users.update(user);
|
||||
}
|
||||
|
||||
async linkAccount(
|
||||
userId: string,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken: string,
|
||||
accessToken: string,
|
||||
accessTokenExpires: number
|
||||
): Promise<void> {
|
||||
async linkAccount(adapterAccount: AdapterAccount): Promise<void> {
|
||||
const {
|
||||
userId,
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
provider: providerId,
|
||||
providerAccountId,
|
||||
expires_at: accessTokenExpires,
|
||||
type: providerType,
|
||||
} = adapterAccount;
|
||||
const exists = await this.repos.users.existsById({ id: userId });
|
||||
if (!exists) return;
|
||||
const account: UnsavedAccount = {
|
||||
|
|
@ -109,7 +116,13 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories> {
|
|||
});
|
||||
}
|
||||
|
||||
createSession(user: SavedUser): Promise<SavedSession> {
|
||||
createSession({
|
||||
sessionToken,
|
||||
userId,
|
||||
}: {
|
||||
sessionToken: string;
|
||||
userId: string;
|
||||
}): Promise<SavedSession> {
|
||||
let expires;
|
||||
if (this.sessionMaxAge) {
|
||||
const dateExpires = new Date(Date.now() + this.sessionMaxAge);
|
||||
|
|
@ -118,22 +131,41 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories> {
|
|||
|
||||
const session: UnsavedSession = {
|
||||
expires,
|
||||
userId: user.id,
|
||||
sessionToken: randomToken(),
|
||||
userId,
|
||||
sessionToken,
|
||||
//sessionToken: randomToken(),
|
||||
accessToken: randomToken(),
|
||||
};
|
||||
|
||||
return this.repos.sessions.insert(session);
|
||||
}
|
||||
|
||||
async getSession(sessionToken: string): Promise<SavedSession | null> {
|
||||
async getSessionAndUser(
|
||||
sessionToken: string
|
||||
): Promise<{ session: AdapterSession; user: AdapterUser } | null> {
|
||||
const session = await this.repos.sessions.findBy({ sessionToken });
|
||||
if (!session) return null;
|
||||
if (session && session.expires && new Date() > session.expires) {
|
||||
this.repos.sessions.remove(session);
|
||||
return null;
|
||||
}
|
||||
|
||||
return session;
|
||||
const user = await this.repos.users.findById({ id: session.userId });
|
||||
if (!user) return null;
|
||||
|
||||
const adapterSession: AdapterSession = {
|
||||
userId: session.userId,
|
||||
expires: session.expires,
|
||||
sessionToken: sessionToken,
|
||||
};
|
||||
|
||||
const adapterUser: AdapterUser = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
emailVerified: user.emailVerified,
|
||||
};
|
||||
|
||||
return { session: adapterSession, user: adapterUser };
|
||||
}
|
||||
|
||||
async updateSession(
|
||||
|
|
|
|||
|
|
@ -9,20 +9,20 @@ export const configSchema = {
|
|||
doc: "The postgres connection url.",
|
||||
format: "uri",
|
||||
default: "postgresql://metamigo:metamigo@127.0.0.1:5435/metamigo_dev",
|
||||
env: "DATABASE_URL",
|
||||
env: "METAMIGO_DATABASE_URL",
|
||||
sensitive: true,
|
||||
},
|
||||
name: {
|
||||
doc: "The name of the postgres database",
|
||||
format: String,
|
||||
default: "metamigo_dev",
|
||||
env: "DATABASE_NAME",
|
||||
env: "METAMIGO_DATABASE_NAME",
|
||||
},
|
||||
owner: {
|
||||
doc: "The username of the postgres database owner",
|
||||
format: String,
|
||||
default: "metamigo",
|
||||
env: "DATABASE_OWNER",
|
||||
env: "METAMIGO_DATABASE_OWNER",
|
||||
},
|
||||
},
|
||||
worker: {
|
||||
|
|
@ -30,19 +30,19 @@ export const configSchema = {
|
|||
doc: "The postgres connection url for the worker database.",
|
||||
format: "uri",
|
||||
default: "postgresql://metamigo:metamigo@127.0.0.1:5435/metamigo_dev",
|
||||
env: "WORKER_DATABASE_URL",
|
||||
env: "METAMIGO_WORKER_DATABASE_URL",
|
||||
},
|
||||
concurrency: {
|
||||
doc: "The number of jobs to run concurrently",
|
||||
default: 1,
|
||||
format: "positiveInt",
|
||||
env: "WORKER_CONCURRENT_JOBS",
|
||||
env: "METAMIGO_WORKER_CONCURRENT_JOBS",
|
||||
},
|
||||
pollInterval: {
|
||||
doc: "How long to wait between polling for jobs in milliseconds (for jobs scheduled in the future/retries)",
|
||||
default: 2000,
|
||||
format: "positiveInt",
|
||||
env: "WORKER_POLL_INTERVAL_MS",
|
||||
env: "METAMIGO_WORKER_POLL_INTERVAL_MS",
|
||||
},
|
||||
},
|
||||
postgraphile: {
|
||||
|
|
@ -50,26 +50,26 @@ export const configSchema = {
|
|||
doc: "The postgres role that postgraphile logs in with",
|
||||
format: String,
|
||||
default: "metamigo_graphile_auth",
|
||||
env: "DATABASE_AUTHENTICATOR",
|
||||
env: "METAMIGO_DATABASE_AUTHENTICATOR",
|
||||
},
|
||||
appRootConnection: {
|
||||
doc: "The postgres root/superuser connection url for development mode so PG can watch the schema changes, this is strangely named in the postgraphile API 'ownerConnectionString'",
|
||||
format: String,
|
||||
default: "postgresql://postgres:metamigo@127.0.0.1:5435/metamigo_dev",
|
||||
env: "APP_ROOT_DATABASE_URL",
|
||||
env: "METAMIGO_APP_ROOT_DATABASE_URL",
|
||||
},
|
||||
authConnection: {
|
||||
doc: "The postgres connection URL for postgraphile, must not be superuser and must have limited privs.",
|
||||
format: String,
|
||||
default:
|
||||
"postgresql://metamigo_graphile_auth:metamigo@127.0.0.1:5435/metamigo_dev",
|
||||
env: "DATABASE_AUTH_URL",
|
||||
env: "METAMIGO_DATABASE_AUTH_URL",
|
||||
},
|
||||
visitor: {
|
||||
doc: "The postgres role that postgraphile switches to",
|
||||
format: String,
|
||||
default: "app_postgraphile",
|
||||
env: "DATABASE_VISITOR",
|
||||
env: "METAMIGO_DATABASE_VISITOR",
|
||||
},
|
||||
schema: {
|
||||
doc: "The schema postgraphile should expose with graphql",
|
||||
|
|
@ -80,7 +80,7 @@ export const configSchema = {
|
|||
doc: "Whether to enable the graphiql web interface or not",
|
||||
format: "Boolean",
|
||||
default: false,
|
||||
env: "ENABLE_GRAPHIQL",
|
||||
env: "METAMIGO_ENABLE_GRAPHIQL",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -89,14 +89,14 @@ export const configSchema = {
|
|||
doc: "The shadow databse connection url used by postgraphile-migrate. Not needed in production.",
|
||||
format: "uri",
|
||||
default: "postgresql://metamigo:metamigo@127.0.0.1:5435/metamigo_shadow",
|
||||
env: "SHADOW_DATABASE_URL",
|
||||
env: "METAMIGO_SHADOW_DATABASE_URL",
|
||||
sensitive: true,
|
||||
},
|
||||
rootConnection: {
|
||||
doc: "The postgres root/superuser connection url for testing only, database must NOT be the app database. Not needed in production.",
|
||||
format: "uri",
|
||||
default: "postgresql://postgres:metamigo@127.0.0.1:5435/template1",
|
||||
env: "ROOT_DATABASE_URL",
|
||||
env: "METAMIGO_ROOT_DATABASE_URL",
|
||||
sensitive: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -105,13 +105,13 @@ export const configSchema = {
|
|||
doc: "The url the frontend can be accessed at",
|
||||
format: "url",
|
||||
default: "http://localhost:3000",
|
||||
env: "FRONTEND_URL",
|
||||
env: "METAMIGO_FRONTEND_URL",
|
||||
},
|
||||
apiUrl: {
|
||||
doc: "The url the api backend can be accessed at from the frontend server",
|
||||
format: "url",
|
||||
default: "http://localhost:3001",
|
||||
env: "API_URL",
|
||||
env: "METAMIGO_API_URL",
|
||||
},
|
||||
},
|
||||
nextAuth: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue