Skip to content

Commit 8478e97

Browse files
committed
fix(Auth): improve redirect logic
1 parent 1fc490e commit 8478e97

File tree

1 file changed

+35
-22
lines changed

1 file changed

+35
-22
lines changed

server/api/auth.ts

+35-22
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const SALT_ROUNDS = 10;
2020
const JWT_SECRET = new TextEncoder().encode(process.env.SECRETKEY);
2121
const JWT_COOKIE_DOMAIN = (process.env.PRODUCTIONURLS || "").split(",")[0];
2222
const SESSION_DEFAULT_EXPIRY_MINUTES = 60 * 24 * 7; // 7 days
23-
const SESSION_DEFAULT_EXPIRY_MILLISECONDS = SESSION_DEFAULT_EXPIRY_MINUTES * 60 * 1000;
23+
const SESSION_DEFAULT_EXPIRY_MILLISECONDS =
24+
SESSION_DEFAULT_EXPIRY_MINUTES * 60 * 1000;
2425

2526
const oidcBase = `https://${process.env.OIDC_HOST}`;
2627
const oidcAuth = `${oidcBase}/cas/oidc/authorize`;
@@ -75,17 +76,23 @@ function splitSessionJWT(sessionJWT: string) {
7576
* @param {string} uuid - The User UUID to initialize the session for.
7677
* @param {string} ticket - A CAS ticket ID to use for the session.
7778
*/
78-
async function createAndAttachLocalSession(res: Response, uuid: string, ticket?: string) {
79+
async function createAndAttachLocalSession(
80+
res: Response,
81+
uuid: string,
82+
ticket?: string
83+
) {
7984
const sessionId = uuidv4();
8085
const sessionCreated = new Date();
81-
const sessionExpiry = new Date(sessionCreated.getTime() + SESSION_DEFAULT_EXPIRY_MILLISECONDS);
86+
const sessionExpiry = new Date(
87+
sessionCreated.getTime() + SESSION_DEFAULT_EXPIRY_MILLISECONDS
88+
);
8289
const session = new Session({
8390
sessionId,
8491
userId: uuid,
8592
valid: true,
8693
createdAt: sessionCreated,
8794
expiresAt: sessionExpiry,
88-
...ticket && { sessionTicket: ticket },
95+
...(ticket && { sessionTicket: ticket }),
8996
});
9097

9198
await session.save();
@@ -153,6 +160,7 @@ async function initLogin(req: Request, res: Response) {
153160
secure: true,
154161
};
155162

163+
// Handle redirection for org Commons (non-LibreTexts instances)
156164
if (
157165
process.env.CONDUCTOR_DOMAIN &&
158166
process.env.CONDUCTOR_DOMAIN !== "commons.libretexts.org"
@@ -164,6 +172,7 @@ async function initLogin(req: Request, res: Response) {
164172
...(process.env.NODE_ENV === "production" && prodCookieConfig),
165173
});
166174
}
175+
167176
res.cookie("oidc_state", base64State, {
168177
encode: String,
169178
httpOnly: true,
@@ -235,7 +244,7 @@ async function completeLogin(req: Request, res: Response) {
235244
}
236245

237246
// Session ID (sid) from id_token will be used as the ticket identifier
238-
const idDecoded = decodeJwt(id_token)
247+
const idDecoded = decodeJwt(id_token);
239248
const ticketID = idDecoded?.sid?.toString();
240249

241250
// Verify ID token with CAS public key set
@@ -348,29 +357,33 @@ async function completeLogin(req: Request, res: Response) {
348357
// Create local session
349358
await createAndAttachLocalSession(res, authUser.uuid, ticketID);
350359

351-
// Redirect user
352-
let redirectURL = req.hostname;
353-
if (req.cookies.conductor_auth_redirect) {
354-
redirectURL = req.cookies.conductor_auth_redirect;
355-
} else {
356-
const domain =
357-
process.env.NODE_ENV === "production"
358-
? process.env.CONDUCTOR_DOMAIN
359-
: `localhost:${process.env.CLIENT_PORT || 3000}`;
360-
redirectURL = `${oidcCallbackProto}://${domain}`;
360+
// Determine base of redirect URL
361+
let finalRedirectURL = `${req.protocol}://${req.get("host")}`; // Default to current host
362+
if(req.cookies.conductor_auth_redirect){
363+
finalRedirectURL = req.cookies.conductor_auth_redirect; // Use auth redirect cookie if available
361364
}
365+
366+
// Check if redirectURI is a full URL
362367
if (state.redirectURI && isFullURL(state.redirectURI)) {
363-
redirectURL = state.redirectURI;
364-
} else {
368+
finalRedirectURL = state.redirectURI;
369+
} else if (state.redirectURI && !isFullURL(state.redirectURI)) {
365370
// redirectURI is only a path or not provided
366-
redirectURL = assembleUrl([redirectURL, state.redirectURI ?? "home"]);
371+
finalRedirectURL = assembleUrl([finalRedirectURL, state.redirectURI]);
372+
} else {
373+
// Default to home if no redirectURI is provided
374+
finalRedirectURL = assembleUrl([finalRedirectURL, "home"]);
367375
}
368376

369377
if (!state.redirectURI && isNewMember) {
370-
redirectURL = `${redirectURL}?newmember=true`;
378+
const _final = new URL(finalRedirectURL);
379+
const _params = new URLSearchParams(_final.search);
380+
_params.set("newmember", "true");
381+
finalRedirectURL = `${_final.origin}${
382+
_final.pathname
383+
}?${_params.toString()}`;
371384
}
372385

373-
return res.redirect(redirectURL);
386+
return res.redirect(finalRedirectURL);
374387
} catch (e) {
375388
debugError(e);
376389
return res.status(500).send({
@@ -727,8 +740,8 @@ async function verifyRequest(req: Request, res: Response, next: NextFunction) {
727740
} catch (e: any) {
728741
let tokenExpired = false;
729742
let sessionInvalid = false;
730-
console.log('VERIFY REQUEST ERROR')
731-
console.log(e)
743+
console.log("VERIFY REQUEST ERROR");
744+
console.log(e);
732745
if (e.code === "ERR_JWT_EXPIRED") {
733746
tokenExpired = true;
734747
} else if (e.message === "ERR_BAD_SESSION") {

0 commit comments

Comments
 (0)