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:
- Client generates a random
code_verifier(43–128 characters) and computescode_challenge = SHA256(code_verifier) - Client redirects user to authorization server with
code_challenge - User authenticates at the authorization server
- Authorization server redirects back with an authorization
code - Client exchanges
code+code_verifierfor tokens at the token endpoint - Authorization server verifies
SHA256(code_verifier) == code_challengebefore 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 |
|---|---|---|
sub | Subject identifier (unique, stable) | auth0|507f1f77bcf86cd799439011 |
email | User’s email | partner@bakermckenzie.com |
email_verified | Whether email is confirmed | true |
name | Display name | Sarah Chen |
iss | Token issuer | https://accounts.google.com |
aud | Intended audience (your client_id) | your-legal-saas-client-id |
exp | Expiration | 1716003600 |
nonce | Replay protection | n-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) pattern | Tokens stay server-side; SPA gets httpOnly session cookie | Best — seamless |
| In-memory (JavaScript variable) | Lost on refresh; no XSS exfiltration risk | Requires silent re-auth on refresh |
| httpOnly cookie via token endpoint | Needs same-origin token endpoint | Good — cookie-based |
| XSS = full token theft | Never use | |
| XSS = 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 admin | scope=openid email (for authentication) |
| Request all scopes at login | Request additional scopes incrementally when needed |
| Single token for all operations | Different 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 APIs | OAuth 2.0 client credentials or authorization code | Strict scope limits; audit requirements |
| Bar association portals | OIDC SSO (lawyer authenticates via bar IdP) | Verify bar membership status claims |
| Client identity providers | OIDC federation (firm’s Azure AD/Okta) | Multi-tenant IdP routing |
| Legal research databases | API key or OAuth client credentials | Rate 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:
- User enters email at login (
partner@bakermckenzie.com) - Your system looks up the domain → tenant mapping
- Redirects to the correct IdP (Baker McKenzie’s Azure AD instance)
- Receives the OIDC response and validates against that IdP’s configuration
- 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 selection | Authorization Code + PKCE for all user-facing flows |
| State parameter | Cryptographic random value; verify on callback (CSRF protection) |
| Redirect URI validation | Exact match only — no wildcards, no open redirectors |
| Token storage | BFF pattern for SPAs; server-side for web apps |
| Scope | Minimum required; request incrementally |
| OIDC validation | Verify iss, aud, exp, nonce, and signature on every id_token |
| Token lifetime | Access: 5–15 min; Refresh: 7 days with rotation |
| Client secrets | Rotated quarterly; stored in secrets manager, never in code |
| PKCE | SHA256 code challenge; 43+ character verifier |
| Logout | Implement 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.