2023-02-13 12:41:30 +00:00
|
|
|
/* eslint-disable unicorn/no-null */
|
2023-06-06 10:28:29 +00:00
|
|
|
import type { Adapter, AdapterAccount, AdapterSession, AdapterUser } from "next-auth/adapters";
|
2023-02-13 12:41:30 +00:00
|
|
|
import * as Wreck from "@hapi/wreck";
|
|
|
|
|
import * as Boom from "@hapi/boom";
|
|
|
|
|
|
2023-03-13 16:46:59 +00:00
|
|
|
import type { IAppConfig } from "@digiresilience/metamigo-config";
|
2023-02-13 12:41:30 +00:00
|
|
|
|
|
|
|
|
export interface Profile {
|
|
|
|
|
name: string;
|
|
|
|
|
email: string;
|
|
|
|
|
emailVerified: string;
|
|
|
|
|
userRole: string;
|
|
|
|
|
avatar?: string;
|
|
|
|
|
image?: string;
|
|
|
|
|
createdBy: string;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-25 08:57:24 +00:00
|
|
|
export type User = Profile & { id: string; createdAt: Date; updatedAt: Date; };
|
2023-02-13 12:41:30 +00:00
|
|
|
|
|
|
|
|
export interface Session {
|
|
|
|
|
userId: string;
|
|
|
|
|
expires: Date;
|
|
|
|
|
sessionToken: string;
|
|
|
|
|
accessToken: string;
|
|
|
|
|
createdAt: Date;
|
|
|
|
|
updatedAt: Date;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// from https://github.com/nextauthjs/next-auth/blob/main/src/lib/errors.js
|
|
|
|
|
class UnknownError extends Error {
|
2023-03-15 12:17:43 +00:00
|
|
|
constructor(message: any) {
|
2023-02-13 12:41:30 +00:00
|
|
|
super(message);
|
|
|
|
|
this.name = "UnknownError";
|
|
|
|
|
this.message = message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
|
return {
|
|
|
|
|
error: {
|
|
|
|
|
name: this.name,
|
|
|
|
|
message: this.message,
|
|
|
|
|
// stack: this.stack
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CreateUserError extends UnknownError {
|
2023-03-15 12:17:43 +00:00
|
|
|
constructor(message: any) {
|
2023-02-13 12:41:30 +00:00
|
|
|
super(message);
|
|
|
|
|
this.name = "CreateUserError";
|
|
|
|
|
this.message = message;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-15 12:17:43 +00:00
|
|
|
const basicHeader = (secret: any) =>
|
2023-02-13 12:41:30 +00:00
|
|
|
"Basic " + Buffer.from(secret + ":", "utf8").toString("base64");
|
|
|
|
|
|
|
|
|
|
export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|
|
|
|
if (!config) throw new Error("MetamigoAdapter: config is not defined.");
|
|
|
|
|
const wreck = Wreck.defaults({
|
|
|
|
|
headers: {
|
|
|
|
|
authorization: basicHeader(config.nextAuth.secret),
|
|
|
|
|
},
|
|
|
|
|
baseUrl: `${config.frontend.apiUrl}/api/nextauth/`,
|
|
|
|
|
maxBytes: 1024 * 1024,
|
|
|
|
|
json: "force",
|
|
|
|
|
});
|
|
|
|
|
|
2023-06-06 10:28:29 +00:00
|
|
|
function getAdapter(): Adapter {
|
2023-02-13 12:41:30 +00:00
|
|
|
async function createUser(profile: Profile) {
|
|
|
|
|
try {
|
|
|
|
|
if (!profile.createdBy) profile = { ...profile, createdBy: "nextauth" };
|
|
|
|
|
profile.avatar = profile.image;
|
|
|
|
|
delete profile.image;
|
|
|
|
|
const { payload } = await wreck.post("createUser", {
|
|
|
|
|
payload: profile,
|
|
|
|
|
});
|
|
|
|
|
return payload;
|
|
|
|
|
} catch {
|
|
|
|
|
throw new CreateUserError("CREATE_USER_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getUser(id: string) {
|
|
|
|
|
try {
|
|
|
|
|
const { payload } = await wreck.get(`getUser/${id}`);
|
|
|
|
|
|
|
|
|
|
return payload;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (Boom.isBoom(error, 404)) return null;
|
|
|
|
|
throw new Error("GET_USER_BY_ID_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getUserByEmail(email: string) {
|
|
|
|
|
try {
|
|
|
|
|
const { payload } = await wreck.get(`getUserByEmail/${email}`);
|
|
|
|
|
return payload;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (Boom.isBoom(error, 404)) return null;
|
|
|
|
|
throw new Error("GET_USER_BY_EMAIL_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-06 10:28:29 +00:00
|
|
|
async function getUserByAccount({ providerAccountId, provider }: { providerAccountId: string, provider: string }) {
|
2023-02-13 12:41:30 +00:00
|
|
|
try {
|
|
|
|
|
const { payload } = await wreck.get(
|
2023-06-06 10:28:29 +00:00
|
|
|
`getUserByAccount/${provider}/${providerAccountId}`
|
2023-02-13 12:41:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return payload;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (Boom.isBoom(error, 404)) return null;
|
2023-06-06 10:28:29 +00:00
|
|
|
console.log(error)
|
|
|
|
|
throw new Error("GET_USER_BY_ACCOUNT");
|
2023-02-13 12:41:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function updateUser(user: User) {
|
|
|
|
|
try {
|
|
|
|
|
const { payload } = await wreck.put("updateUser", {
|
|
|
|
|
payload: user,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return payload;
|
|
|
|
|
} catch {
|
|
|
|
|
throw new Error("UPDATE_USER");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function linkAccount(
|
2023-06-06 10:28:29 +00:00
|
|
|
account: AdapterAccount
|
2023-02-13 12:41:30 +00:00
|
|
|
) {
|
|
|
|
|
try {
|
2023-06-06 10:28:29 +00:00
|
|
|
await wreck.put("linkAccount", {payload: account} as any );
|
|
|
|
|
} catch(error) {
|
|
|
|
|
console.log(error);
|
2023-02-13 12:41:30 +00:00
|
|
|
throw new Error("LINK_ACCOUNT_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createSession(user: User) {
|
|
|
|
|
try {
|
2023-06-06 10:28:29 +00:00
|
|
|
const { payload }: {payload: AdapterSession} = await wreck.post("createSession", {
|
2023-02-13 12:41:30 +00:00
|
|
|
payload: user,
|
|
|
|
|
});
|
2023-06-06 10:28:29 +00:00
|
|
|
payload.expires = new Date(payload.expires)
|
2023-02-13 12:41:30 +00:00
|
|
|
return payload;
|
2023-06-06 10:28:29 +00:00
|
|
|
} catch(error) {
|
|
|
|
|
console.log(error)
|
2023-02-13 12:41:30 +00:00
|
|
|
throw new Error("CREATE_SESSION_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-06 10:28:29 +00:00
|
|
|
async function getSessionAndUser(sessionToken: string) {
|
2023-02-13 12:41:30 +00:00
|
|
|
try {
|
2023-06-06 10:28:29 +00:00
|
|
|
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}
|
2023-02-13 12:41:30 +00:00
|
|
|
} catch (error) {
|
2023-06-06 10:28:29 +00:00
|
|
|
console.log(error)
|
2023-02-13 12:41:30 +00:00
|
|
|
if (Boom.isBoom(error, 404)) return null;
|
2023-06-06 10:28:29 +00:00
|
|
|
throw new Error("GET_SESSION_AND_USER_ERROR");
|
2023-02-13 12:41:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function updateSession(session: Session, force: boolean) {
|
|
|
|
|
try {
|
|
|
|
|
const payload = {
|
|
|
|
|
...session,
|
|
|
|
|
expires: new Date(session.expires).getTime(),
|
|
|
|
|
};
|
|
|
|
|
const { payload: result } = await wreck.put(
|
|
|
|
|
`updateSession?force=${Boolean(force)}`,
|
|
|
|
|
{
|
|
|
|
|
payload,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return result;
|
|
|
|
|
} catch {
|
|
|
|
|
throw new Error("UPDATE_SESSION_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function deleteSession(sessionToken: string) {
|
|
|
|
|
try {
|
|
|
|
|
await wreck.delete(`deleteSession/${sessionToken}`);
|
|
|
|
|
} catch {
|
|
|
|
|
throw new Error("DELETE_SESSION_ERROR");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-15 12:17:43 +00:00
|
|
|
return {
|
2023-02-13 12:41:30 +00:00
|
|
|
createUser,
|
|
|
|
|
getUser,
|
|
|
|
|
getUserByEmail,
|
2023-06-06 10:28:29 +00:00
|
|
|
getUserByAccount,
|
2023-02-13 12:41:30 +00:00
|
|
|
updateUser,
|
|
|
|
|
// deleteUser,
|
|
|
|
|
linkAccount,
|
|
|
|
|
// unlinkAccount,
|
|
|
|
|
createSession,
|
2023-06-06 10:28:29 +00:00
|
|
|
getSessionAndUser,
|
2023-02-13 12:41:30 +00:00
|
|
|
updateSession,
|
|
|
|
|
deleteSession,
|
|
|
|
|
// @ts-expect-error: Type error
|
2023-03-15 12:17:43 +00:00
|
|
|
} as AdapterInstance<Profile, User, Session, unknown>;
|
2023-02-13 12:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
2023-06-06 10:28:29 +00:00
|
|
|
return getAdapter();
|
2023-02-13 12:41:30 +00:00
|
|
|
};
|