CISSP-114 · Lesson 6 of 12 · 28 min

Federation: SAML, OAuth 2.0 and OpenID Connect

Federation extends single sign-on across organizational boundaries. Instead of every software-as-a-service provider, partner portal and government site keeping its own copy of your users and their passwords, they trust your identity provider to authenticate your users and send them a signed statement of who the user is. Sub-domain 5.3 of the CISSP covers federated identity with third-party services, whether on premises, in the cloud or hybrid. This lesson explains the trust model, compares the three protocols you will meet everywhere, SAML 2.0, OAuth 2.0 and OpenID Connect, and walks through the flows, tokens and mistakes that matter for security. Cryptographic signatures and certificates, which make federation trustworthy, are covered in CISSP-112.

The federation trust model

Federation has three parties. The user, or subject, wants access. The identity provider, IdP, authenticates the user and issues assertions or tokens; in OpenID Connect it is called the OpenID provider. The service provider, SP, also called the relying party, RP, consumes those assertions and grants access. The IdP and SP establish trust in advance by exchanging metadata: identifiers, endpoint URLs and the public keys or certificates used to sign and encrypt messages. After that, the SP accepts the IdP's signed statements without ever seeing the user's credentials.

The benefits are significant. Users have one set of credentials and one place to authenticate strongly. The organization controls authentication policy, such as multifactor requirements, for every federated application, and disabling one account at the IdP cuts access to all of them. Service providers no longer store passwords they could lose in a breach. The risks follow from the trust: if the IdP is compromised, or its signing key is stolen, every relying party can be fooled; and if a relying party validates assertions carelessly, an attacker may forge or replay them. Federation also creates privacy questions, since the IdP learns which services a user visits and attributes flow to third parties.

The three main federation protocols compared
TermSAML 2.0OAuth 2.0OpenID Connect
PurposeAuthentication and attribute exchange (federated SSO)Delegated authorization to APIsAuthentication layer on top of OAuth 2.0
IssuerIdentity providerAuthorization serverOpenID provider
ConsumerService providerResource server (API), used by a clientRelying party
Token formatXML assertion, signedAccess token, often a JWT or opaque stringID token, a signed JWT, plus OAuth tokens
Typical useEnterprise SSO to SaaSAn app accessing an API on a user's behalfSign in with a corporate or social account, mobile apps

SAML 2.0

The Security Assertion Markup Language, SAML 2.0, standardized by OASIS in 2005, remains the workhorse of enterprise SSO to software-as-a-service applications. A SAML assertion is an XML document, signed by the IdP, containing statements about the subject: an authentication statement saying when and how the user authenticated, attribute statements such as email address and group membership, and sometimes authorization decision statements. Assertions are delivered through bindings, most often the HTTP POST binding, where the browser carries an auto-submitted form from the IdP to the SP's assertion consumer service.

In SP-initiated SSO the user starts at the application, which redirects to the IdP with an authentication request; in IdP-initiated SSO the user starts at the IdP's portal, which sends an unsolicited assertion. SP-initiated flows are safer because the SP can match the response to its own request, which helps prevent replay and injection. A relying party must validate every assertion fully: check the XML signature with the IdP's trusted key and make sure the signature covers the part of the document it actually uses; check the issuer, the audience restriction naming this SP, the recipient URL, the validity window and the one-time use of the assertion identifier. Attacks such as XML signature wrapping have exploited parsers that validated a signature on one element but read the identity from another, which is why mature, well-maintained SAML libraries matter.

OAuth 2.0

OAuth 2.0, RFC 6749, solves a different problem: delegated authorization. It lets a user grant an application, the client, limited access to their resources on another service, the resource server, without giving the client their password. The authorization server, after authenticating the user and obtaining consent, issues the client an access token with specific scopes, such as read access to a calendar. The resource server accepts requests carrying a valid token. Access tokens are short-lived; refresh tokens, which are long-lived and must be protected carefully, let the client obtain new access tokens without bothering the user.

OAuth defines several grant types, and current best practice, captured in the OAuth 2.0 Security Best Current Practice, RFC 9700 of January 2025, and the draft OAuth 2.1, narrows them. The authorization code grant with Proof Key for Code Exchange, PKCE, is recommended for web, mobile and single-page applications. The client credentials grant is used for machine-to-machine access where no user is involved. The device authorization grant serves devices without browsers, such as televisions. The implicit grant, which returned tokens in the browser URL, and the resource owner password credentials grant, where the client collected the user's password, are deprecated because they expose tokens or passwords.

PKCE, RFC 7636, protects the authorization code from interception. The client creates a random secret, the code verifier, and sends only its SHA-256 hash, the code challenge, with the authorization request. When it later exchanges the authorization code for tokens, it must present the original verifier; an attacker who stole the code does not have it.

PKCE with the S256 method: the client sends a hash of a secret verifier and must later prove it knows the verifier. The output matches the RFC 7636 example.
import hashlib, base64, secrets
def b64url(b):
    return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
# RFC 7636 Appendix B example verifier
verifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'
challenge = b64url(hashlib.sha256(verifier.encode()).digest())
print('code_verifier :', verifier)
print('code_challenge:', challenge)
print('matches RFC example:', challenge == 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM')
# A client generates a fresh verifier for every authorization request
fresh = secrets.token_urlsafe(32)
print('fresh verifier length:', len(fresh), 'characters (43 to 128 allowed)')
# The authorization server later checks the presented verifier against the stored challenge
def server_check(stored_challenge, presented_verifier):
    return b64url(hashlib.sha256(presented_verifier.encode()).digest()) == stored_challenge
print('genuine client accepted:', server_check(challenge, verifier))
print('attacker with stolen code but no verifier accepted:', server_check(challenge, 'guess'))
Output
code_verifier : dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
code_challenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
matches RFC example: True
fresh verifier length: 43 characters (43 to 128 allowed)
genuine client accepted: True
attacker with stolen code but no verifier accepted: False

OAuth is not an authentication protocol on its own. An access token says that some user authorized this client to call this API; it does not tell the client who the user is in a reliable, standardized way. Applications that treated access tokens as proof of identity created vulnerabilities, which is why OpenID Connect was defined.

OpenID Connect

OpenID Connect, OIDC, finalized by the OpenID Foundation in 2014, adds an identity layer to OAuth 2.0. In addition to an access token, the OpenID provider returns an ID token: a JSON Web Token, JWT, signed by the provider, stating who the user is, which client it was issued for, when the user authenticated and how. OIDC also defines a user info endpoint for further attributes, standard scopes such as openid, email and profile, and discovery documents that publish the provider's endpoints and signing keys. Because it uses JSON and REST rather than XML, OIDC is the natural choice for mobile and modern web applications and for consumer sign-in with social accounts.

Sequence with four columns: user, client application, OpenID provider and API. 1, the client creates a PKCE verifier and challenge and redirects the user to the provider with scope openid, a state value and the challenge. 2, the user authenticates and consents. 3, the provider redirects back with an authorization code. 4, the client exchanges the code plus the verifier at the token endpoint. 5, the provider returns an ID token, an access token and optionally a refresh token. 6, the client validates the ID token and calls the API with the access token.
The OpenID Connect authorization code flow with PKCE.

A JWT has three base64url-encoded parts separated by dots: a header naming the signing algorithm and key, a payload of claims, and a signature over the first two parts. Standard claims include iss, the issuer; sub, the subject identifier; aud, the audience; exp, the expiry time; iat, the issue time; and nonce, a value that binds the token to the client's request. Base64url is an encoding, not encryption, so anyone holding a JWT can read its claims; sensitive data should not be placed in tokens unless they are encrypted as JWE.

Building and validating a JWT: the payload is only encoded, so anyone can read it. A verifier must pin the algorithm and check signature, audience and expiry; a forged token with alg set to none is rejected.
import base64, hmac, hashlib, json
def b64url(b):
    return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
def unb64url(s):
    return base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
key = b'demo-shared-secret-for-HS256-only'
header = {'alg': 'HS256', 'typ': 'JWT'}
claims = {'iss': 'https://idp.example.ca', 'sub': 'u-48213', 'aud': 'payroll-app', 'exp': 1767258000, 'nonce': 'n-0S6_WzA2Mj'}
signing_input = b64url(json.dumps(header).encode()) + '.' + b64url(json.dumps(claims).encode())
token = signing_input + '.' + b64url(hmac.new(key, signing_input.encode(), hashlib.sha256).digest())
print('token parts:', [len(p) for p in token.split('.')], 'characters')
print('payload is readable by anyone:', json.loads(unb64url(token.split('.')[1]))['sub'])
def verify(tok, now, expected_aud):
    h, p, s = tok.split('.')
    alg = json.loads(unb64url(h)).get('alg')
    if alg != 'HS256':
        return 'reject: unexpected algorithm ' + str(alg)
    good = b64url(hmac.new(key, (h + '.' + p).encode(), hashlib.sha256).digest())
    if not hmac.compare_digest(good, s):
        return 'reject: bad signature'
    c = json.loads(unb64url(p))
    if c['aud'] != expected_aud:
        return 'reject: wrong audience'
    if now >= c['exp']:
        return 'reject: expired'
    return 'accept ' + c['sub']
print(verify(token, 1767254400, 'payroll-app'))
print(verify(token, 1767254400, 'crm-app'))
print(verify(token, 1767260000, 'payroll-app'))
forged = b64url(json.dumps({'alg': 'none'}).encode()) + '.' + b64url(json.dumps(dict(claims, sub='u-00001')).encode()) + '.'
print(verify(forged, 1767254400, 'payroll-app'))
Output
token parts: [40, 156, 43] characters
payload is readable by anyone: u-48213
accept u-48213
reject: wrong audience
reject: expired
reject: unexpected algorithm none

The example uses a shared-secret HMAC for brevity. Real identity providers sign ID tokens with asymmetric keys, such as RS256 or ES256, and publish the public keys in a JSON Web Key Set, so relying parties can verify tokens without holding any secret. Libraries have historically been tricked into accepting tokens with the algorithm set to none, or into verifying an RS256 token as HS256 using the public key as the secret, which is why verifiers must pin the expected algorithm rather than trusting the token header.

Federation assurance and design choices

NIST SP 800-63C-4 defines federation assurance levels. In simplified terms, FAL1 uses signed bearer assertions, FAL2 adds stronger protection against assertion injection and requires trust to be established in advance, and FAL3 requires the subject to prove possession of a key bound to the assertion, a holder-of-key assertion, so that a stolen assertion alone is useless. Bearer tokens, like cash, work for whoever holds them; sender-constrained tokens, using mutual TLS or the newer DPoP mechanism, bind a token to a key the legitimate client holds.

Federation design choices and good practice
DecisionGood practice
Protocol choiceSAML for established enterprise SaaS; OIDC for new web, mobile and API-based applications
Flow choiceAuthorization code with PKCE; avoid implicit and password grants; SP-initiated SAML
ValidationUse maintained libraries; check signature, issuer, audience, expiry, nonce or one-time use
Token lifetimeShort access tokens (minutes); rotate refresh tokens and detect reuse
Token storageAvoid browser local storage for long-lived tokens; prefer secure, HTTP-only cookies or backend-for-frontend patterns
Key managementRotate signing keys; publish keys via metadata or JWKS; monitor for unexpected keys
Attribute releaseSend only the attributes each relying party needs; obtain consent where required by privacy law

Federation with third parties also raises contractual and privacy questions. A software-as-a-service contract should say which attributes the provider receives and how it protects them, how quickly access ends when the IdP disables a user, how the provider supports automated provisioning and deprovisioning with SCIM, covered in lesson 9, and what logs it provides. Cross-border transfers of identity attributes engage privacy law, covered in CISSP-110. Organizations that join trust federations, such as research and education federations, agree to common policies so that members can trust each other's assertions at scale.

Worked problem

Reviewing a SaaS integration

A marketing team connected a new analytics SaaS tool to the corporate identity provider using OIDC. A review finds the app uses the implicit flow, stores tokens in browser local storage, accepts ID tokens without checking the audience and receives every user attribute, including employee number and manager. Identify the risks and the fixes.

Show solution
  1. Implicit flow exposes tokens in URLs and browser history; switch to the authorization code flow with PKCE.
  2. Local storage is readable by any script on the page, so a cross-site scripting flaw would leak tokens; use secure, HTTP-only cookies or a backend.
  3. Without an audience check, an ID token issued to a different application could be replayed to this one; require aud to equal the app's client ID.
  4. Excess attributes violate data minimization; release only email and display name, and record the decision.

Answer: Each finding is a common real-world weakness. The identity team should publish a standard integration checklist and refuse connections that do not meet it.

Thinking like a manager

Federation questions test whether you know which protocol does what: SAML and OIDC authenticate users to applications, OAuth delegates authorization to APIs. They also test trust: a relying party trusts the IdP's signing key completely, so that key and the IdP itself are critical assets, and every relying party must validate assertions properly. When choosing between answers, prefer authorization code with PKCE, short-lived and sender-constrained tokens, strict validation and minimal attribute release, and remember that federation centralizes both control and risk.

Common mistakes

  • MistakeUsing an OAuth access token as proof of the user's identity.

    InsteadUse OpenID Connect ID tokens, validated for issuer, audience, expiry and nonce, for authentication.

  • MistakeUsing the implicit or password grant in new applications.

    InsteadUse the authorization code grant with PKCE, and client credentials for machine-to-machine access.

  • MistakeTrusting the algorithm named in a token's header.

    InsteadPin the expected algorithm and keys in the verifier.

  • MistakePutting sensitive data in JWT claims assuming they are hidden.

    InsteadRemember JWTs are only encoded; minimize claims or encrypt them.

  • MistakeReleasing every directory attribute to every SaaS provider.

    InsteadRelease only what each relying party needs, under contract.

Summary and key terms

  • Federation lets relying parties trust an identity provider's signed assertions instead of managing their own credentials.
  • SAML 2.0 uses signed XML assertions, mainly for enterprise SSO; SP-initiated flows are safer.
  • OAuth 2.0 delegates authorization to APIs with scoped, short-lived access tokens; it is not authentication.
  • OpenID Connect adds a signed ID token, a JWT, so applications can authenticate users.
  • Use the authorization code grant with PKCE; implicit and password grants are deprecated (RFC 9700, January 2025).
  • JWTs are encoded, not encrypted; verifiers must pin algorithms and check issuer, audience, expiry and nonce.
  • Federation assurance levels rise from bearer assertions to holder-of-key assertions.
Identity provider
The party that authenticates users and issues assertions or tokens.
Relying party
The party that consumes assertions or tokens and grants access.
SAML assertion
A signed XML statement about a subject's authentication and attributes.
Access token
An OAuth credential giving a client scoped access to an API.
Refresh token
A long-lived credential used to obtain new access tokens.
ID token
An OpenID Connect JWT stating who the user is and how they authenticated.
PKCE
Proof Key for Code Exchange, which binds an authorization code to the client that requested it.
JWT
JSON Web Token, a compact, signed set of claims.
Bearer token
A token usable by anyone who holds it.
Holder-of-key assertion
An assertion usable only by a party that proves possession of a bound key.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Which protocol is designed for delegated authorization to APIs?

    Show answer

    OAuth 2.0.

  2. What does OpenID Connect add to OAuth 2.0?

    Show answer

    An identity layer with a signed ID token.

  3. What is the code challenge in PKCE?

    Show answer

    The base64url-encoded SHA-256 hash of the code verifier.

  4. Why is SP-initiated SAML preferred?

    Show answer

    The SP can match the response to its own request, reducing replay and injection.

  5. Name three claims a relying party must check in an ID token.

    Show answer

    Issuer, audience and expiry, plus nonce.

  6. Are JWT claims encrypted by default?

    Show answer

    No; they are base64url-encoded and readable.

  7. Which OAuth grants are deprecated?

    Show answer

    Implicit and resource owner password credentials.

  8. Which grant suits a backend service calling an API with no user?

    Show answer

    Client credentials.

  9. What attack exploited SAML parsers that validated one element and read another?

    Show answer

    XML signature wrapping.

  10. What makes a token sender-constrained?

    Show answer

    Binding it to a key the client holds, using mutual TLS or DPoP.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz