oauth-entra-id
Version:
🛡️ A Secure, Performant, and Feature-Rich OAuth 2.0 Integration for Microsoft Entra ID — Fully Abstracted and Production-Ready.
151 lines (148 loc) • 7.19 kB
JavaScript
;
var chunk7MTPXR5V_cjs = require('./chunk-7MTPXR5V.cjs');
var modernCookies = require('modern-cookies');
async function $sharedHandleAuthentication(req, res) {
const body = req.body;
const params = body ? { loginPrompt: body.loginPrompt, email: body.email, frontendUrl: body.frontendUrl, azureId: body.azureId } : {};
const { authUrl } = await req.oauthProvider.getAuthUrl(params);
res.status(200).json({ url: authUrl });
}
async function $sharedHandleCallback(req, res) {
const body = req.body;
if (!body) throw new chunk7MTPXR5V_cjs.OAuthError({ msg: "Invalid params", desc: "Body must contain code and state" });
const { accessToken, refreshToken, frontendUrl } = await req.oauthProvider.getTokenByCode({
code: body.code,
state: body.state
});
modernCookies.setCookie(res, accessToken.name, accessToken.value, accessToken.options);
if (refreshToken) modernCookies.setCookie(res, refreshToken.name, refreshToken.value, refreshToken.options);
res.redirect(303, frontendUrl);
}
async function $sharedHandleLogout(req, res) {
const body = req.body;
const params = body ? { frontendUrl: body.frontendUrl, azureId: body.azureId } : {};
const { logoutUrl, deleteAccessToken, deleteRefreshToken } = await req.oauthProvider.getLogoutUrl(params);
modernCookies.deleteCookie(res, deleteRefreshToken.name, deleteRefreshToken.options);
modernCookies.deleteCookie(res, deleteAccessToken.name, deleteAccessToken.options);
res.status(200).json({ url: logoutUrl });
}
async function $sharedHandleOnBehalfOf(req, res) {
const body = req.body;
if (!body) throw new chunk7MTPXR5V_cjs.OAuthError({ msg: "Invalid params", desc: "Body must contain serviceNames" });
if (!req.accessTokenInfo?.jwt) {
throw new chunk7MTPXR5V_cjs.OAuthError({
msg: "Unauthorized",
desc: "Access token is required for on-behalf-of requests",
status: 401
});
}
if (req.userInfo?.isApp === true) {
throw new chunk7MTPXR5V_cjs.OAuthError({
msg: "Forbidden",
desc: "On-behalf-of requests are not allowed for app users",
status: 403
});
}
const { results } = await req.oauthProvider.getTokenOnBehalfOf({
services: body.services,
accessToken: req.accessTokenInfo.jwt,
azureId: body.azureId
});
for (const { accessToken } of results) {
modernCookies.setCookie(res, accessToken.name, accessToken.value, accessToken.options);
}
res.status(200).json({ tokensSet: results.length });
}
async function $sharedMiddleware(req, res) {
const b2b = await $checkB2BToken(req);
if (b2b.error) throw new chunk7MTPXR5V_cjs.OAuthError(b2b.error);
if (b2b.userInfo) {
return {
userInfo: b2b.userInfo,
tryInjectData: (_data) => Promise.resolve(
chunk7MTPXR5V_cjs.$err({ msg: "Invalid user type", desc: "Injecting data is only supported for user-based sessions" })
)
};
}
const injectFunc = $createInjectFunc(req, res);
const { cookies } = req.oauthProvider.settings;
let firstError = null;
for (const { accessTokenName, refreshTokenName } of cookies.cookieNames) {
const cookie = await $checkCookieTokens(req, res, injectFunc, accessTokenName, refreshTokenName);
if (cookie.error) {
if (!firstError) firstError = cookie.error;
continue;
}
for (const { azureId, accessTokenName: accessTokenName2, refreshTokenName: refreshTokenName2 } of cookies.cookieNames) {
if (azureId === cookie.userInfo.azureId) continue;
modernCookies.deleteCookie(res, accessTokenName2, cookies.deleteOptions);
modernCookies.deleteCookie(res, refreshTokenName2, cookies.deleteOptions);
}
return { userInfo: cookie.userInfo, tryInjectData: cookie.tryInjectData };
}
throw new chunk7MTPXR5V_cjs.OAuthError(firstError ?? chunk7MTPXR5V_cjs.$err({ msg: "Unauthorized", desc: "Tokens are invalid or missing", status: 401 }));
}
function $createInjectFunc(req, res) {
return async (accessToken, data) => {
const inj = await req.oauthProvider.tryInjectData({ accessToken, data });
if (inj.error) return chunk7MTPXR5V_cjs.$err(inj.error);
if (req.userInfo?.isApp !== false) {
return chunk7MTPXR5V_cjs.$err({ msg: "Invalid user type", desc: "Injecting data is only supported for user-based sessions" });
}
modernCookies.setCookie(res, inj.newAccessToken.name, inj.newAccessToken.value, inj.newAccessToken.options);
req.userInfo = { ...req.userInfo, injectedData: data };
return chunk7MTPXR5V_cjs.$ok({ injectedData: data });
};
}
async function $checkB2BToken(req) {
const authHeader = req.headers.authorization;
if (!req.oauthProvider.settings.acceptB2BRequests || !authHeader) return chunk7MTPXR5V_cjs.$ok({ userInfo: void 0 });
const token = authHeader.startsWith("Bearer ") ? authHeader.split(" ")[1] : void 0;
const bearer = await req.oauthProvider.verifyAccessToken(token);
if (bearer.error) return chunk7MTPXR5V_cjs.$err(bearer.error);
return chunk7MTPXR5V_cjs.$ok({ userInfo: $userInfo(req, bearer.meta, bearer.rawJwt, bearer.payload) });
}
async function $checkCookieTokens(req, res, injectFunc, accessTokenName, refreshTokenName) {
const accessToken = modernCookies.getCookie(req, accessTokenName);
const refreshToken = modernCookies.getCookie(req, refreshTokenName);
if (!accessToken && !refreshToken) {
return chunk7MTPXR5V_cjs.$err({ msg: "Unauthorized", desc: "Access token and refresh token are both missing", status: 401 });
}
const at = await req.oauthProvider.verifyAccessToken(accessToken);
if (at.error && !refreshToken) return chunk7MTPXR5V_cjs.$err(at.error);
if (at.success) {
return chunk7MTPXR5V_cjs.$ok({
azureId: at.meta.azureId,
userInfo: $userInfo(req, at.meta, at.rawJwt, at.payload, at.injectedData),
tryInjectData: (data) => injectFunc(at.rawJwt, data)
});
}
const rt = await req.oauthProvider.tryRefreshTokens(refreshToken);
if (rt.error) return chunk7MTPXR5V_cjs.$err(rt.error);
modernCookies.setCookie(res, rt.newAccessToken.name, rt.newAccessToken.value, rt.newAccessToken.options);
if (rt.newRefreshToken) modernCookies.setCookie(res, rt.newRefreshToken.name, rt.newRefreshToken.value, rt.newRefreshToken.options);
return chunk7MTPXR5V_cjs.$ok({
azureId: rt.meta.azureId,
userInfo: $userInfo(req, rt.meta, rt.rawJwt, rt.payload),
tryInjectData: (data) => injectFunc(rt.rawJwt, data)
});
}
function $userInfo(req, meta, rawJwt, payload, injectedData) {
const userInfo = {
azureId: meta.azureId,
tenantId: meta.tenantId,
uniqueId: meta.uniqueId,
roles: meta.roles,
...meta.isApp ? { isApp: true, appId: meta.appId } : { isApp: false, name: meta.name, email: meta.email, injectedData }
};
req.userInfo = userInfo;
req.accessTokenInfo = { jwt: rawJwt, payload, meta };
return userInfo;
}
exports.$sharedHandleAuthentication = $sharedHandleAuthentication;
exports.$sharedHandleCallback = $sharedHandleCallback;
exports.$sharedHandleLogout = $sharedHandleLogout;
exports.$sharedHandleOnBehalfOf = $sharedHandleOnBehalfOf;
exports.$sharedMiddleware = $sharedMiddleware;
//# sourceMappingURL=chunk-F55YGO6G.cjs.map
//# sourceMappingURL=chunk-F55YGO6G.cjs.map