[Openid-specs-fapi] Issue #155: Support authorization and identity federation use cases for the same client_id (openid/fapi)

Torsten Lodderstedt torsten at lodderstedt.net
Thu Aug 2 18:29:53 UTC 2018


Hi Vladimir,

thanks for sharing your insights.

> Am 02.08.2018 um 20:04 schrieb Vladimir Dzhuvinov <vladimir at connect2id.com>:
> 
> Hi Torsten,
> 
> I'm not sure about moving the state into the JWT.
> 
> The client normally starts processing the authZ response by checking the
> state. This can be a DB lookup, or some sort of validation (e.g. state
> encoded into JWT). Only then, when the state is found to be valid, makes
> sense to proceed with validating the code signature (JWT).

what if the RP determines the state value was modified when it checks c_state?

> 
> If the client works with multiple AS, the state would also include
> information about the expected issuer and JWK set to use to validate the
> code signature (JWT). The JWT "iss" also cannot be trusted to initiate
> the validation. In a situation like that, or when we have JWE on top of
> the code signature, client developers might actually find processing the
> authZ response more complicated, or less efficient (if JWK sets from
> multiple AS need to be tried).

If I got you right then there are two strategies to process the response:

Option 1
1) check state parameter value against local state (relationship to local session)
2) get expected iss and jwk set from local data associated with state
3) check JWT signature using local data (jwk set)
4) check JWT against expected iss
5) check local binding of nonce 

Option 2
1) check JWT signature based on its meta data (iss -> jwk set, JWT header)
2) get state and nonce from JWT and check local binding
3) check JWT iss against expected iss

Is that correct?

kind regards,
Torsten.
> 
> Vladimir
> 
> 
>> On 02/08/18 20:10, Torsten Lodderstedt wrote:
>> Hi Vladimir, 
>> 
>> I like your idea!
>> 
>> We can even go one step further and also add the state to the JWT, so the developers don’t even need to calculate a hash for state or code. They just check the integrity and authenticity of the JWT and are done! 
>> 
>> 
>> The JWT could look like this
>> 
>> {  
>>   "iss":"https://accounts.example.com",
>>   "aud":"s6BhdRkqt3",
>>   "jti":"2730dc2f-c9c6-4be4-97c9-6223d18f2b13",
>>   "nonce":"n-0S6_WzA2Mj",
>>   "exp":1311281970,
>>   "code":"PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA",
>>   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw“
>> }
>> 
>> and the OP/AS response could look like this
>> 
>> GET /cb?response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImp0aSI6IjI3MzBkYzJmLWM5YzYtNGJlNC05N2M5LTYyMjNkMThmMmIxMyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxOTcwLCJjb2RlIjoiUHl5RmF1eDJvN1EwWWZYQlUzMmpody41RlhTUXB2cjhha3Y5Q2VSRFNkMFFBIiwic3RhdGUiOiJTOE5KN3VxazVmWTRFak52UF9HX0Z0eUp1NnBVc3ZIOWpzWW5pOWRNQUp3In0.lBWY1Rr36IwhFnE1dQ3b79h9eNie3fqu_hV9w1JgEAejT_t1IuBXnZCcCKrZ_vnuKtL4CT36n-jhf12nIdq0pUOhEdVfgvd-SafmxY3hSWUaTiiXIcWQQeQE80WAb_pYPVVjMNS-k6KO2sixiE5UWE5flOHuBhb6Dh-ozJRjM7I
>> Host: client.example.com
>> 
>> kind regards,
>> Torsten.
>> 
>> 
>>> Am 01.08.2018 um 21:26 schrieb Vladimir Dzhuvinov via Openid-specs-fapi <openid-specs-fapi at lists.openid.net>:
>>> 
>>> Just added a comment to the ticket to consider putting the code into the
>>> JWT claims set. That way client developers will not be able to skip or
>>> ignore the JWT processing, e.g. by treating the response as a regular
>>> code response. Having the code inside the JWT will also make it possible
>>> to apply additional encryption (JWE), if there's a need to hide the code
>>> from the end-user / browser.
>>> 
>>> Vladimir
>>> 
>>> 
>>>> On 01/08/18 11:56, Torsten Lodderstedt via Openid-specs-fapi wrote:
>>>> New issue 155: Support authorization and identity federation use cases for the same client_id
>>>> https://bitbucket.org/openid/fapi/issues/155/support-authorization-and-identity
>>>> 
>>>> Torsten Lodderstedt:
>>>> 
>>>> 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 then can be used 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. 
>>>> 
>>>> {  
>>>>  "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"
>>>> }
>>>> 
>>>> 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 at 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
>>>> 
>>>> 
>>>> _______________________________________________
>>>> Openid-specs-fapi mailing list
>>>> Openid-specs-fapi at lists.openid.net
>>>> http://lists.openid.net/mailman/listinfo/openid-specs-fapi
>>> 
>>> _______________________________________________
>>> Openid-specs-fapi mailing list
>>> Openid-specs-fapi at lists.openid.net
>>> http://lists.openid.net/mailman/listinfo/openid-specs-fapi
>> 
> 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 3717 bytes
Desc: not available
URL: <http://lists.openid.net/pipermail/openid-specs-fapi/attachments/20180802/9873acc5/attachment-0001.p7s>


More information about the Openid-specs-fapi mailing list