Skip to content

Commit

Permalink
STCOR-776 RTR adjustments for keycloak (#1490)
Browse files Browse the repository at this point in the history
There are many small differences in how keycloak and okapi respond to
authentication related requests.
* permissions are structured differently in Okapi between `login` and
  `_self` requests and depending on whether `expandPermissions=true` is
  present on the request; keycloak always responds with a flattened
  list.
* token expiration data is nested in the login-response in Okapi but is
  a root-level element in the `/authn/token` response from keycloak.

STCOR-776, STCOR-846

(cherry picked from commit 2e162f6)
  • Loading branch information
zburke committed Jul 25, 2024
1 parent 17d3f7f commit b17e9ad
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 9 deletions.
13 changes: 9 additions & 4 deletions src/components/Root/FFetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,20 @@ export class FFetch {

// When starting a new session, the response from /bl-users/login-with-expiry
// will contain AT expiration info, but when restarting an existing session,
// the response from /bl-users/_self will NOT, although that information will
// the response from /bl-users/_self will NOT, although that information should
// have been cached in local-storage.
//
// This means there are many places we have to check to figure out when the
// AT is likely to expire, and thus when we want to rotate. First inspect
// the response, otherwise the session. Default to 10 seconds.
// the response, then the session, then default to 10 seconds.
if (res?.accessTokenExpiration) {
this.logger.log('rtr', 'rotation scheduled with login response data');
const rotationPromise = Promise.resolve((new Date(res.accessTokenExpiration).getTime() - Date.now()) * RTR_AT_TTL_FRACTION);

scheduleRotation(rotationPromise);
} else {
const rotationPromise = getTokenExpiry().then((expiry) => {
if (expiry.atExpires) {
if (expiry?.atExpires) {
this.logger.log('rtr', 'rotation scheduled with cached session data');
return (new Date(expiry.atExpires).getTime() - Date.now()) * RTR_AT_TTL_FRACTION;
}
Expand Down Expand Up @@ -189,7 +189,12 @@ export class FFetch {
if (clone.ok) {
this.logger.log('rtr', 'authn success!');
clone.json().then(json => {
this.rotateCallback(json.tokenExpiration);
// we want accessTokenExpiration. do we need to destructure?
// in community-folio, a /login-with-expiry response is shaped like
// { ..., tokenExpiration: { accessTokenExpiration, refreshTokenExpiration } }
// in eureka-folio, a /authn/token response is shaped like
// { accessTokenExpiration, refreshTokenExpiration }
this.rotateCallback(json.tokenExpiration ?? json);
});
}

Expand Down
18 changes: 13 additions & 5 deletions src/loginServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,15 +413,23 @@ export function spreadUserWithPerms(userWithPerms) {
// remap data's array of permission-names to set with
// permission-names for keys and `true` for values.
//
// userWithPerms is shaped differently depending on whether
// it comes from a login call or a `.../_self` call, which
// is just totally totally awesome. :|
// userWithPerms is shaped differently depending on the API call
// that generated it.
// in community-folio, /login sends data like [{ "permissionName": "foo" }]
// and includes both directly and indirectly assigned permissions
// in community-folio, /_self sends data like ["foo", "bar", "bat"]
// but only includes directly assigned permissions
// in community-folio, /_self?expandPermissions=true sends data like [{ "permissionName": "foo" }]
// and includes both directly and indirectly assigned permissions
// in eureka-folio, /_self sends data like ["foo", "bar", "bat"]
// and includes both directly and indirectly assigned permissions
//
// we'll parse it differently depending on what it looks like.
let perms = {};
const list = userWithPerms?.permissions?.permissions;
if (list && Array.isArray(list) && list.length > 0) {
// _self sends data like ["foo", "bar", "bat"]
// login sends data like [{ "permissionName": "foo" }]
// shaped like this ["foo", "bar", "bat"] or
// shaped like that [{ "permissionName": "foo" }]?
if (typeof list[0] === 'string') {
perms = Object.assign({}, ...list.map(p => ({ [p]: true })));
} else {
Expand Down

0 comments on commit b17e9ad

Please sign in to comment.