Support authorization and identity federation use cases for the same client_id

Issue #155 resolved
Torsten Lodderstedt created an issue

FAPI part 2 uses the scope value "openid" in conjunction with the response types "code id_token" or "code id_token token" to turn on the detached signature over the authorization response. This design forces OPs to always provide a RP/client with a sub value even if the particular use case is API access authorization (e.g. payment initiation). In such a use case, most implementations provide the client with an ephemeral sub value (reasons to be clarified, hypothesis: privacy/data minimization/liability/no identity data in PSD 2 regulatory context).

This creates the challenge that the same OP cannot provide a real, long-lasting user id to the same client simply because the same client does not have a means to indicate its desire.

The proposal is, to disentangle detached signature and identity federation by introducing a new response type to trigger the creation of a detached signature. The "openid" scope can then be used (again) as usual in OpenId Connect to request identity data.

Note: the nonce parameter should be utilized as well because it can, in combination with the detached signature, provide replay detection (no need for PKCE).

#response type signed_code#

This response type will cause the OP/AS to respond with a JWT containing the following claims:

{  
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "jti":"2730dc2f-c9c6-4be4-97c9-6223d18f2b13",
   "nonce":"n-0S6_WzA2Mj",
   "exp":1311281970,
   "c_hash":"4422E0E094F34E97C852EB5F9B2839D8120066C27EF66AA28C3DDC7CE7A79815",
   "s_hash":"44D41668D199FF3D525FA357A25525D738AADF2A7B1E2C819A39E38500ABAED9"
}

The client can use the payload to verify the integrity of the response as well as the sender and proper audience. This response type can be used with and without the scope openid.

#Example: API access authorization #

The request uses the new response type along with nonce and an API specific scope value. In this case, this is a scope value as used by the Berlin Group's OAuth mode.

GET /authorise?responseType=signed_code&
client_id=s6BhdRkqt3&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&
scope=pis:f0bbf1fd-2857-4e1b-a403-9fd1dc171183&
state= S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
nonce=n-0S6_WzA2Mj HTTP/1.1
Host: accounts.example-bank.com

The AS/OP responds with code and state and also includes a signature object,

GET /cb?code=PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA&
state=S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
signature=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImp0aSI6IjI3MzBkYzJmLWM5YzYtNGJlNC05N2M5LTYyMjNkMThmMmIxMyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxOTcwLCJjX2hhc2giOiI0NDIyRTBFMDk0RjM0RTk3Qzg1MkVCNUY5QjI4MzlEODEyMDA2NkMyN0VGNjZBQTI4QzNEREM3Q0U3QTc5ODE1Iiwic19oYXNoIjoiNDRENDE2NjhEMTk5RkYzRDUyNUZBMzU3QTI1NTI1RDczOEFBREYyQTdCMUUyQzgxOUEzOUUzODUwMEFCQUVEOSJ9.ldPh3w3QAkkbz3voa3F2pvruWQwNBv3AYV9xpcuVLZi5Da4tjep-xayjO4_flznYuu9EZbyYA1lb9uzu0rSSSiwEJ5Ms9ZEvB4l1xXNLT5TRV00erXb6Y1JsvxHjanB0I8-FUHdP-HMA0Zhg9UVohAc2qiO6wDcEfi5y_1fST4Y
Host: client.example.com

that contains the following data:

{  
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "jti":"2730dc2f-c9c6-4be4-97c9-6223d18f2b13",
   "nonce":"n-0S6_WzA2Mj",
   "exp":1311281970,
   "c_hash":"4422E0E094F34E97C852EB5F9B2839D8120066C27EF66AA28C3DDC7CE7A79815",
   "s_hash":"44D41668D199FF3D525FA357A25525D738AADF2A7B1E2C819A39E38500ABAED9"
}

The client/RP performs all the checks as defined in FAPI part 1&2 on iss, s_hash, c_hash and nonce in order to detect replay and mix up.

It then uses the code to obtain the access token.

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=authorization_code&code=PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{  
   "access_token":"SlAV32hkKG",
   "token_type":"Bearer",
   "expires_in":3600
}

Example identity federation/data

The RP uses the same response type, this time combined with the scope "openid email profile", which asks for a sub value along with the email address and profile data of the user.

GET /authorise?responseType=signed_code&
client_id=s6BhdRkqt3&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&
scope=openid%20email%20profile&
state= S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
nonce=n-0S6_WzA2Mj HTTP/1.1
Host: accounts.example-bank.com

Response from the OP and response validation works the same as in the first example.

GET /cb?code=PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA&
state=S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
signature=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImp0aSI6IjI3MzBkYzJmLWM5YzYtNGJlNC05N2M5LTYyMjNkMThmMmIxMyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxOTcwLCJjX2hhc2giOiI0NDIyRTBFMDk0RjM0RTk3Qzg1MkVCNUY5QjI4MzlEODEyMDA2NkMyN0VGNjZBQTI4QzNEREM3Q0U3QTc5ODE1Iiwic19oYXNoIjoiNDRENDE2NjhEMTk5RkYzRDUyNUZBMzU3QTI1NTI1RDczOEFBREYyQTdCMUUyQzgxOUEzOUUzODUwMEFCQUVEOSJ9.ldPh3w3QAkkbz3voa3F2pvruWQwNBv3AYV9xpcuVLZi5Da4tjep-xayjO4_flznYuu9EZbyYA1lb9uzu0rSSSiwEJ5Ms9ZEvB4l1xXNLT5TRV00erXb6Y1JsvxHjanB0I8-FUHdP-HMA0Zhg9UVohAc2qiO6wDcEfi5y_1fST4Y
Host: client.example.com

The RP exchanges the code for access and (this time) id token.

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=authorization_code&code=PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{  
   "access_token":"SlAV32hkKG",
   "token_type":"Bearer",
   "expires_in":3600,
   "id_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmV4YW1wbGUuY29tIiwic3ViIjoiMjQ0MDAzMjAiLCJhdWQiOiJzNkJoZFJrcXQzIiwibm9uY2UiOiJuLTBTNl9XekEyTWoiLCJleHAiOjEzMTEyODE5NzAsImlhdCI6MTMxMTI4MDk3MCwiYXV0aF90aW1lIjoxMzExMjgwOTY5LCJhY3IiOiJ1cm46bWFjZTppbmNvbW1vbjppYXA6c2lsdmVyIn0.culBs939W3NjZ-WhDIvmtKuia-RFK-MghASHWLst62mLFh7qoSXBlQu0INKOfCXx6Zn9ZT8dLJFb93IxUyrwQty5tNHM8L28AdXNt6WXhOdFAf3EGx-bXmXLzfSdluvPAgWMkLHTA2YhX_zI5XfKJxq439mVXpFqJnnh6TRkTjU"
}

The ID Token contains the usual data including sub

{  
   "iss":"https://accounts.example.com",
   "sub":"24400320",
   "aud":"s6BhdRkqt3",
   "nonce":"n-0S6_WzA2Mj",
   "exp":1311281970,
   "iat":1311280970,
   "auth_time":1311280969,
   "acr":"urn:mace:incommon:iap:silver"
}

Note: basically nonce could be omited as it had already been conveyed by the detached signature.

The client then uses the access token to obtain the user data:

GET /userinfo HTTP/1.1
Host: accounts.example.com
Authorization: Bearer SlAV32hkKG

HTTP/1.1 200 OK
Content-Type: application/json

{  
   "sub":"248289761001",
   "name":"Jane Doe",
   "given_name":"Jane",
   "family_name":"Doe",
   "preferred_username":"j.doe",
   "email":"janedoe@example.com",
   "picture":"http://example.com/janedoe/me.jpg"
}

#Example: combination#

Surely, both aspects could be combined.

GET /authorise?responseType=signed_code&
client_id=s6BhdRkqt3&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&
scope=openid%20email%20profile%20ais%3Af0bbf1fd-2857-4e1b-a403-9fd1dc171183&
state= S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
nonce=n-0S6_WzA2Mj HTTP/1.1
Host: accounts.example-bank.com

Comments (18)

  1. Vladimir Dzhuvinov

    Putting the code instead of its hash into the JWT claims set will have the added benefit that client developers will not be able to skip the JWT processing, i.e. treat the response as a simple code response.

    If the JWT is additionally encrypted, that will also make it possible, if needed, to keep the code confidential from the browser / end-user.

  2. Nat Sakimura

    Yes > @vdzhuvinov . At the same time, it will be further away from RFC6749. We may need to write a parallel to OAuth JAR.

  3. Dave Tonge

    I like the ideas here, but I'm still worried that we are conflating a few different things:

    1. How do OpenID Connect RPs specify that they want a persistent identifier?
    2. How do OPs specify whether a subject is an ephemeral or persistent identifier?
    3. How can we simplify the hybrid flow to detangle identity from detached signatures?

    I like the idea of a new response type and think Vlardimir's idea of only have the code inside the JWT is great. But I think this really only addresses point 3 above.

    For points 2 or 3 I suggest that a new "subject identifier type" is defined, at the moment we have pairwise and public. I suggest that ephemeral is defined. As Nat mentioned the use of OIDC doesn't guarantee that the OP will provide a persistent identifier. It would be good to discuss this on the next call.

  4. Torsten Lodderstedt reporter

    to 1: I would claim any RP using OpenID Connect expects the OP to attest persistent identifiers. That might be an hidden assumption, but a very strong one. to 2: What are use cases for ephemeral identifiers?

  5. Nat Sakimura

    Typical use cases for ephemeral identifiers are attribute-based authentication, e.g., age-verification. RP just need to know that the user is over 21, etc.

  6. Torsten Lodderstedt reporter

    Here is the consolidated proposal for the signed response

    GET /cb?state=S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw&
    signature=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImp0aSI6IjI3MzBkYzJmLWM5YzYtNGJlNC05N2M5LTYyMjNkMThmMmIxMyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxOTcwLCJjX2hhc2giOiI0NDIyRTBFMDk0RjM0RTk3Qzg1MkVCNUY5QjI4MzlEODEyMDA2NkMyN0VGNjZBQTI4QzNEREM3Q0U3QTc5ODE1Iiwic19oYXNoIjoiNDRENDE2NjhEMTk5RkYzRDUyNUZBMzU3QTI1NTI1RDczOEFBREYyQTdCMUUyQzgxOUEzOUUzODUwMEFCQUVEOSJ9.ldPh3w3QAkkbz3voa3F2pvruWQwNBv3AYV9xpcuVLZi5Da4tjep-xayjO4_flznYuu9EZbyYA1lb9uzu0rSSSiwEJ5Ms9ZEvB4l1xXNLT5TRV00erXb6Y1JsvxHjanB0I8-FUHdP-HMA0Zhg9UVohAc2qiO6wDcEfi5y_1fST4Y
    Host: client.example.com
    
    {  
       "iss":"https://accounts.example.com",
       "aud":"s6BhdRkqt3",
       "jti":"2730dc2f-c9c6-4be4-97c9-6223d18f2b13",
       "exp":1311281970,
      "code":"PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA",
       "s_hash":"44D41668D199FF3D525FA357A25525D738AADF2A7B1E2C819A39E38500ABAED9"
    }
    
  7. Nat Sakimura
    • changed status to open

    Semantically, what you are asking is a new response_type. I am not sure if response mode is the right approach.

  8. Torsten Lodderstedt reporter

    the way the mode is defined, It could be combined with grant type code or token

  9. Dave Tonge

    I like this a lot. But it would be good to discuss on the call the implications for vendors / implementers.

  10. Log in to comment