link-stack/apps/link/app/_lib/authentication.ts

168 lines
4.4 KiB
TypeScript
Raw Normal View History

2024-03-20 17:51:21 +01:00
import type {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import {
NextAuthOptions,
getServerSession as internalGetServerSession,
} from "next-auth";
import Google from "next-auth/providers/google";
import Credentials from "next-auth/providers/credentials";
import Apple from "next-auth/providers/apple";
2025-01-20 11:25:13 +01:00
import AzureADProvider from "next-auth/providers/azure-ad";
import { createLogger } from "@link-stack/logger";
const logger = createLogger('link-authentication');
2024-03-20 17:51:21 +01:00
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
const fetchRoles = async () => {
const url = `${process.env.ZAMMAD_URL}/api/v1/roles`;
const res = await fetch(url, { headers });
const roles = await res.json();
const formattedRoles = roles.reduce((acc: any, role: any) => {
acc[role.id] = role.name;
return acc;
}, {});
return formattedRoles;
};
const fetchUser = async (email: string) => {
2025-11-10 14:55:22 +01:00
const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=${encodeURIComponent(`login:${email}`)}&limit=1`;
2024-03-20 17:51:21 +01:00
const res = await fetch(url, { headers });
const users = await res.json();
const user = users?.[0];
return user;
};
const getUserRoles = async (email: string) => {
try {
const user = await fetchUser(email);
2025-01-15 14:15:02 +01:00
if (!user) {
return [];
}
2024-03-20 17:51:21 +01:00
const allRoles = await fetchRoles();
const roles = user.role_ids.map((roleID: number) => {
const role = allRoles[roleID];
return role ? role.toLowerCase().replace(" ", "_") : null;
});
return roles.filter((role: string) => role !== null);
} catch (e) {
logger.error({ error: e, email }, 'Failed to get user roles');
2024-03-20 17:51:21 +01:00
return [];
}
};
const login = async (email: string, password: string) => {
const url = `${process.env.ZAMMAD_URL}/api/v1/users/me`;
const authorization =
"Basic " + Buffer.from(email + ":" + password).toString("base64");
const res = await fetch(url, {
headers: {
authorization,
},
});
const user = await res.json();
if (user && !user.error && user.id) {
return user;
} else {
return null;
}
};
const providers = [];
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
providers.push(
2024-03-20 17:51:21 +01:00
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
);
} else if (process.env.APPLE_CLIENT_ID && process.env.APPLE_CLIENT_SECRET) {
providers.push(
2024-03-20 17:51:21 +01:00
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET,
}),
);
2025-01-20 11:25:13 +01:00
} else if (
process.env.AZURE_AD_CLIENT_ID &&
process.env.AZURE_AD_CLIENT_SECRET &&
process.env.AZURE_AD_TENANT_ID
) {
providers.push(
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
}),
);
} else {
providers.push(
2024-03-20 17:51:21 +01:00
Credentials({
name: "Zammad",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
2024-08-07 12:02:33 +02:00
async authorize(credentials) {
2024-03-20 17:51:21 +01:00
const user = await login(credentials.email, credentials.password);
if (user) {
return user;
} else {
return null;
}
},
}),
);
}
export const authOptions: NextAuthOptions = {
pages: {
2025-11-10 14:55:22 +01:00
signIn: "/login",
error: "/login",
signOut: "/logout",
},
providers,
session: {
2024-12-13 16:37:20 +01:00
maxAge: 3 * 24 * 60 * 60,
},
2024-03-20 17:51:21 +01:00
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
2024-08-07 12:02:33 +02:00
signIn: async ({ user }) => {
2024-03-20 17:51:21 +01:00
const roles = (await getUserRoles(user.email)) ?? [];
return roles.includes("admin") || roles.includes("agent");
2024-03-20 17:51:21 +01:00
},
2024-08-07 12:02:33 +02:00
session: async ({ session, token }) => {
2024-03-20 17:51:21 +01:00
// @ts-ignore
session.user.roles = token.roles ?? [];
2024-10-09 12:21:06 +02:00
// @ts-ignore
session.user.zammadCsrfToken = token.zammadCsrfToken;
2024-03-20 17:51:21 +01:00
return session;
},
2024-10-09 12:21:06 +02:00
jwt: async ({ token, user, trigger, session }) => {
2024-03-20 17:51:21 +01:00
if (user) {
token.roles = (await getUserRoles(user.email)) ?? [];
}
2024-10-09 12:21:06 +02:00
if (session && trigger === "update") {
token.zammadCsrfToken = session.zammadCsrfToken;
}
2024-03-20 17:51:21 +01:00
return token;
},
},
};
2024-03-20 17:51:21 +01:00
export const getServerSession = (
...args:
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
| [NextApiRequest, NextApiResponse]
| []
) => internalGetServerSession(...args, authOptions);