link-stack/apps/metamigo-frontend/lib/nextauth-adapter.ts

231 lines
5.6 KiB
TypeScript
Raw Normal View History

2023-02-13 12:41:30 +00:00
/* eslint-disable unicorn/no-null */
2023-06-07 11:18:58 +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";
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-06-07 11:18:58 +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",
});
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-07 11:18:58 +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(
`getUserByAccount/${provider}/${providerAccountId}`
2023-02-13 12:41:30 +00:00
);
return payload;
} catch (error) {
if (Boom.isBoom(error, 404)) return null;
2023-06-07 11:18:58 +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");
}
}
2023-06-07 11:18:58 +00:00
async function linkAccount(account: AdapterAccount) {
2023-02-13 12:41:30 +00:00
try {
2023-06-07 11:18:58 +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-07 11:18:58 +00:00
const { payload }: { payload: AdapterSession } = await wreck.post(
"createSession",
{
payload: user,
}
);
payload.expires = new Date(payload.expires);
2023-02-13 12:41:30 +00:00
return payload;
2023-06-07 11:18:58 +00:00
} catch (error) {
console.log(error);
2023-02-13 12:41:30 +00:00
throw new Error("CREATE_SESSION_ERROR");
}
}
async function getSessionAndUser(sessionToken: string) {
2023-02-13 12:41:30 +00:00
try {
2023-06-07 11:18:58 +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-07 11:18:58 +00:00
console.log(error);
2023-02-13 12:41:30 +00:00
if (Boom.isBoom(error, 404)) return null;
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,
getUserByAccount,
2023-02-13 12:41:30 +00:00
updateUser,
// deleteUser,
linkAccount,
// unlinkAccount,
createSession,
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
}
return getAdapter();
2023-02-13 12:41:30 +00:00
};