Fix OAuth infinite-redirect loop caused by httpOnly session cookies
The bug: SESSION_COOKIE_OPTIONS had httpOnly: true, so js/app.js could not see the `id` cookie set by /oauth. Every page load thought the user was signed out, redirected to Webex, minted a fresh token, set another invisible cookie, redirected back, and looped -- until Webex's Common Token Store hit its per-user limit and returned error=tokenlimit_reached on the next callback. Fix: - httpOnly is now explicitly false with a comment explaining why: the app's design has always relied on the client reading the id and displayName cookies via document.cookie. - sameSite tightened change from 'strict' to 'lax' so the cookie reliably survives the webex.com -> /oauth -> /sendMessage.html redirect chain across all browsers (some treat continuations of a cross-site navigation as cross-site for strict cookies). - Stop setting access_token, refresh_token, avatar, email, orgId on the response. `req.cookies.*` grep confirms the server never reads any of them, and the client uses id/displayName only. Removing the token cookies also eliminates a would-be XSS foothold. Existing broken sessions: setting a new cookie with the same name and path replaces the old one regardless of httpOnly flag, so a single completed OAuth after this deploy repairs the browser state. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2984e8e851
commit
04fb4f6442
1 changed files with 19 additions and 9 deletions
28
index.js
28
index.js
|
|
@ -415,11 +415,21 @@ app.get('/CollabCentral/:app/user/:scope/:action', (req, res) => {
|
||||||
} else { res.status(401) }
|
} else { res.status(401) }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Standard options for the session cookies set after a successful OAuth round-trip.
|
// Options for the session cookies set after a successful OAuth round-trip.
|
||||||
|
//
|
||||||
|
// httpOnly is deliberately false: js/app.js reads `id` (to decide whether to
|
||||||
|
// redirect to OAuth) and `displayName` (to render the user chip in the header)
|
||||||
|
// via document.cookie. Enabling httpOnly here hides the cookies from JS, which
|
||||||
|
// causes an infinite auth loop where every page load thinks the user is not
|
||||||
|
// signed in and kicks off a fresh OAuth exchange — burning Webex tokens until
|
||||||
|
// the CTS token limit is reached.
|
||||||
|
//
|
||||||
|
// sameSite is 'lax' rather than 'strict' so the cookie survives the OAuth
|
||||||
|
// redirect chain webexapis.com -> /oauth -> /sendMessage.html on all browsers.
|
||||||
const SESSION_COOKIE_OPTIONS = {
|
const SESSION_COOKIE_OPTIONS = {
|
||||||
httpOnly: true,
|
httpOnly: false,
|
||||||
secure: true,
|
secure: true,
|
||||||
sameSite: 'strict',
|
sameSite: 'lax',
|
||||||
maxAge: 24 * 60 * 60 * 1000
|
maxAge: 24 * 60 * 60 * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -467,14 +477,14 @@ app.get(`/CollabCentral/:app/oauth`, async function (req, res) {
|
||||||
}
|
}
|
||||||
logger("oauth", whoami.displayName + " successfully authed for " + appName + ".");
|
logger("oauth", whoami.displayName + " successfully authed for " + appName + ".");
|
||||||
|
|
||||||
|
// Only the two cookies that the server (req.cookies.id, .displayName) and
|
||||||
|
// the client (getCookie('id'), getCookie('displayName')) actually read.
|
||||||
|
// Access/refresh tokens deliberately stay out of the browser: they never
|
||||||
|
// need to leave the server, and putting them in cookies would expose them
|
||||||
|
// to any XSS that might slip in later.
|
||||||
res
|
res
|
||||||
.cookie('displayName', whoami.displayName, SESSION_COOKIE_OPTIONS)
|
|
||||||
.cookie('id', whoami.id, SESSION_COOKIE_OPTIONS)
|
.cookie('id', whoami.id, SESSION_COOKIE_OPTIONS)
|
||||||
.cookie('avatar', whoami.avatar, SESSION_COOKIE_OPTIONS)
|
.cookie('displayName', whoami.displayName, SESSION_COOKIE_OPTIONS)
|
||||||
.cookie('email', whoami.userName, SESSION_COOKIE_OPTIONS)
|
|
||||||
.cookie('orgId', whoami.orgId, SESSION_COOKIE_OPTIONS)
|
|
||||||
.cookie('access_token', jsonData.access_token, SESSION_COOKIE_OPTIONS)
|
|
||||||
.cookie('refresh_token', jsonData.refresh_token, SESSION_COOKIE_OPTIONS)
|
|
||||||
.redirect(301, '/CollabCentral/' + appName + '/sendMessage.html');
|
.redirect(301, '/CollabCentral/' + appName + '/sendMessage.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue