Deep Linking with OpenAthens Keystone
Part of the Deep Linking series. This page covers the OpenAthens Keystone (OIDC) authentication path. If you have not read the main deep-linking overview yet, start there first — it explains what deep linking is, how it works at a high level, and where each authentication method stands.
How It Works
OpenAthens Keystone is a gateway that bridges our OIDC-based authentication (Auth0) to the SAML federations used by academic institutions. From a user's perspective the login experience is seamless — they click a link, their institution's login page appears, they authenticate, and they land on the resource. From an engineering perspective, the destination URL must survive the SAML and OIDC round-trips that happen in between.
The approach we use is straightforward:
- The user follows a WAYFless login URL that includes both the institution identifier and the target resource.
- Our
oa-deeplink.tsroute stores the target URL in an HTTP-only cookie before handing off to OpenAthens. - The OIDC callback reads the cookie once authentication completes and redirects the user to their destination.
The cookie acts as a safe holding pen: it is out of reach of client-side JavaScript, it survives cross-origin redirects with sameSite: lax, and both the entry point and the callback run on the same domain so neither loses sight of it.
Entry Points: WAYFless Login URLs
Keystone supports WAYFless login URLs — links that bypass the institution discovery page and go straight to a known identity provider. Two forms exist:
Simple WAYFless (authentication only, no target):
https://connect.openathens.net/{API_NAME}/{APP_ID}/login?entity={entity}
Deep-linking WAYFless (authentication + target resource):
https://connect.openathens.net/{API_NAME}/{APP_ID}/login?entity={entity}&target={target}
In both:
| Placeholder | Meaning |
|---|---|
{API_NAME} |
Your OpenAthens domain (e.g. statista.com) |
{APP_ID} |
The OIDC application ID from the SP dashboard |
{entity} |
The federation entityID of the user's institution (URL-encoded) |
{target} |
The URL of the resource page (URL-encoded) |
In our implementation we route users through our own /oa-deeplink endpoint rather than directly to this URL, so that we can store the target in a cookie before the OpenAthens round-trip starts. The target parameter is not passed on to Keystone — we use the cookie approach.
The Full Flow
sequenceDiagram
actor User
participant DiscoverySystem as Discovery System /<br/>Library Portal
participant OADeeplink as remix-sso<br/>/oa-deeplink
participant Cookie as Cookie Store<br/>(__sso_redirect)
participant Keystone as connect.openathens.net<br/>(Keystone OIDC)
participant IdP as Institution IdP<br/>(SAML)
participant Auth0 as Auth0<br/>(openathens-keystone-login)
participant Callback as remix-sso<br/>/sso/callback
participant Target as Target Resource Page
User->>DiscoverySystem: Clicks article link
DiscoverySystem->>OADeeplink: GET /oa-deeplink?entity=https://idp.bigstate.edu/entity&target=https://statista.com/statistics/123
OADeeplink->>OADeeplink: Validate entity + target params
OADeeplink->>Cookie: Set __sso_redirect = "/statistics/123?__sso_origin=https://statista.com" (httpOnly, sameSite:lax)
OADeeplink->>User: 302 → https://connect.openathens.net/statista.com/{APP_ID}/login?entity=https://idp.bigstate.edu/entity
User->>Keystone: Follows WAYFless login URL
Keystone->>IdP: SAML AuthnRequest (directed at institution)
IdP-->>User: Login prompt
User->>IdP: Credentials
IdP-->>Keystone: SAML Assertion
Keystone->>Auth0: OIDC callback to custom social connection
Auth0->>Auth0: Run post-login Actions (resolve institution, enrich claims)
Auth0->>User: 302 → /sso/callback?code=…
User->>Callback: Follows redirect with auth code
Callback->>Auth0: Token exchange (code → tokens)
Callback->>Callback: Validate tokens, establish session
Callback->>Cookie: Read __sso_redirect cookie
Callback->>User: 302 → /statistics/123?__sso_origin=https://statista.com ✓
User->>Target: Lands on the specific statistics page
Our Implementation: oa-deeplink.ts
The Remix SSO route at app/routes/oa-deeplink.ts implements this flow in six steps.
Step 1 — Parse and validate incoming parameters
The route expects two query parameters:
entity(required): the federation entityID of the user's institution.target(optional): the full URL of the resource page to land on after authentication.
Both are validated with Zod. A missing or malformed entity returns 400 Bad Request immediately — without knowing which institution to target, there is no point in proceeding.
Step 2 — Transform the target URL
The getTargetUrl function strips the target down to a path-relative form and appends __sso_origin as a query parameter. This serves two purposes: it avoids storing full absolute URLs in the cookie (which might carry sensitive query strings), and it provides the origin domain to downstream code that needs to reconstruct the full URL.
// Input: https://www.statista.com/statistics/269025/worldwide-mobile-app-revenue-forecast/
// Output: /statistics/269025/worldwide-mobile-app-revenue-forecast/?__sso_origin=https://www.statista.com
If target is absent, the stored value defaults to "/" — authentication still works, the user just lands on the homepage instead of a specific page.
Step 3 — Store the target in an HTTP-only cookie
The __sso_redirect cookie is configured as:
httpOnly: true— not accessible to client-side JavaScript.sameSite: "lax"— survives the top-level cross-site redirects in the SAML/OIDC round-trip.- Scoped to
APP_DOMAIN— readable by the OIDC callback handler on the same domain.
Step 4 — Redirect to Keystone
The route returns a 302 redirect to:
https://connect.openathens.net/statista.com/{APP_OPENATHENS_APPLICATION_ID}/login?entity={entity}
The target is deliberately not forwarded to Keystone here — the cookie is our mechanism for carrying the destination, not Keystone's target parameter.
Step 5 — Keystone authenticates, Auth0 processes the result
Keystone handles the SAML round-trip with the institution's IdP. Once the assertion is validated, Keystone hands off to Auth0 via the openathens-keystone-login custom social connection. Auth0 runs its post-login Actions (institution resolution, claims enrichment) and redirects to /sso/callback.
Step 6 — Callback reads the cookie and finishes
/sso/callback exchanges the authorization code for tokens, validates them, and establishes the session. It then reads the __sso_redirect cookie, redirects the user to that path, and immediately clears the cookie.
The OpenAthens Redirector (Library Admin Tool)
Worth knowing about for context, but not something we implement or maintain. The redirector (go.openathens.net/redirector/{ORG}?url=TARGET) is a library-administered tool that wraps any vendor URL behind a consistent prefix. OpenAthens handles institution routing internally and delivers the user to the target page. It is a consumer of the same WAYFless and deep-link infrastructure described here.
If a library administrator asks how redirector links work, point them to About the OpenAthens Redirector.
Common Pitfalls
The target gets lost during the round-trip.
Check the sameSite cookie setting (lax is correct; strict will cause the cookie to be dropped on the SAML POST-back). Also verify the cookie domain is scoped correctly — if the callback runs on a different subdomain than the entry point, the cookie must be scoped to the shared parent domain.
The entity parameter is not URL-encoded.
Federation entityIDs look like URLs themselves (e.g. https://idp.bigstate.edu/entity). When embedded as a query parameter value, they must be URL-encoded. Forgetting this causes the server to misparse the URL.
The target URL is not validated.
An unvalidated target parameter is an open redirect vulnerability. The getTargetUrl function addresses this — anything that fails URL parsing throws 400, and only path-relative results are stored.
Testing
With OpenAthens test accounts: The SP dashboard at sp.openathens.net allows you to create test accounts under your own OpenAthens domain. They behave like real federated users. See OpenAthens test accounts for how to set up accounts with specific attributes.
Locally with remix-sso:
http://localhost:3000/oa-deeplink?entity=https://idp.test.openathens.net/entity&target=https://www.statista.com/statistics/269025/worldwide-mobile-app-revenue-forecast/
The __sso_redirect cookie will be set and the browser redirected to the Keystone login page. After authenticating with a test account, you should land on the statistics page.
See Also
- Deep Linking Overview
- OpenAthens Keystone Architecture — how Keystone integrates with Auth0 in our stack.
- OpenAthens WAYFless access and deep linking in Keystone — official Keystone documentation.
- OpenAthens WAYFless access and deep linking in the Federation — official SAML federation documentation.
- OpenAthens Best Practices
- About the OpenAthens Redirector — the library-side tool that wraps vendor URLs; a consumer of these deep-link capabilities, not something we implement.
- SAML v2.0 — OASIS Standard — the protocol specification underlying the SAML federation round-trip that Keystone bridges.
- How OpenID Connect Works — OpenID Foundation — background on the OIDC layer Auth0 uses to communicate with Keystone.