Skip to content

Log in to FOLIO with expiring tokens and support the SCELC member tenant - #10

Merged
t4k merged 2 commits into
mainfrom
worktree-folio-token-refresh
Nov 4, 2026
Merged

t4k merged 2 commits into
mainfrom
worktree-folio-token-refresh

Conversation

@t4k

@t4k t4k commented Nov 4, 2026

Copy link
Copy Markdown
Member

Summary

Caltech’s FOLIO no longer accepts long-lived API tokens, so Boffo’s lookups fail today, and Caltech Library’s FOLIO moves to the SCELC consortial member tenant cs00000003m013 (the API URLs don’t change). This pull request updates Boffo for both.

Login and token renewal (Code.js)

  • saveFolioInfo() logs in with POST /authn/login-with-expiry. It takes the access token from an x-okapi-token header or the folioAccessToken cookie, and the refresh token from the folioRefreshToken cookie. Header names are matched case-insensitively, and Set-Cookie may be a string or an array.
  • Boffo stores the access token, refresh token, and both expiration times in user properties (boffo_folio_api_token, boffo_folio_api_token_expires, boffo_folio_refresh_token, and boffo_folio_refresh_token_expires). It never stores the password.
  • Every FOLIO GET goes through a new folioFetch(). It renews the access token with POST /authn/refresh when less than 60 seconds remain, and on a 401 it renews and retries once. Lookup functions no longer pass a token around, so renewal works partway through long call-number searches and when the call-number dialog stays open for a long time.
  • Renewal takes a per-user lock and rechecks the stored token, so two overlapping runs don’t both spend the same refresh token. If FOLIO rejects the refresh token, Boffo clears the stored tokens.
  • Without an unexpired refresh token, a command shows the credentials dialog. If renewal fails partway through a run, Boffo stops and asks the user to log in again.
  • Clear FOLIO token deletes all four properties.

Tenant

  • No tenant or URL is hard-coded.
  • When a user has no stored refresh token, Boffo copies boffo_folio_url and boffo_folio_tenant_id from the script properties over that user’s stored values before showing the credentials dialog. Every existing user starts without a refresh token, so each one gets the new tenant once. After logging in, values the user typed are kept.
  • This also fills in the values for users who got Boffo through an admin install, where onInstall() doesn’t run.

Docs

  • PRIVACY.md, docs/usage.md, README.md, and the text in credentials-form.html describe the stored tokens.
  • CHANGES.md is renamed to CHANGELOG.md and follows Keep a Changelog 1.1.0, with dated version headings and comparison links. The Makefile and codemeta.json point to the new name.

Consortium notes

Boffo calls:

  • POST /authn/login-with-expiry and POST /authn/refresh
  • GET /instance-statuses?limit=0
  • GET /inventory/items
  • GET /locations

It never queries instance records directly. Items and holdings belong to the member tenant, so lookups should work. During testing, confirm that the Title and Contributor names columns are filled in for an item on a shared instance, and that the location picker lists Caltech’s locations.

Before release

  • Set the Apps Script script property boffo_folio_tenant_id to cs00000003m013.
  • Run the test plan below in a test deployment.

Merging this doesn’t change the published add-on, which stays pinned to its Apps Script version until a new version is created and selected in the Marketplace SDK settings.

Test plan

Not yet tested against real FOLIO. The token helpers were checked offline in Node with fake Apps Script services (login, cookie parsing, renewal before expiry, retry after a 401, a rejected refresh, the old-tenant migration, and Clear FOLIO token).

For steps 2 and 3, run small functions from the Apps Script editor as yourself to change your user properties, for example:

function t1() { PropertiesService.getUserProperties().setProperty('boffo_folio_api_token_expires', new Date(Date.now() + 30000).toISOString()); }
  1. First login: run Clear FOLIO token, then a barcode lookup. The dialog should show the new tenant, the lookup should work after you log in, and all four token properties should be set.
  2. Long lookup: a single Apps Script run is capped at about 6 minutes, so run t1() and then start a large call-number range search. The execution log should show one renewal partway through. Also leave the call-number dialog open for more than 10 minutes before submitting.
  3. Refresh-token expiry: set boffo_folio_refresh_token_expires to a past time; the next command should show the credentials dialog. Separately, set the refresh token to junk and run t1(); Boffo should clear the tokens and ask you to log in again.
  4. Existing user with the old tenant: set boffo_folio_tenant_id to fs00001057 and boffo_folio_api_token to any string, and delete the refresh-token properties. A lookup should open the dialog with cs00000003m013 filled in.

If renewal fails in step 2, check first that Apps Script sends the Cookie header on the /authn/refresh request.

🤖 Generated with Claude Code

t4k and others added 2 commits November 4, 2026 08:08
FOLIO no longer accepts long-lived API tokens, so the token from
/authn/login stopped working. Boffo now logs in with
/authn/login-with-expiry, stores the access token, refresh token, and
both expiration times in user properties (never the password), and
renews the access token with /authn/refresh when it is about to expire
or FOLIO returns 401, retrying the request once. All FOLIO GETs go
through folioFetch(), so renewal also works partway through long
call-number searches.

Caltech's FOLIO is moving to the SCELC member tenant cs00000003m013.
When a user has no stored refresh token, which is true of every
existing user, Boffo copies the default URL and tenant from the script
properties over the user's stored values before showing the
credentials dialog. The script property boffo_folio_tenant_id must be
updated in the Apps Script project.

"Clear FOLIO token" deletes all of the token data. PRIVACY.md, the
usage docs, README.md, the credentials form, and CHANGES.md describe
the new behavior.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rename CHANGES.md to CHANGELOG.md and follow Keep a Changelog 1.1.0:
add the standard header, rewrite each version heading as
"[X.Y.Z] - YYYY-MM-DD" using the tagged release dates, and add
comparison links at the bottom. Released entries are unchanged; the
Unreleased section groups its entries under Added, Changed, and Fixed.

Update the release steps in the Makefile and releaseNotes in
codemeta.json to point to the new file name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@t4k
t4k merged commit 7dc6825 into main Nov 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant