Enhancing User Privacy with OpenID Connect Pairwise Identifiers

Written by Simon Moffat

This is a quick post to describe how to set up Pairwise subject hashing, when issuing OpenID Connect id_tokens that require the users sub= claim to be pseudonymous. The main use case for this approach, is to prevent clients or resource servers, from being able to track user activity and correlate the same subject’s activity across different applications.

OpenID Connect basically provides two subject identifier types: public or pairwise. With public, the sub= claim is simply the user id or equivalent for the user. This creates a flow something like the below:

image

This is just a typical authorization_code flow – end result is the id_token payload. The sub= claim is simply clear and readable. This allows the possibility of correlating all of sub=jdoe activity.

So, what if you want a bit more privacy within your ecosystem? Well here comes the Pairwise Subject Identifier type. This allows each client to be basically issued with a non-reversible hash of the sub= claim, preventing correlation.

To configure in ForgeRock Access Management, alter the OIDC provider settings. On the advanced tab, simply add pairwise as a subject type.

image

Next alter the salt for the hash, also on the provider settings advanced tab.
image

Each client profile, then needs either a request_uri setting or a sector_identifier_uri. Basically akin to the redirect_uri whitelist. This is just a mechanism to identify client requests. On the client profile, add in the necessary sector identifier and change the subject identifier to be “pairwise”. This is on the client profile Advanced tab.

image

Once done, just slightly alter the incoming authorization_code generation request to looking something like this:

/openam/oauth2/authorize?response_type=code
&save_consent=0
&decision=Allow
&scope=openid
&client_id=OIDCClient
&redirect_uri=http://app.example.com:8080
&sector_identifier_uri=http://app.example.com:8080

Note the addition of the sector_identifier_uri parameter. Once you’ve exchanged the authorization_code for an access_token, take a peak inside the associated id_token. This now contains an opaque sub= claim:

{
  "at_hash": "numADlVL3JIuH2Za4X-G6Q",
  "sub": "lj9/l6hzaqtrO2BwjYvu3NLXKHq46SdorqSgHAUaVws=",
  "auditTrackingId": "f8ca531a-61dd-4372-aece-96d0cea21c21-152094",
  "iss": "http://openam.example.com:8080/openam/oauth2",
  "tokenName": "id_token",
  "aud": "OIDCClient",
  "c_hash": "Pr1RhcSUUDTZUGdOTLsTUQ",
  "org.forgerock.openidconnect.ops": "SJNTKWStNsCH4Zci8nW-CHk69ro",
  "azp": "OIDCClient",
  "auth_time": 1517485644000,
  "realm": "/",
  "exp": 1517489256,
  "tokenType": "JWTToken",
  "iat": 1517485656

}

The overall flow would now look something like this:

image
OIDC flow with Pairwise