sgo.to

OPRF OIDC

This is an early exploration of a protocol that uses OPRF to enable identity federation on the Web.

The protocol assumes:

  1. An IDP with a logged in user user_id.
  2. An RP with a origin.
  3. A user agent that is trusted by the RP and the IDP.

At the end of the protocol:

  1. The RP gets a sharded identifier sub without learning about the IDP's global user_id
  2. The IDP never learns the RP's origin in the exchange
  3. The user is able to reconstruct sub to recover their account without the original user agent.

The core idea is to run an OPRF between the user agent and the RP to construct a masking of the RP's origin to generate an aud = OPRFᴿᴾ(origin) which is guaranteed to have the following properties:

  1. the user agent knows that it is a function of the RP's origin
  2. the user agent knows that the IDP can't invert it back to the original origin

With a masked aud, a sharded identifier sub is constructed by hashing the global user_id with the aud in such a way that it can be verified by the user agent.

Sign-in

  1. The procotol starts with the IDP sending the signed metadata (e.g. name, trustworthyness) about the RP to the RP
  2. When a user initiates a sign-in flow at the RP,
  3. The RP sends to the user agent its signed metadata
  4. The user agent constructs a prompt with the metadata available and gathers the user's consent to sign-in
  5. Upon consent, the user agent starts constructing the masked aud by starting an OPRF:
    1. The user agent uses the RP's x = origin as the OPRF input
    2. The user agent selects a random number r
    3. The user agent computes a = H(x) ^ r and
    4. The user agent sends a to the RP
  6. The RP takes its pass at the OPRF:
    1. The RP selects its secret key k
    2. The RP computes b = a ^ k
    3. The RP sends b to the user agent
  7. The user agent calculates b ^ (1 / r) which is guaranteed to be H(origin) ^ k and uses it as the masked aud
  8. The user agent proceeds to send the aud (which represents the RP but isn't invertible) to the IDP
  9. The IDP constructs a sharded identifier sub by computing H(user_id, aud)
  10. The IDP signs an idtoken for sub (which is guaranteed a function of the user_id) and aud (which is guaranteed a function of the RP) and sends both the idtoken and the user_id to the user agent
  11. The user agent checks if the sub is in fact sharded by the aud by comparing it with H(user_id, aud)
  12. If it checks out, the user agent sends the idtoken to the RP
  13. The RP checks if the idtoken is addressed to it, by checking if the aud matches H(origin) ^ k
  14. If so, the RP logs the user in as sub

Recovery

The sub is a hash of of the user_id and the aud, which in turn is a function of the RP's secret key k and the RP's origin. In a new user agent, where the user is logged in as user_id, and a consistent RP you can reconstruct sub.