Skip to content
WICGPublic

About

Device Bound Session Credentials for SSO WICG repository

Resources

Code of conduct

Contributing

Stars

8 stars

Watchers

7 watching

Forks

Latest commit

 

History

51 Commits

Folders and files

Repository files navigation

Device Bound Session Credentials for SSO Explainer

Authors

Contributors

Participate

Issue tracker

Table of Content

Introduction

Cookie theft has emerged as a highly effective method for compromising user accounts, surpassing credential theft (passwords) in efficacy due to intensified efforts to strengthen password-based security (e.g., 2SV and passkeys).

As the existing DBSC protocol lacks support for cross-origin SSO authentication, it creates a significant attack surface. This vulnerability arises if either the IdP or the RP fail to perform periodic proof-of-possession for securely stored cryptographic keys.

Furthermore, even if both parties adopt DBSC, they cannot definitively prove that the two DBSC sessions are bound to the same physical device, which opens another vulnerability: malware with temporary device access can compel the IdP to grant a sign-in to the RP, subsequently binding the RP session to an attacker-owned private key.

Additionally, if malware is present during user's sign-in to the IdP, it can bind that session to its own key material, leading to a "session init attack" where the attacker takes over the session. While DBSC for SSO does not directly mitigate this specific vulnerability, it empowers the IdP to integrate complementary technologies, such as mTLS, to enhance protection.

In “normal” two-party DBSC we say that malware at sign-in time is out of scope. This is reasonable because sign-in on that model is inherently infrequent and noisy for the user. That limits the scalability of malware that has to wait for the sign in ceremony to make sense to the user.

On the other hand, traditional cookie-based authentication of an established session is very frequent (every request) and totally silent. So that is ripe for scaled infostealer malware.

The reason this proposal is valuable and needed is that SSO authentication is somewhere in the middle: It’s relatively frequent (e.g. daily) and mostly invisible to the user apart from maybe some quick redirects. Generally no user action is needed.

This document is inspired by the current DBSC(E) Explainer and targets consumer users, as Enterprise scenarios are already covered.

Terminology

  • IdP: The Identity Provider that the user authenticates with. It manages user’s credentials and issues SAML assertions to relying parties.
  • RP: Acronym for Relying Party; a web application that relies on IdP's assertion to allow users to access restricted content (referred to as "SP" or "Service Provider" in SAML docs).
  • Session Provider: In the context of the underlying DBSC specification, a website or service that issues and maintains a user session. In this explainer, the Session Provider in the SSO flow is referred to as the Identity Provider (IdP).
  • TEE: Acronym for Trusted Execution Environment; a secure, isolated area within a computer's main processor (CPU) that protects sensitive data and code from other parts of the system.
  • AIK: Acronym for Attestation Identity Key.
  • Key material: Asymmetric key pair hardware-backed (either by TPMs on PCs or Secure Enclaves on Mac devices).

How is it different from DBSC(E)?

While both proposals aim to protect the IdP <> RP dance, they target different use cases and threat models:

  • DBSC for SSO targets consumer scenarios where devices are unmanaged. It focuses on securing the hand-off between an Identity Provider (IdP) and a Relying Party (RP). It prevents attackers from using stolen IdP authentication tokens to establish sessions on their own devices by ensuring the RP session key is bound to the same device as the IdP session. It relies on the IdP's existing session trust (often Trust On First Use).

  • DBSC(E) targets enterprise scenarios with managed devices. It relies on a "clean room" device registration process where the device is pre-provisioned and trusted by the organization. It introduces specific components like a "Local Key Helper" to enforce that binding keys are generated on these trusted devices, mitigating malware that might be present during the sign-in ceremony.

Attack vector in depth

Relying parties (RP) that adopt DBSC protect their users against cookie theft as they periodically ask for proof-of-possession in order to issue fresh authentication cookies. However, those who adopt Single Sign On (SSO) are still vulnerable to this kind of attack, even if both the RP and the Identity Provider (IdP) adopt DBSC. The vulnerability is particularly subtle when both RP and IDP implement DBSC because malware can silently circumvent the security measures.

In this attack, the malicious software intercepts an authentication token from the IdP and uses it to establish a new RP session.

Consequently, the session becomes tied to distinct keys, controlled by the attacker. Neither the RP nor the IdP can detect this malicious activity, as both sessions appear legitimate and are successfully bound.

This attack vector does not require any user interaction, hence can be automated in the background, and built into popular infostealers. It also does not require the infostealer to keep persistent presence on the infected user device, but only at the time of issuing new RP authentication cookies.

The following diagram shows when the malware comes into action:

sequenceDiagram
autonumber
	participant  m  as  Malware
	participant  u  as  User  Agent
	participant  r  as  Relying  Party
	participant  i  as  Identity  Provider
	
	note  over  u, i: DBSC session established between UA and IdP
	
	u->>r: Signed in content
	r->>u: Redirects to IdP's endpoint
	u->>i: Sends authentication request
		note  over  u, i: Login ceremony
	i->>u: Authentication token
	
	alt Malware comes into play
		u->>m: Intercepts authentication token from the client
		m->>r: Sends authentication token to the RP
		r->>r: Verifies the authentication token
		r->>m: RP sends protected resource
			note  over  m, r: Asynchronously initiates the DBSC session registration
	end
Loading

Malicious software intercepts IdP's authentication token and issues a new RP session bound to its own keys, bypassing IdP's binding

High-level design

The DBSC support for SSO authentication cryptographically links both IdP and RP keys, so that even if malware is able to trick the IdP to give the attacker an authentication token, this token becomes unusable if exfiltrated from the device that originally created the IdP session.

This is because the device will need to, at RP-session refresh time, prove possession of a signing key that is trusted by the IdP, which in case of an IdP's cookie exfiltration will not be possible.

The device creates a new signing key for every RP the user is signing in to, and the underlying IdP is responsible for making sure the key was generated in the same device that originally signed in to the IdP.

Key attestation gives remote parties the ability to attest that a particular object is loaded in the same TEE, hence proving that both IdPKey and RPKey come from the same device.

The following diagram shows the overall SSO authentication flow and how the key presented by the User Agent is attested to be owned by the expected device:

sequenceDiagram
	autonumber

	participant t as TEE
	participant u as User agent
	participant r as Relying Party
	participant i as Identity Provider

	note over u, i: DBSC session already initiated between the User agent and the IDP<br>UA's attestation key is trusted on first use

	u->>r: Sign in request
	r->>u: SAML/OIDC Authn request
	u->>i: Authn request (DBSC support hinting)
    
    note  over  u, i: Browser authentication<br>(cookies, proof-of-posession, etc)
	
    i->>u: 401 Secure-Session-GenerateKey=...
	u->>t: Signing key generation
	t->>u: Return
	u->>t: Key attestation
	t->>u: Attestation claim
	u->>i: (retry 3.) Authn request<br>Secure-Session-KeyBinding=binding_statement
	i->>i: Binding statement validation
	i->>u: Authn token w/ key thumbprint + <br>DBSC support hinting
	u->>r: Authn token w/ key thumbprint + <br>DBSC support hinting
	
    note  over  u, r: DBSC session initialization ceremony
Loading

Overview of the Single Sign-On authentication flow.

As stated in the diagram's first note, it's expected that a DBSC session between the device and the IdP is previously established. In the IdP's session initialization the user agent also sends an attestation key to the Identity Provider.

It is also expected that the IdP adopts a TOFU approach and trusts the attestation key as soon as it is presented at sign in time. Although this might represent an attack vector, IdPs can also improve their session initialization mechanism (e.g. by adopting security keys) so that they can make sure the attestation key comes from a trusted device. Such additional mechanisms are out of scope of this proposal.

The above high level diagram is broken down as follows:

  • Attestation key generation: During the session initialization between the device and the IdP, the device creates an AIK (Attestation Identity Key) and sends it to the IdP, which stores it for future RP signing keys attestation verification.

    Mac devices do not support such an attestation capability, so an extra key pair is generated by the browser and provided to the Identity Provider (IdP) during sign-in to serve as such. This offers minimal security benefit currently as malicious software could inject its own RP key to be signed by the Secure Enclave. However, this implementation is designed for future compatibility: it can be directly replaced with a genuine Attestation Identity Key (AIK) once Apple introduces support for such a capability.

    In addition to that, this model relies on Apple's code signing capabilities, which prevents software other than the one that created a particular Secure Enclave-protected key to access (and hence use) it.

  • Sign in request: The user navigates to an authentication-protected resource in the RP. If the user has visited that page before, generally the RP automatically redirects the user to the Identity Provider (seamless authentication), otherwise the user clicks on “Sign in With …” (interactive authentication), which generates an authentication request that is forwarded to the IdP, typically via an HTTP redirect.

  • Browser authentication: The IdP checks the browser authentication. This can be done either via authentication cookie presence or re-validating the proof-of-possession.

  • Identity Provider asks the browser to generate a new key: If the IdP detects a bound session for that browser when receiving the authentication request, it then returns an HTTP 401 with the Secure-Session-GenerateKey header. The header indicates that the browser must generate a new signing key for the underlying RP, and includes the provider_session_id identifying the IdP session whose attestation key (AIK) will be used to attest the new key.

    When the browser sees the Secure-Session-GenerateKey header, it must check if the issuing request can set a cookie bound to the IdP's session, otherwise it fails the operation.

    The new signing key must be tied to the Relying Party's origin by the browser. This serves as a protection mechanism against malicious RPs trying to guess unique hardware-backed identifiers. The user agent only shares the public key material with the RP that are supposed to have access to it.

    The key access control model is discussed in detail further in this document.

  • Signing key generation: This step relies on hardware-based keys generation, which can be either on TPMs (Windows and Linux), Secure Enclave (macOS), or even security keys. These keys are the same used in the standard DBSC implementation.

  • Browser sends the key digest to the IdP: When the browser finishes computing the binding statement, it retries the request that triggered the key generation, appending the new header Secure-Session-KeyBinding carrying the statement.

    This permits the SSO authentication process to continue normally.

  • Authentication tokens: Upon a valid binding statement presented, the IdP issues its regular authentication token (SAML assertion or OIDC Tokens) to the RP, which now includes the signing key digest (or certificate fingerprint) that should be trusted by the RP. If the authentication token is legit (signed by the IdP), the RP can trust the public key (or client certificate) that matches the specified digest.

    On OIDC flows, the key digest is included in the OIDC token, which is sent via backchannel communication between the RP and the IdP.

  • RP session initialization: RPs can issue authentication cookies bound to the key (or certificate) trusted by the IdP immediately, and then return the Secure-Session-Registration header to start the binding process on the User Agent side. The header must include the provider_key parameter to indicate the expected key digest (or certificate fingerprint).

    RPs should not issue unbound long-lived cookies, otherwise the session would not be protected.

  • Relying Party and Identity Provider DBSC support hinting: In consumer IdP scenarios, the Relying Party should hint the Identity Provider whether it supports DBSC. This is to avoid the browser being unnecessarily asked to generate signing key and attestation statement, as the RP will very likely ignore this information if it does not support it.

    As authentication requests can be spoofed, malware can fake support absence to induce the Identity Provider not to send a trusted key digest back to the RP, which would lead the RP not to start a bound session, or fallback to standard DBSC.

    To circumvent this, the IdP should also hint the RP back in its signed response (in the SAML Assertion or OIDC Token), so that if the RP supports DBSC and does not get a trusted key digest (or certificate fingerprint) in the assertion, it's a strong signal that the authentication request has been tampered with.

    In Enterprise scenarios, Identity Providers can hold additional configurations in which Admins can state that a particular Relying Party supports DBSC-SSO, or even further; that the IdP should only issue an assertion if the device succesfully generates a binding statement.

Identity Provider's session initialization

Initializing the session with an Identity Provider has some extra steps in comparison to the standard DBSC. The attestation key is additional information required to later attest that new RP signing keys reside in the same device that the IdP has a bound session.

During the IdP's DBSC session registration the new boolean parameter aik_required in the Secure-Session-Registration header indicates that the client should provide an Attestation Key in addition to the regular signing key.

The following diagram shows how a bound session is established between the User Agent and the IdP:

sequenceDiagram
	autonumber
	
	participant t as Trusted Execution Environment
	participant u as User agent
	participant i as Identity Provider
	
	note  over  u, i: Sign in ceremony
	i->>u: 200 w/ authn cookie, response includes header to start session and share attestation key.<br>Header: Secure-Session-Registration=aik_required: ?1, Secure-Session-Challenge=...

	alt  Background
		u->>t: Computes IdP signing and attestation keys,<br>and the signature of the challenge
		t->>u: Return
		u->>i: Post (IDPpk, IDPaik, attStatement, attSignature, signature)
		
		note  over  i: Validate signature and attestation<br>Store IDPpk and IDPaik
		i->>u: Bound cookies
	end
Loading

Identity Provider session binding flow.

Each step in the diagram is detailed below:

Sign in ceremony: The sign in ceremony happens in the same way as it usually does. However, upon successful login, the IdP includes the aik_required parameter in the registration header (Secure-Session-Registration). The client must provide cryptographic proof of attestation and signing keys relationship.

Key generation and attestation: This step involves a few cryptographic operations in the TEE, which are detailed as such:

  1. A new signing key pair $(IdP_\text{sk-pub}, IdP_\text{sk-priv})$ is created. $IdP_\text{sk-pub}$ is used to register the IdP DBSC session.

  2. The challenge sent by the server is signed with $IdP_\text{sk-priv}$.

  3. A new attestation key pair $(IdP_\text{ak-pub}, IdP_\text{ak-priv})$ is created and used to create an attestation claim for $IdP_\text{sk}$.

Registration statement: A new variant of the DBSC proof takes place to accommodate both subject and attestation keys. It uses nested JWT so the subject key is the payload of the encompassing attestation key, expressed in the JWS format:

// Header
{
	"alg": "ES256",
	"typ": "dbsc+aik",
	"cty": "jwt", // Indicates nested JWT
	"jwk": { // Holds the attestation public key material.
		"kty": "EC",
		"crv": "P-256",
		"x": "...",
		"y": "..."
	}
}

// Payload
{
	"aud": "https://idp.com/reg",
	"jti": "Base64URL encoded JWT representing the DBSC proof as specified at https://w3c.github.io/webappsec-dbsc/#format-jwt",
	"att": "Base64URL encoded JSON object defined below."
}

The attestation statement format is defined as follows depending on fmt. IdP session registration and RP session binding use the same attestation construction; they differ in the subject key being certified ($IdP_\text{sk-pub}$ vs. $RP_\text{sk-pub}$), in the corresponding server challenge, and in which fields are carried on the wire — the registration statement below declares alg and fmt, while the binding statement omits both because by then the IdP holds them as stored session state:

For TPM:

{
	"fmt": "TPM",
	"alg": "ES256|RS256",
	"stmt": "Base64URL encoded TPMS_ATTEST structure",
	"sig": "Base64URL encoded TPMT_SIGNATURE structure",
	"sub_key": "Base64URL encoded TPMT_PUBLIC structure"
}

For SECURE_ENCLAVE:

{
	"fmt": "SECURE_ENCLAVE",
	"alg": "ES256",
	"stmt": "Base64URL encoded raw_stmt",
	"sig": "Base64URL encoded signature of raw_stmt",
	"sub_key": "Base64URL encoded SubjectPublicKeyInfo structure"
}
  • fmt: The format of the presented attestation statement. It must be either TPM or SECURE_ENCLAVE. The IdP MUST store this value with the attestation key; it is the value the binding statement validation branches on.
  • alg: The signature algorithm of the attestation signing key ($IdP_\text{ak}$), e.g., ES256 or RS256 — not a digest algorithm. The hash algorithm used for hashing the challenge and computing the subject key digest is written hash_alg(alg) and is derived from it (e.g. SHA-256 for ES256/RS256, SHA-384 for ES384). The IdP MUST store alg with the attestation key; the binding statement does not repeat it.
  • stmt: The attestation statement payload encoded in Base64URL:
    • If fmt is TPM, it is the TPMS_ATTEST object certifying sub_key, generated with qualifyingData set to the hashed challenge hash(challenge, hash_alg(alg)). Hashing the challenge ensures a fixed payload size across the hardware/enclave boundary.
    • If fmt is SECURE_ENCLAVE, it is base64url_enc(raw_stmt) where raw_stmt is the concatenation of the hashed challenge and the digest of the subject key as carried in sub_key:
// pseudo-code
c := hash(challenge, hash_alg(alg)) // hashed challenge
d := hash(sub_key_bytes, hash_alg(alg)) // digest over the DER SubjectPublicKeyInfo
raw_stmt := concat(c, d)
stmt := base64url_enc(raw_stmt)
  • sig: The attestation statement signature encoded in Base64URL. If fmt is set to TPM, the format of the signature is the TPMT_SIGNATURE object. If fmt is set to SECURE_ENCLAVE, then it is the ECDSA signature over raw_stmt signed using $IdP_\text{ak-priv}$, formatted as a raw IEEE P1363 signature ($r \parallel s$, fixed 64 bytes for P-256 / ES256 per RFC 7518 Section 3.4), encoded in Base64URL.

    Platform Implementation Note (Apple platforms): On Apple platforms, SecKeyCreateSignature produces an ASN.1 DER-encoded ECDSA signature (kSecKeyAlgorithmECDSASignatureMessageX962SHA256). Implementations (such as Chromium / User Agents) MUST convert this ASN.1 DER signature to the raw IEEE P1363 format ($r \parallel s$, fixed 64 bytes for P-256) before Base64URL encoding into sig.

  • sub_key: The Base64URL-encoded public key of the subject key being certified ($IdP_\text{sk-pub}$). The encoding is selected by fmt:

    The field is required for both formats. stmt only ever carries a digest of the subject key, so without sub_key the IdP cannot recover the key material itself, and it cannot re-derive that digest under any other encoding.

Server-side validation: The server validates the registration statement and securely stores both the signing and attestation keys $(IdP_\text{pk}, IdP_\text{pak})$ public material. If valid, the server issues fresh (and bound) authentication cookies.

Attestation key bypass: Key attestation is used to generate per-RP keys and avoid re-using the same signing key as the IdP. This is mostly designed to prevent user tracking. However, under enterprise contexts, sharing the IdP key with multiple RPs might not pose a privacy concern, and should be allowed under certain circumstances (e.g. Enterprise browser policies) to increase adoption.

Relying Party's session initialization

When starting an SSO flow, the RP should signal its DBSC support to the IdP. This prevents the IdP from unnecessarily requesting key generation if the RP doesn't support DBSC.

Since authentication requests can be tampered with (e.g., to hide DBSC support in a downgrade attack attempt), the RP should verify that the IdP's signed response confirms both its own DBSC support and the parameters received in the initial request. This allows the RP to detect if the request was modified to prevent a bound session from being established.

In Enterprise scenarios this hinting dance can be skipped entirely as Identity Providers (in general) hold additional per-RP configurations.

Upon authentication request, the IdP instructs the User Agent to create a new key pair through an HTTP header (Secure-Session-GenerateKey). Once the new signing key is generated, the browser builds the binding statement and sends it to the IdP. Upon the binding statement and session validation, the IdP sends an authentication code back to the RP containing the signing key's digest that should be trusted.

The binding statement uses the same attestation construction as the registration statement described above, with the per-RP key ($RP_\text{sk-pub}$) as the certified subject key and the binding challenge from Secure-Session-GenerateKey as the challenge. It differs in one respect: it does not carry alg or fmt.

Both values were established, and signed, when the attestation key was registered, so the IdP already holds them in the session state it must load anyway to obtain $IdP_\text{ak-pub}$. Repeating them on the binding statement would make them attacker-controlled inputs that every IdP would have to remember to pin against the stored record; a missing pin passes every test a conforming browser generates and fails only under attack. Omitting them makes the stored record the sole source, so the pin cannot be skipped.

Invariant: the verification key $IdP_\text{ak-pub}$, the algorithm, and the attestation format MUST all come from the session state stored at AIK registration. The binding statement carries only stmt, sig, and sub_key. Everywhere hash_alg(alg) appears in the binding path below, alg is the stored algorithm, never a value read from the statement.

This asymmetry with the registration statement is deliberate. At registration the two values are covered by the signature and the IdP has no prior state to compare them against; at binding time they would be unauthenticated and fully redundant. If key transport (x5c or jwk) is ever added to the binding statement, the reasoning no longer holds and both fields must move back in, inside the signed bytes.

For TPM:

{
	"stmt": "Base64URL encoded TPMS_ATTEST structure",
	"sig": "Base64URL encoded TPMT_SIGNATURE structure",
	"sub_key": "Base64URL encoded TPMT_PUBLIC structure"
}

For SECURE_ENCLAVE:

{
	"stmt": "Base64URL encoded raw_stmt",
	"sig": "Base64URL encoded signature of raw_stmt",
	"sub_key": "Base64URL encoded SubjectPublicKeyInfo structure"
}
  • stmt: The attestation statement payload encoded in Base64URL:
    • If the stored fmt is TPM, it is the TPMS_ATTEST structure certifying $RP_\text{sk}$, generated with qualifyingData set to hash(challenge, hash_alg(alg)).
    • If the stored fmt is SECURE_ENCLAVE, it is base64url_enc(raw_stmt) where raw_stmt is the concatenation of the hashed challenge and the digest of the subject key as carried in sub_key:
// pseudo-code
c := hash(challenge, hash_alg(alg)) // hashed challenge
d := hash(sub_key_bytes, hash_alg(alg)) // digest over the DER SubjectPublicKeyInfo
raw_stmt := concat(c, d)
stmt := base64url_enc(raw_stmt)
  • sig: The signature encoded in Base64URL:

    • If the stored fmt is TPM, it is the TPMT_SIGNATURE structure defined in the TPM 2.0 specs.
    • If the stored fmt is SECURE_ENCLAVE, it is the ECDSA signature over raw_stmt signed using $IdP_\text{ak-priv}$, formatted as a raw IEEE P1363 signature ($r \parallel s$, fixed 64 bytes for P-256 / ES256 per RFC 7518 Section 3.4), encoded in Base64URL. On Apple platforms, user agents MUST convert the ASN.1 DER signature from SecKeyCreateSignature to the raw IEEE P1363 format ($r \parallel s$, fixed 64 bytes for P-256) before Base64URL encoding into sig. Unlike TPMT_SIGNATURE, this encoding carries no algorithm identifier, so the stored AIK record is what tells the IdP how to decode it, not merely how to verify it.
  • sub_key: The Base64URL-encoded public key of the per-RP key ($RP_\text{sk-pub}$). The encoding is selected by the stored fmt:

    The field is required for both formats. The IdP has to forward a trusted key digest to the RP (dbsc_trusted_key_digest in OIDC, <dbsc:TrustedKey digest="..."> in SAML), and that digest is computed over the canonical JWK of $RP_\text{sk-pub}$ (RFC 7638) — a different encoding from the one hashed into raw_stmt. The IdP therefore needs the key itself, not just a digest of it.

The following diagram shows how a new DBSC session is established between the device and the RP on top of a trusted key digest:

	sequenceDiagram
		autonumber

		participant t as  TEE
		participant  u  as  User  agent
		participant  r  as  Relying  Party
		participant  i  as  Identity  Provider

		u->>r: Sign in with IdP
		r->>u: HTTP 302 w/ authn request + DBSC support hint
		u->>i: GET idp.com w/ authn request + DBSC support hint + IdP cookies
		i->>u: HTTP 401<br>Secure-Session-GenerateKey=...
		note  over  u: Generates RP key pair
		note  over  u: Computes<br>binding statement
		u->>i: (retry request on 3.) GET idp.com w/ authn request + IdP cookies<br>Secure-Session-KeyBinding=binding_statement
		note  over  i: Verifies<br>binding statement
		i->>u: HTTP 302 w/ authn token (w/ key digest + DBSC support hint)
		u->>r: GET/POST authn token (w/ key digest + DBSC support hint)
		r->>r: Stores key digest
		r->>u: 200 w/ authn cookie, response includes header to start session using a particular key<br>Secure-Session-Registration=..., Secure-Session-Challenge=...
		alt  Background
			u->>u: Loads RPpub with<br>the following digest
			u->>t: Sign challenge with RPpriv
			t->>u: Signature
			u->>r: Post (RPpub, signature)
			r->>r: Validates signature<br>and digest
			r->>u: Bound cookies
		end
		note  over  u, r: DBSC session established
Loading

DBSC session initialization with a trusted key digest

Before establishing the session, the RP should evaluate the following scenarios:

  • If the Relying Party does not support DBSC, then it proceeds as is.
  • If the Relying Party does support DBSC, then it should evaluate:
    • If the signed IdP response contains the same parameters initially sent by the RP and a trusted key digest, then it should proceed with session establishment.
    • If the signed IdP response contains the initial parameters but lacks a trusted key, it means that the IdP failed to assert any signing key. Detailed error messages may or may not be in the IdP response.
    • If the signed IdP response does not contain the initial parameters, it's a strong indicator that the authentication request has been tampered with and this can be part of a downgrade attack.

The Relying Party indicates what key should be used in the parameter provider_key set in the Secure-Session-Registration header.

The value for this parameter is the key digest sent by the IdP. The browser will send the public key material only if all the following criterias match:

  • RP's origin matches the origin indicated by the Identity Provider in the target_origin parameter of the Secure-Session-GenerateKey header.
  • The provider_key parameter matches the underlying key digest, treated as an opaque string.

Once the existing key is sent to the RP, the session registration flow happens in the same way as the standard DBSC.

Session refresh

Both IdP and RP sessions are standard DBSC sessions, and upon authentication cookie expiration, the User Agent uses their respective signing key to perform the proof-of-possession required in the refresh process. No additional steps are required.

Key access control

When the Identity Provider instructs the User Agent to generate a new signing key via the Secure-Session-GenerateKey header, the User Agent stores the generated private key and associates it with the following metadata:

  • Target Origin: The origin specified in the target_origin parameter, which is the RP origin.

Note: Because keys are scoped to origins, distinct subdomains naturally receive distinct keys. If an RP wants to keep separate keys on the same origin, it's up to them to use separate session IDs so that browsers do not overwrite keys.

This metadata enforces a strict access control policy: the User Agent must only prove possession of this specific key to the Relying Party if:

  1. The Relying Party identifies the key by its digest (via the provider_key parameter in the Secure-Session-Registration header).

  2. The Relying Party's origin matches the stored Target Origin.

By relying on the key digest as the primary identifier, the User Agent allows multiple keys to exist for the same Target Origin. This prevents key collisions in scenarios where an Identity Provider manages multiple tenants or sessions for the same Relying Party, ensuring that the correct key is always selected based on the unique fingerprint provided by the RP.

Component-level design

In this section we dive deep into each of the involved components and detail what capabilities they support to enable cross-origin DBSC.

Relying Party

Besides the standard DBSC implementation, the RP also needs to perform the following changes to support it on SSO flows:

SAML Assertion and OIDC Tokens

The Identity Provider shares the trusted public key's digest (for TPM-bound sessions) or the certificate fingerprint (for mTLS-bound sessions) and the algorithm utilized (SHA-256|384|512) with the Relying Party via either SAML Assertion or OIDC Tokens, depending on which SSO protocol the RP is using.

While DBSC primarily focuses on TPM-bound keys, it also supports certificate-based binding (e.g., via mTLS) to accommodate platforms or scenarios where hardware-backed keys might not be available or as an additional layer of security. In such cases, the IdP can share the client certificate's fingerprint instead of, or in addition to, the public key digest.

Note: Sharing certificate fingerprints can pose privacy concerns as they may serve as a persistent identifier across different Relying Parties. While this is generally acceptable in enterprise scenarios where device tracking and security auditing are required, it should be carefully considered for consumer use cases.

In case of SAML, the trusted key's digest and digest_alg (or the certificate fingerprint and its algorithm) are returned in a saml:Advice tag using either <dbsc:TrustedKey> or <dbsc:TrustedCertificate>, as in the following example:

<samlp:Response>
	<saml:Assertion>
		<saml:Advice>
			<dbsc:TrustedKey xmlns:dbsc="https://www.w3.org/NS/DBSC"
				digest="nZgxCylNy7jXvn4+j0DykE+TDK4W41LTffxei29e/G0="
				digest_alg="SHA-256"/>
			<dbsc:TrustedCertificate xmlns:dbsc="https://www.w3.org/NS/DBSC"
				fingerprint="f3e9619a9d701a52701469e4f83d32847b2374e2593f66d48b788647097c234b"
				fingerprint_alg="SHA-256"/>
		</saml:Advice>
	</saml:Assertion>
</samlp:Response>

The XML Schema Definition for these elements is defined as follows:

<?xml version="1.0" encoding="UTF-8"?>
	<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://www.w3.org/NS/DBSC" xmlns:dbsc="https://www.w3.org/NS/DBSC" elementFormDefault="qualified">
		<xs:simpleType name="DigestAlgorithmType">
			<xs:restriction base="xs:string">
				<xs:enumeration value="SHA-256"/>
				<xs:enumeration value="SHA-384"/>
				<xs:enumeration value="SHA-512"/>
			</xs:restriction>
		</xs:simpleType>
		<xs:simpleType name="DigestType">
			<xs:union memberTypes="xs:base64Binary xs:string"/>
		</xs:simpleType>
		<xs:simpleType name="FingerprintType">
			<xs:union memberTypes="xs:hexBinary xs:string"/>
		</xs:simpleType>
	<xs:element name="TrustedKey">
		<xs:complexType>
			<xs:attribute name="digest" type="dbsc:DigestType" use="required">
			<!-- documentation omitted for brevity -->
			</xs:attribute>
			<xs:attribute name="digest_alg" type="dbsc:DigestAlgorithmType" use="required">
			<!-- documentation omitted for brevity -->
			</xs:attribute>
		</xs:complexType>
	</xs:element>
	<xs:element name="TrustedCertificate">
		<xs:complexType>
			<xs:attribute name="fingerprint" type="dbsc:FingerprintType" use="required">
			<!-- documentation omitted for brevity -->
			</xs:attribute>
			<xs:attribute name="fingerprint_alg" type="dbsc:DigestAlgorithmType" use="required">
			<!-- documentation omitted for brevity -->
			</xs:attribute>
		</xs:complexType>
	</xs:element>
</xs:schema>

Corresponding custom claims are present in the OIDC Token, as follows:

{
	"iss": "http://idp.com",
	...
	"dbsc_trusted_key_digest": "nZgxCylNy7jXvn4+j0DykE+TDK4W41LTffxei29e/G0=",
	"dbsc_trusted_key_digest_alg": "SHA-256|384|512",
	"dbsc_trusted_cert_fingerprint": "f3e9619a9d701a52701469e4f83d32847b2374e2593f66d48b788647097c234b",
	"dbsc_trusted_cert_fingerprint_alg": "SHA-256|384|512"
}

In OIDC implementations, the Discovery Document may be updated to indicate that dbsc_trusted_key_digest and dbsc_trusted_key_digest_alg (as well as dbsc_trusted_cert_fingerprint and dbsc_trusted_cert_fingerprint_alg for certificate-bound sessions) are supported claims.

Key storage

As the DBSC session registration is done asynchronously, the RP needs to keep track of the trusted digest sent in the authentication token, so that when the User Agent presents its key, the RP is able to compare its digest with the trusted one.

DBSC session registration

In the RP's DBSC session registration, the parameter provider_key must be sent in the Secure-Session-Registration header, which tells the browser what key the RP expects. To avoid any guessing capabilities for malicious RPs, the User Agent only sends the key if it was assigned to the RP's origin during its creation.

When the User Agent responds with the DBSC registration proof (in the Secure-Session-Response header as a JWS):

  1. The RP extracts the public key (jwk) from the JWS header.
  2. The RP computes the RFC 7638 JWK Thumbprint of the extracted jwk using the algorithm specified by the IdP (dbsc_trusted_key_digest_alg in OIDC or digest_alg in SAML).
  3. The RP verifies that this computed JWK Thumbprint matches the trusted key digest received in the IdP's assertion or token (dbsc_trusted_key_digest in OIDC, or the digest attribute of <dbsc:TrustedKey> in SAML).
  4. The RP verifies the JWS signature over the registration challenge to prove possession of the private key.

If both the JWK thumbprint verification and the signature verification succeed (or if certificate fingerprint verification succeeds for certificate-bound sessions), the RP establishes the bound session with the User Agent.

Identity Provider

As shown in the first diagram of this document, it is expected that the IdP supports DBSC. So starting from this premise, the Identity Provider should implement the following:

DBSC registration header

When the user is signing in to the IdP, besides the standard DBSC headers, the Secure-Session-Registration header includes the new aik_required parameter.

This is a boolean parameter indicating whether the User Agent should create an attestation identity key.

Registration statement validation

The registration statement is the payload sent to the IdP when the user is signing in to it. It is composed of the following:

  • IdP public signing key material - The $IdP_\text{sk-pub}$ is used to validate the challenge signature. This proves possession of the private counterpart ($IdP_\text{sk-priv}$) of the signing key.

  • IdP attestation key - The $IdP_\text{ak-pub}$ is used to verify the attestation claim.

  • Attestation claim - The attestation claim cryptographically proves the relationship between $IdP_\text{sk-priv}$ and $IdP_\text{ak-priv}$. In other words, it proves they are both stored in the same TEE if the platform allows it. If the platform does not have such a capability (e.g. macOS), this key just proves that both come from the same client, but they might not be stored in the same secure hardware.

  • Challenge signature - This signature is used to prove $IdP_\text{sk-priv}$ possession, as previously mentioned.

To validate the registration statement, the IdP must do the following:

  1. Verify the challenge signature using $IdP_\text{sk-pub}$.
  2. Verify that alg matches the expected algorithm of $IdP_\text{ak-pub}$.
  3. Verify the attestation statement according to fmt:
    • SECURE_ENCLAVE:
      1. Decode stmt from Base64URL to obtain raw_stmt.
      2. Verify that the first half of raw_stmt matches hash(challenge, hash_alg(alg)).
      3. Decode sub_key from Base64URL to obtain the DER SubjectPublicKeyInfo of the certified key, and verify that the second half of raw_stmt matches hash(sub_key_bytes, hash_alg(alg)).
      4. Verify that $IdP_\text{sk-pub}$ matches the public key parsed from sub_key.
      5. Decode sig from Base64URL and verify that the decoded signature is exactly 64 bytes in length (raw IEEE P1363 format $r \parallel s$, with 32 bytes for $r$ and 32 bytes for $s$).
      6. Verify the signature over raw_stmt using $IdP_\text{ak-pub}$ (converting the 64-byte raw IEEE P1363 $r \parallel s$ signature to ASN.1 DER format if required by the server's cryptographic library).
    • TPM:
      1. Decode stmt as TPMS_ATTEST and sig as TPMT_SIGNATURE.
      2. Verify that stmt.extraData matches hash(challenge, hash_alg(alg)).
      3. Decode sub_key as TPMT_PUBLIC and verify that stmt.certifyInfo.name matches nameAlg || hash(sub_key).
      4. Verify that $IdP_\text{sk-pub}$ matches the public key parameters extracted from sub_key.
      5. Verify sig over stmt using $IdP_\text{ak-pub}$ per TPM 2.0 specs.

Public and attestation keys storage

As IdPs are expected to already support DBSC, most likely they already handle public key storage. In addition to the public keys, attestation keys should also be stored in association to the user session.

User consent for key sharing

As stated in the high-level design section, the per-RP key is 1P data from the RP's perspective. As the IdP is a third-party, the User Agent only shares the RP's key digest with the IdP under explicit user consent via Storage Access API.

DBSC Key generation header

The Secure-Session-GenerateKey is a new HTTP header that instructs the User Agent how to generate a key for a given Relying Party. It is a Structured Field whose value is an Inner List of Tokens representing the acceptable cryptographic algorithms for the new key (e.g., (ES256 RS256)). This header contains the following parameters:

  • A string parameter called target_origin, which is the serialized secure origin of the RP performing the sign in operation (e.g., https://relyingparty.com). The User Agent must limit this key usage to the origin indicated by this parameter.

  • A string parameter challenge, which is a replay-resistant challenge used to prove the private key possession.

  • A string parameter provider_session_id, which identifies the Identity Provider's bound session. The User Agent uses this identifier to look up the corresponding Attestation Identity Key (AIK) to attest the newly generated RP key. If no active session or AIK matches this identifier, the User Agent ignores the Secure-Session-GenerateKey header and does not reply to the Identity Provider.

  • An sf-parameter whose key is provider_session_id, and whose value is an sf-string, conveying which of the Identity Provider's sessions the attestation key used to certify this key is keyed by. The User Agent needs it because the attestation key is keyed by the (IdP’s domain, session ID) pair: with two concurrent sessions at the same IdP, the domain alone does not identify a single key.

Example:

Secure-Session-GenerateKey: (ES256 RS256); target_origin="https://relyingparty.com"; challenge="..."; provider_session_id="..."

Binding statement validation

The binding statement validation is done as follows:

  1. Verify that the provided challenge is not expired and has not been reutilized.
  2. Load the attestation key record stored for the user session: $IdP_\text{ak-pub}$, its algorithm alg, and its attestation format fmt. The binding statement does not carry alg or fmt; an implementation that reads either from the statement reintroduces a format-confusion attack, because a genuine TPM attestation key can sign the arbitrary byte string a SECURE_ENCLAVE statement consists of.
  3. Verify the attestation statement based on the stored fmt:
    • SECURE_ENCLAVE:

      1. Decode stmt from Base64URL to obtain raw_stmt.
      2. Verify that the first half of raw_stmt matches hash(challenge, hash_alg(alg)).
      3. Decode sig from Base64URL and verify that the decoded signature is exactly 64 bytes in length (raw IEEE P1363 format $r \parallel s$, fixed 64 bytes for P-256 / ES256).
      4. Verify the signature over raw_stmt using the stored $IdP_\text{ak-pub}$ associated with the user session (converting the IEEE P1363 signature to ASN.1 DER format if required by the cryptographic verification library).
      5. Decode sub_key from Base64URL to obtain the DER SubjectPublicKeyInfo of $RP_\text{sk-pub}$, and verify that the second half of raw_stmt matches hash(sub_key_bytes, hash_alg(alg)). This is what ties the signed statement to the key the IdP is about to vouch for.
      6. Parse the public key from sub_key, construct its canonical JWK representation per RFC 7638, compute its digest using hash_alg(alg), and Base64URL-encode the result to produce the RFC 7638 JWK Thumbprint to include in the authentication token forwarded to the RP (as dbsc_trusted_key_digest in OIDC tokens or in <dbsc:TrustedKey digest="..."> in SAML assertions).

      Note: the digest inside raw_stmt is not a JWK Thumbprint and MUST NOT be forwarded as one. It is taken over the DER SubjectPublicKeyInfo, whereas the RFC 7638 thumbprint is taken over the canonical JWK; the two produce different byte strings for the same key. The RP recomputes the thumbprint from the JWK it registers with DBSC, so an IdP that re-encodes the raw_stmt digest emits a value the RP can never match.

    • TPM:

      1. Decode stmt as TPMS_ATTEST and sig as TPMT_SIGNATURE.
      2. Verify that stmt.extraData matches hash(challenge, hash_alg(alg)).
      3. Decode sub_key as TPMT_PUBLIC and verify that stmt.certifyInfo.name matches nameAlg || hash(sub_key).
      4. Verify sig over stmt using the stored $IdP_\text{ak-pub}$ per TPM 2.0 specs.
      5. Extract the public key parameters from sub_key (TPMT_PUBLIC), construct its canonical JWK representation per RFC 7638, compute its digest using hash_alg(alg), and Base64URL-encode the result to produce the RFC 7638 JWK Thumbprint to include in the authentication token forwarded to the RP (as dbsc_trusted_key_digest in OIDC tokens or in <dbsc:TrustedKey digest="..."> in SAML assertions).

SAML Assertions and OIDC tokens

The SAML assertion or OIDC token returned to the User Agent after a successful login must contain both the trusted key digest (or certificate fingerprint) and the algorithm used (SHA-256, 384, or 512) in the fields indicated in this section.

User agent

There are a few capabilities that the browser must provide in order to support DBSC in SSO authentication flows, which are described as follows.

Identity Provider registration statement

When signing in to an IdP, the User Agent must provide not only the session key and the challenge signature, but also an attestation key that will be later used to verify per-RP keys, along with its attestation statement and signature. The attestation key, statement, and signature are generated whenever the parameter aik_required is set in the Secure-Session-Registration header.

The registration statement is built as follows:

  1. Browser computes a new session key ($IdP_\text{sk-pub}, IdP_\text{sk-priv}$) for the IdP.
  2. Browser signs the challenge sent in the Secure-Session-Registration header using $IdP_\text{sk-priv}$.
  3. Browser computes a new attestation key ($IdP_\text{ak-pub}, IdP_\text{ak-priv}$) for the IdP.
    • The attestation key should be keyed by the (IdP’s domain, session ID) pair, where the IdP's domain is the registrable domain as stated in the DBSC session store specification.
  4. Browser computes the attestation claim (stmt):
    • For TPM: Browser encodes $IdP_\text{sk-pub}$ as TPMT_PUBLIC, computes qualifyingData = hash(challenge, hash_alg(alg)), and invokes TPM2_Certify to produce TPMS_ATTEST.
    • For SECURE_ENCLAVE: Browser encodes $IdP_\text{sk-pub}$ as a DER SubjectPublicKeyInfo, computes raw_stmt = concat(hash(challenge, hash_alg(alg)), hash(sub_key_bytes, hash_alg(alg))) and Base64URL-encodes it into stmt.
  5. Browser computes the attestation signature (sig) using $IdP_\text{ak-priv}$. If fmt is SECURE_ENCLAVE, the signature MUST be formatted as a raw IEEE P1363 ECDSA signature ($r \parallel s$, fixed 64 bytes for P-256 / ES256 per RFC 7518 Section 3.4), converting from ASN.1 DER if generated via Apple's SecKeyCreateSignature.
  6. Browser includes the certified public key as sub_key, Base64URL-encoded: TPMT_PUBLIC for TPM, DER SubjectPublicKeyInfo for SECURE_ENCLAVE.

The response is then encoded in the format of a DBSC proof and sent to the server.

Delegated key generation

The User Agent creates a new key pair when instructed by the Identity Provider via the Secure-Session-GenerateKey header.

The User Agent only creates such a key if and only if the user has granted 3PC (via Storage Access API) for the target origin sent in the Secure-Session-GenerateKey header, and a valid Identity Provider session matching provider_session_id (with its associated AIK) is found. Otherwise the User Agent ignores the Secure-Session-GenerateKey header and does not reply to the Identity Provider.

As the key needs to be generated while the user is signing in to the Relying Party, this operation must be done synchronously. However, as TEE key generation is generally slow (might take up to 1s to finish), this can lead to bad user experience due to considerable latency added to the sign in flow.

To overcome this issue, browsers are expected to pre-generate keys as soon as the IdP's session is established. The User Agent triggers the key creation whenever it sees the aik_required parameter in the Secure-Session-Registration header. This is a strong signal that per-RP keys are to be asked by the IdP.

Identity Provider binding statement

When signing in to Relying Parties, the Identity Provider needs to assert that the RP key comes from the same device as originally signed in to the IdP.

This is done as follows:

  1. Browser verifies that IdP has 3PC access (meaning, cookies from IdP work in a context that is 3P to the IdP), otherwise it fails the operation.
  2. Browser computes the RP session key $RP_\text{sk}$ when the IdP instructs it to.
  3. Browser retrieves the attestation key keyed by (IdP domain, session ID) matching the provider_session_id parameter from the Secure-Session-GenerateKey header, where the IdP domain is the registrable domain of the issuing request's host. The browser then verifies that the issuing request's URL is in scope of that session, so an origin-scoped IdP session (include_site: false) can only be used by the origin it was registered for. If no session or attestation key is found matching provider_session_id, or the issuing request is not in scope of that session, the browser ignores the Secure-Session-GenerateKey header and does not reply to the Identity Provider. The attestation key's format fmt and algorithm alg are properties of that key; the browser uses them to build the statement but does not put them on the wire.
  4. Browser computes the attestation statement (stmt):
    • For TPM: Browser encodes $RP_\text{sk-pub}$ as TPMT_PUBLIC, computes qualifyingData = hash(challenge, hash_alg(alg)), and invokes TPM2_Certify to produce TPMS_ATTEST.
    • For SECURE_ENCLAVE: Browser encodes $RP_\text{sk-pub}$ as a DER SubjectPublicKeyInfo, computes raw_stmt = concat(hash(challenge, hash_alg(alg)), hash(sub_key_bytes, hash_alg(alg))) and Base64URL-encodes it into stmt.
  5. Browser computes the attestation claim's signature (sig) using the IdP's attestation key. If fmt is SECURE_ENCLAVE, the signature MUST be formatted as a raw IEEE P1363 ECDSA signature ($r \parallel s$, fixed 64 bytes for P-256 / ES256 per RFC 7518 Section 3.4), converting from ASN.1 DER if generated via Apple's SecKeyCreateSignature.
  6. Browser includes the certified public key as sub_key, Base64URL-encoded: TPMT_PUBLIC for TPM, DER SubjectPublicKeyInfo for SECURE_ENCLAVE. Without it the IdP cannot derive the trusted key digest it has to forward to the RP.
  7. Browser signs the challenge sent by the IdP using $RP_\text{sk-priv}$.

The response is also encoded in the format of a DBSC proof and sent to the server.

Once this binding statement is verified, the IdP can then issue an authentication token to be redirected to the RP.

Relying Party's session initialization

The Relying Party sends the Secure-Session-Registration header as it would in a standard DBSC session. However, in SSO cases, this header holds the parameter provider_key, which tells the User Agent which key the RP expects. If the key with the specified digest matches the RP's origin assigned to that key, the User Agent uses it to establish the new session. From this point, the DBSC session initialization happens as usual.

Alternatives Considered

Relayed refreshes

Instead of creating per-RP keys and letting each relying party handle its own cryptographic operations, we considered relaying the session refreshes to the Identity Provider. However, this would impose availability requirements for IdPs, as they would need to be online in order to keep RP's users signed in.

IdP's key sharing

A “key sharing” approach was also extensively evaluated, so the IdP dependency could be avoided and RPs would be also able to perform the proof-of-possession on their own (as they do in the current design). The approach, however, imposes a major privacy concern: a device-identifiable key would be shared across multiple RPs, allowing them not only tracking user navigation, but also correlating users sharing the same device.

Trusted Platform Modules (TPM) support creating different keys for different RPs, and attesting that their private material is present in the same device. Yet, Apple's Secure Enclave doesn't provide such capabilities, which leads to the single-shared-key approach only.

Sync vs Async key generation

As the standard DBSC session registration (and hence key creation) happens asynchronously, we considered not changing this for SSO support. However, the current SSO protocols (SAML and OIDC) happen synchronously through a few HTTP redirects.

Keeping the key generation asynchronous would imply new RP <-> IDP communication (polling) to check when a new key is generated and verified by the IdP.

Privacy considerations

The DBSC specification states that the protocol's goal is not to introduce new user tracking surfaces.

The SSO implementation of the protocol follows the same principle, so users have total control over when and to whom their keys can be shared with. Such keys are resettable and do not uniquely identify the users' hardware permanently as all keys have the same lifespan as regular cookies.

About

Device Bound Session Credentials for SSO WICG repository

Resources

Code of conduct

Contributing

Stars

8 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages