Understanding the EZProxy/OverDrive connection
Disclaimer and intended audience
This document describes a one off, OverDrive style EZProxy integration that we maintain only for a small set of existing Statista customers who cannot use modern SSO options such as SAML or OIDC.
It is aimed at engineers, SSO specialists, and EZProxy administrators who need to understand how this legacy-compatible flow works end to end in order to support, debug, or gradually phase it out in favor of more standard integrations.
OverDrive Mode: Custom EZProxy Adventures with Statista
Before diving deep into the nitty-gritty details of this implementation, a little bit of history is important to understand the reasoning behind this decision.
In the beginning, our customers using EZProxy already for their authentication and authorization needs, relied on our IP based authentication to get access to Statista's premium content. Since, things have changed: customers have acquired proper identity providers for their infrastructures, Statista's focus has shifted toward a more data driven business model, and IP based login have become a nuance and a liability, granting anonymous, uncontrolled access to premium resources under the umbrella of a Statista Campus license acquired by the institutions.
With the sunset of IP based authentication, we encouraged our existing customers to migrate to better authentication alternatives such as SAML or OIDC. However, not every customer has the budget or willingness to invest in a proper IdP if they are already relying on EZProxy for that purpose. There's a small group of customers who fit this category and are considered high profile customers from a sales perspective. For these customers, OCLC offered a custom development they had previously done for OverDrive, which, as mentioned on our discussions with them: will fulfill our minimum requirements for a secure and acceptable authentication method.
Behind the Proxy: How EZProxy Works
Before we get into the specifics of our Statista setup, it helps to understand the role EZProxy plays as the middle layer between users, identity providers, and content platforms.
If you are not yet familiar with EZProxy, or need a quick refresher, please refer to the Understanding EZProxy section.
How the OverDrive-Style Solution Works
At a high level the OverDrive-style solution turns EZProxy into a dedicated authentication gateway: it authenticates the user, mints a stable opaque identifier, signs the request, and then gets out of the way so the user can talk directly to the vendor.
No content is proxied; only the authentication hop passes through EZProxy.
High level authentication flow
The core flow can be summarized like this: the user arrives at EZProxy, they verify their session and permissions, generate a persistent token, compute an authentication hash, and finally redirect them to Statista with all required parameters.
sequenceDiagram
participant U as User
participant E as EZProxy
participant A as Library Auth System
participant V as Vendor (Statista)
U->>E: Request vendor access
E->>A: Authenticate user (SAML/OIDC/LDAP/etc.)
A-->>E: User identity confirmed
E->>E: Verify session & group permissions
E->>E: Generate/retrieve persistent token (ezproxy.tkn)
E->>E: Build timestamp (UTC ISO 8601)
E->>E: Compute SHA-1 auth hash (token | timestamp | secret)
E-->>U: HTTP redirect with token, timestamp, hash, landing URL
U->>V: Follow redirect to vendor endpoint
V->>V: Validate hash and timestamp
V-->>U: Grant access to premium content
Token generation and persistence
The solution relies on a persistent, opaque token that uniquely represents a user without exposing their real identity.
flowchart TD
A["User identity<br/>(username, IdP attributes)"] --> B["Normalize username\n(e.g. lowercase)"]
B --> C["Combine inputs:<br/>- token salt<br/>- normalized username<br/>- server identifier<br/>- service prefix (e.g. 'ods')<br/>- obscurity value"]
C --> D[SHA-512 hash]
D --> E[Take first 10 hex chars]
E --> F["Prefix with service tag<br/>(e.g. 'ods' + 10 hex chars)"]
F --> G[Store in ezproxy.tkn<br/>if new mapping]
G --> H[Use as PatronID/token<br/>for vendor redirects]
Key properties of this token design are: deterministic for the same user and server, persistent via
the ezproxy.tkn file, unique with collision checks, and opaque from the vendor’s perspective.
Because ezproxy.tkn is the single source of truth for these mappings, losing it effectively
breaks all existing vendor-side accounts linked to those tokens.
Redirect and Hash Construction
To hand off a user to the vendor, EZProxy builds a signed redirect URL containing the token, a timestamp, and an authentication hash based on a shared secret.
flowchart TD
A[Persistent token] --> B["Generate UTC timestamp<br/>(ISO 8601)"]
B --> C["Concatenate:<br/> token | timestamp | shared secret"]
C --> D["Compute SHA-1 hash<br/>(40 hex chars)"]
D --> E["Build redirect URL:<br/>/BANGAuthenticate.dll?<br/> Action=ExternalAuth<br/> PatronID=token<br/> Timestamp=timestamp<br/> Hash=hash<br/> URL=landing_url<br/> (plus ILSName, LibraryID if needed)"]
E --> F[Redirect user to vendor endpoint]
This simple concatenation hash (not HMAC) is a legacy requirement, but combined with session checks, group-based access control, and token obfuscation, it still forms part of a layered security approach.
Wiring OverDrive-Style Auth into Authi0
To make this OverDrive-style flow play nicely with our existing SSO stack, we let EZProxy do its thing (authenticate and mint a token), then hand everything over to Auth0 and our own services to finish the OpenID Connect flow on behalf of the user.
The end result is that Statista sees a regular Auth0-federated user, while the actual
authentication story is driven by EZProxy and our ezproxy-auth-service behind the scenes.
remix_sso as the BANGAuthenticate endpoint
On the frontend of this integration, our remix-sso app exposes a /BANGAuthenticate.dll route
that takes the place of the original vendor endpoint.
This route validates the incoming query parameters from EZProxy using a strict Zod schema, ensuring they match the OverDrive-style contract before we go anywhere near Auth0.
Once the parameters pass validation, remix-sso:
- Encodes the resulting JSON object in base64.
- Adds it as the
access_typesearch parameter. - Appends
connection=ezproxy-loginso Auth0 uses the custom social connection wired to ourezproxy-auth-service.
sequenceDiagram
participant U as User
participant EZ as EZProxy
participant RS as remix_sso (/BANGAuthenticate.dll)
participant A0 as Auth0 (ezproxy-login)
U->>EZ: Request Statista access
EZ-->>U: Redirect to /BANGAuthenticate.dll<br/>with action, patron_id, timestamp, hash, ils_name
U->>RS: GET /BANGAuthenticate.dll?...
RS->>RS: Validate params with ezProxyAuthParamsSchema
RS->>RS: Base64-encode validated JSON -> access_type
RS-->>U: Redirect to Auth0<br/>connection=ezproxy-login&access_type=...
U->>A0: Auth0 authorize request
Custom social connection and ezproxy-auth-service
In Auth0, we configured a custom social connection that points its OAuth endpoints to our
ezproxy-auth-service lambda: /ezproxyauth/authorize and /ezproxyauth/oauthtoken.
Within Auth0, the access_type query parameter is mapped to ezproxy_claims, which becomes the
payload our lambda receives and validates.
The ezproxy-auth-service is responsible for:
- Parsing and validating the
ezproxy_claimsagain (defensive validation). - Verifying the OverDrive-style hash using the shared secret.
- Looking up the correct group account in a JSON configuration file encrypted with ejson.
The [EZProxy customers file] (https://github.com/CPE-Orga/platform-sso-services/blob/5ff844767956497300eb828eb90d34d13a2a532f/data/prod_ezproxy-domains.json) contains the shared secrets and additional customer data.
The shared secret for each domain is stored encrypted using the ejson
library, and _groupAccountId identifies the group account that holds the Statista license in our
users database.
Once the hash and claims are verified, the service resolves the actual user_id behind that group
account and uses to generate the authorization code we return to Auth0.
Here, user.id is the internal identifier of the group account, and ezProxyAuthParams.patron_id
keeps the link back to the EZProxy token so we can respect that identity across the flow.
sequenceDiagram
participant RS as remix_sso
participant A0 as Auth0
participant EZS as ezproxy-auth-service
participant DB as Platform DB
RS->>A0: /authorize connection=ezproxy-login&access_type=...
A0->>EZS: /ezproxyauth/authorize with ezproxy_claims
EZS->>EZS: Validate ezproxy_claims & hash
EZS->>DB: Lookup group account by _groupAccountId
DB-->>EZS: user record (group account)
EZS->>EZS: generateAuthorizationCode(clientId, clientSecret, user.id, patron_id)
EZS-->>A0: Authorization code
A0-->>RS: Redirect back with authorization code
From authorization code to JWT and Statista session
The /ezproxyauth/oauthtoken endpoint is the token side of this custom connection.
It validates the authorization code, looks up the same group account user, and issues a signed JWT that Auth0 will accept as the result of the social login.
The user object is built from our group account data, using a query to our legacy users database.
Auth0 consumes this JWT, creates (or retrieves) the corresponding federated user, and then
continues with its standard flow: it issues its own id and access tokens and redirects back to the
/callback endpoint in remix_sso.
Finally, remix_sso establishes the session, sets the necessary cookies, and redirects the browser
to Statista with an authenticated user that now looks like any other Auth0 user from Statista’s
perspective.
flowchart TD
A[EZProxy token & claims] --> B[remix_sso /BANGAuthenticate.dll]
B --> C["Auth0 custom social<br/>(ezproxy-login)"]
C --> D[ezproxy-auth-service<br/>/ezproxyauth/authorize]
D --> E[Issue authorization code<br/>for group account user]
E --> F[ezproxy-auth-service<br/>/ezproxyauth/oauthtoken]
F --> G["Signed JWT (user from group account)"]
G --> H[Auth0 federated user & tokens]
H --> I[remix_sso /callback<br/>creates session & cookies]
I --> J[Redirect to Statista<br/>with authenticated user]
Why we did it this way
This design lets us keep EZProxy in the driver’s seat for authentication, while still presenting a clean, standards-based OIDC integration to Statista via Auth0.
By using opaque EZProxy tokens and group accounts, we avoid exposing individual user identities to Statista and still respect academia licensing models.
Reusing the OverDrive-style pattern also means we build on a flow that OCLC already supports and documents, instead of inventing a one-off protocol for a handful of high-profile customers.
Auth0’s custom social connection acts as a flexible bridge: it lets us wrap our proprietary EZProxy based authentication in a familiar OAuth2/OIDC envelope that our platform already know how to consume.
Finally, because we had no other option. This is a crutch we offer to our existing customers out of courtesy and should not be offered to any new customers
Resources
OCLC – OverDriveSite directive (configuration and qualifiers) Auth0 – Connect apps to generic OAuth2 authorization servers (generic/custom OAuth2 connection)