Security for Legal SaaS

Episode 20 · Module 5 · Authentication & Identity

OAuth 2.0 and OpenID Connect

18 May 2026 · 10:32 · Security for Legal SaaS

10:32 10:32

Every law firm already has an identity provider. Alice and Dan cover why legal SaaS should delegate authentication rather than managing passwords, the difference between OAuth 2.0 (authorization) and OpenID Connect (authentication), the Authorization Code + PKCE flow, why the Implicit flow is deprecated, the Backend-for-Frontend pattern for SPAs, scope minimisation, Client Credentials for service-to-service communication, multi-tenant IdP routing, court system and e-filing integrations, and the five most common OAuth implementation mistakes.

Today’s Lesson

Security for Legal SaaS — Episode 20: OAuth 2.0 and OpenID Connect

Delegated Authentication — Why Build Your Own When You Can’t?

Every law firm already has an identity provider (IdP) — the system that manages user accounts and authenticates logins. Active Directory, Google Workspace, Okta, Azure AD. When your legal SaaS platform asks lawyers to create yet another username and password, you’re not just adding friction — you’re creating a credential that will be reused, forgotten, or compromised. OAuth 2.0 (RFC 6749) solves authorization delegation. OpenID Connect (OIDC) layers authentication on top. Together, they let users authenticate with their existing identity provider while your application receives only the claims it needs.

Key distinction: OAuth 2.0 is an authorization framework — it answers “what is this application allowed to do?” OIDC is an authentication layer — it answers “who is this user?” Using OAuth 2.0 alone for authentication is a well-documented anti-pattern. Always use OIDC when you need to verify user identity.

For legal SaaS, OIDC integration means: single sign-on (SSO — logging in once and gaining access to multiple systems) with the firm’s existing IdP, centralized access revocation when a lawyer leaves the firm, and no platform-specific passwords to manage, leak, or stuff.

OAuth 2.0 Flows — Choosing the Right One

Authorization Code + PKCE (Recommended)

The Authorization Code flow with PKCE (Proof Key for Code Exchange, RFC 7636) is the recommended flow for all client types — web apps, SPAs, mobile apps, and native applications.

Flow:

  1. Client generates a random code_verifier (43–128 characters) and computes code_challenge = SHA256(code_verifier)
  2. Client redirects user to authorization server with code_challenge
  3. User authenticates at the authorization server
  4. Authorization server redirects back with an authorization code
  5. Client exchanges code + code_verifier for tokens at the token endpoint
  6. Authorization server verifies SHA256(code_verifier) == code_challenge before issuing tokens

Why PKCE? Without it, an attacker who intercepts the authorization code (via a compromised redirect URI, browser history, or referrer header) can exchange it for tokens. PKCE binds the code to the original client — only the party that generated the code_verifier can complete the exchange.

OAuth 2.1 (draft) mandates PKCE for all authorization code grants. It’s no longer optional.

Client Credentials (Service-to-Service)

For backend services communicating without user context — batch processing, scheduled jobs, inter-service API calls. No user interaction. The service authenticates directly with its own credentials. RFC 6749 Section 4.4 defines this flow. For legal SaaS, this is how your document processing service authenticates to your API — with narrowly scoped permissions and rotated credentials.

Implicit Flow — Deprecated

Do not use. The Implicit flow returns tokens directly in the URL fragment. This exposes tokens to browser history, referrer headers, and any JavaScript on the page. OAuth 2.1 formally removes it. If your legal SaaS still uses Implicit flow, migrate to Authorization Code + PKCE immediately.

OpenID Connect as the Authentication Layer

OAuth 2.0 alone does not tell you who the user is — it tells you what they’re authorized to access. OIDC adds an id_token — a JWT containing identity claims:

Claim Purpose Example
subSubject identifier (unique, stable)auth0|507f1f77bcf86cd799439011
emailUser’s emailpartner@bakermckenzie.com
email_verifiedWhether email is confirmedtrue
nameDisplay nameSarah Chen
issToken issuerhttps://accounts.google.com
audIntended audience (your client_id)your-legal-saas-client-id
expExpiration1716003600
nonceReplay protectionn-0S6_WzA2Mj

Critical validation steps (per OIDC Core spec Section 3.1.3.7): verify the signature using the IdP’s published keys (JWKS endpoint), verify iss matches the expected issuer, verify aud contains your client_id, verify exp is in the future, and verify nonce matches what you sent in the authorization request. Skipping any of these checks opens specific attack vectors — issuer confusion, token injection, replay attacks.

Token Storage in SPAs

Single-page applications (SPAs) — where the entire app runs in the user’s browser — cannot keep secrets. There is no client_secret. This makes token storage critical.

Strategy Security UX
Backend-for-Frontend (BFF) patternTokens stay server-side; SPA gets httpOnly session cookieBest — seamless
In-memory (JavaScript variable)Lost on refresh; no XSS exfiltration riskRequires silent re-auth on refresh
httpOnly cookie via token endpointNeeds same-origin token endpointGood — cookie-based
localStorageXSS = full token theftNever use
sessionStorageXSS = full token theft (slightly better — tab-scoped)Never use

The current best practice for SPAs is the Backend-for-Frontend (BFF) pattern: your SPA communicates with a thin backend that holds the tokens server-side and proxies API calls. The SPA itself never touches OAuth tokens — it uses a standard httpOnly session cookie to communicate with its BFF.

Architecture recommendation: For legal SaaS handling privileged documents, always use the BFF pattern. The cost is one lightweight backend service. The benefit is that tokens — which grant access to privileged legal content — never exist in browser-accessible storage.

Scope Minimisation

Request only the permissions your application actually needs. The principle of least privilege applied to OAuth scopes:

Bad Practice Good Practice
scope=openid profile email read write adminscope=openid email (for authentication)
Request all scopes at loginRequest additional scopes incrementally when needed
Single token for all operationsDifferent tokens for different sensitivity levels

For legal SaaS integrating with external systems: court filing systems should request read-only access to case status, not write access to filings. Client IdPs should request only openid, email, and necessary profile claims. Document storage should scope to specific folders/labels, not entire mailbox or drive.

Google’s OAuth scope documentation illustrates granular scoping — each API has narrow, specific scopes rather than blanket access.

Legal SaaS Integrations

Court Systems and E-Filing

Many jurisdictions are deploying electronic filing systems that support OAuth/OIDC for third-party access:

Integration Authentication Pattern Considerations
Court e-filing APIsOAuth 2.0 client credentials or authorization codeStrict scope limits; audit requirements
Bar association portalsOIDC SSO (lawyer authenticates via bar IdP)Verify bar membership status claims
Client identity providersOIDC federation (firm’s Azure AD/Okta)Multi-tenant IdP routing
Legal research databasesAPI key or OAuth client credentialsRate limiting; usage tracking

Singapore’s eLitigation system provides API access for authorised legal technology providers. Integration requires strict adherence to the Judiciary’s authentication specifications and scope limitations.

Multi-Tenant IdP Routing

When your legal SaaS serves multiple law firms, each with their own identity provider, you need tenant-aware IdP routing:

  1. User enters email at login (partner@bakermckenzie.com)
  2. Your system looks up the domain → tenant mapping
  3. Redirects to the correct IdP (Baker McKenzie’s Azure AD instance)
  4. Receives the OIDC response and validates against that IdP’s configuration
  5. Maps the identity to your internal user record

Auth0’s “Organizations” feature and Okta’s multi-tenancy patterns document this architecture. The critical security requirement: never allow IdP confusion — validate that the issuer claim in the id_token matches the expected IdP for that tenant.

Security Checklist

Control Requirement
Flow selectionAuthorization Code + PKCE for all user-facing flows
State parameterCryptographic random value; verify on callback (CSRF protection)
Redirect URI validationExact match only — no wildcards, no open redirectors
Token storageBFF pattern for SPAs; server-side for web apps
ScopeMinimum required; request incrementally
OIDC validationVerify iss, aud, exp, nonce, and signature on every id_token
Token lifetimeAccess: 5–15 min; Refresh: 7 days with rotation
Client secretsRotated quarterly; stored in secrets manager, never in code
PKCESHA256 code challenge; 43+ character verifier
LogoutImplement both RP-Initiated Logout and Back-Channel Logout

OAuth 2.0 Security Best Current Practice (RFC 9700) consolidates all known OAuth security considerations into one document — essential reading for implementers.

Common Mistakes

1. Open redirect on callback URL — If your redirect_uri accepts arbitrary paths, an attacker can steal authorization codes by redirecting to their server. OWASP documents this as “Unvalidated Redirects”.

2. Missing state parameter — The state parameter is a random value your app sends at the start of the OAuth flow and verifies when the response returns, proving the two are linked. Without it, CSRF attacks can force a victim to authenticate as the attacker (login CSRF) or link the attacker’s account to the victim’s session.

3. Accepting tokens from any issuer — If you don’t validate iss, an attacker running their own IdP can issue tokens that your application accepts.

4. Using access tokens for authentication — Access tokens are for API authorization, not identity verification. This creates a “confused deputy” problem — your service trusts a token that an attacker obtained for a different purpose, effectively tricking the service into acting on a forged identity. Always use the id_token for authentication.

5. Storing client_secret in frontend code — SPAs cannot hold secrets. Use PKCE instead of client_secret for public clients. OAuth 2.0 for Browser-Based Apps is explicit on this point.

Conclusion

OAuth 2.0 and OpenID Connect are not optional features for legal SaaS — they’re the mechanism that eliminates platform-specific credentials, enables centralized access control, and integrates with the identity infrastructure law firms already operate. Use Authorization Code + PKCE for all user-facing flows. Use OIDC for authentication, never raw OAuth. Implement the BFF pattern for SPAs. Validate every claim. Minimise scopes. And route each tenant to their correct IdP with strict issuer validation.

The goal is simple: a lawyer authenticates once with their firm’s identity provider, and your platform trusts that assertion — verified cryptographically, scoped minimally, and revocable centrally when they leave the firm.

Next episode: Multi-Factor Authentication — adding a second factor so that a compromised password alone isn’t game over.

Sources & references

  1. IETF, RFC 6749: The OAuth 2.0 Authorization Framework
  2. OpenID Foundation, "OpenID Connect Core 1.0"
  3. OAuth.net, "OAuth 2.0 for Authentication." Anti-pattern documentation
  4. IETF, RFC 7636: Proof Key for Code Exchange (PKCE)
  5. IETF, OAuth 2.1 Draft. PKCE mandatory; Implicit removed
  6. IETF, "OAuth 2.0 for Browser-Based Applications." BFF pattern and SPA guidance
  7. Google, "OAuth 2.0 Scopes." Granular scope examples
  8. Supreme Court of Singapore, eLitigation. Electronic filing system
  9. Auth0, "Organizations." Multi-tenant IdP routing
  10. Okta Developer, "Multi-Tenancy Concepts"
  11. IETF, RFC 9700: OAuth 2.0 Security Best Current Practice
  12. OWASP, "Unvalidated Redirects and Forwards Cheat Sheet"