[keycloak-user] Token Exchange AWS Cognito & Keycloak

Pedro Igor Silva psilva at redhat.com
Mon Apr 15 08:20:00 EDT 2019


On Mon, Apr 15, 2019 at 5:08 AM Matteo Restelli <mrestelli at cuebiq.com>
wrote:

> Hi Pedro,
> Thank you for your replies and your patience :)
> My answers inline below ;)
>
> Have a nice day,
> Matteo
>
> On Fri, Apr 12, 2019 at 4:53 PM Pedro Igor Silva <psilva at redhat.com>
> wrote:
>
>>
>>
>> On Fri, Apr 12, 2019 at 11:28 AM Matteo Restelli <mrestelli at cuebiq.com>
>> wrote:
>>
>>> Thank you Pedro,
>>> My answers (and questions) inline below ;)
>>>
>>> Thank you!
>>> Matteo
>>>
>>> On Fri, Apr 12, 2019 at 3:20 PM Pedro Igor Silva <psilva at redhat.com>
>>> wrote:
>>>
>>>> Thanks. Now it is more clear.
>>>>
>>>> Answers inline below.
>>>>
>>>>
>>>> On Fri, Apr 12, 2019 at 7:29 AM Matteo Restelli <mrestelli at cuebiq.com>
>>>> wrote:
>>>>
>>>>> Hi Pedro,
>>>>> i'll try to reply to your questions:
>>>>>
>>>>> - We've configured Cognito as an identity provider in Keycloak,
>>>>> importing the configuration via the OIDC discovery-configuration endpoint.
>>>>> At this point we needed to introduce the clientID & secret, so we've
>>>>> created a new confidential client inside AWS Cognito and used its id
>>>>> &secret in the Keycloak's Identity provider config
>>>>>
>>>>
>>>>> - We've set the permission & policy about token exchange feature to
>>>>> our Keycloak client
>>>>>
>>>>> - The SRP flow leverages the SRP authentication protocol (so
>>>>> basically, no password is sent to the server). The result of this flow is a
>>>>> couple of JWT tokens (access and id token), but the access token doesn't
>>>>> respect the OIDC rules (it doesn't contain the openid scope). This last
>>>>> point is what make the token exchange process impossible (this because,
>>>>> during the process, Cognito replies that "the token doesn't contain the
>>>>> openid scope"). About that i want to highlight the fact that these problems
>>>>> are entirely Cognito related: if we use a standard OAuth2 Flow (like
>>>>> Authorization code grant or implicit) the process works as expected.
>>>>>
>>>>
>>>> I see now. In this case, I think you should try to include somehow the
>>>> openid scope in the access token so that Cognito can process it. I guess
>>>> this error is returned when the broker is invoking the user endpoint  on
>>>> Cognito? based on the OIDC user info endpoint definition, the endpoint
>>>> should accept access tokens.
>>>>
>>>
>>> Yeah unfortunately we're stuck with this option, because Cognito is
>>> lacking support on adding this scope to the token (especially this is
>>> caused by the Amplify.js library provided by AWS, which is the one we're
>>> using to implement the SRP flow). Yes, the error is returned from Cognito
>>> when Keycloak contacts the provider to validate the token.
>>>
>>
>>>
>>>
>>>>
>>>>
>>>>> - Since the SRP flow enables us to use a self-hosted login page which
>>>>> doesn't send the password directly to the server, we've tried to find other
>>>>> solution. So we've tried to provide to the token exchange endpoint the id
>>>>> token, changing some parameters of the HTTP call. And at this point
>>>>> something unexpected for us happened: the token exchange process works also
>>>>> providing the id token. Here's the reason of my first flow of questions: is
>>>>> this behaviour expected? Is the "exchange with id token" approach a
>>>>> feasible and good one? Or is completely a bad approach?
>>>>>
>>>>> - Since using this flow (SRP) force us to provide the id token to our
>>>>> backend side, here comes the other flow of questions :). From an OIDC point
>>>>> of view, can be a right approach accessing a backend resource from a single
>>>>> page application, using an id token? I've always read that if you want to
>>>>> access to a backend resource, from a client application, is better to use
>>>>> the access token, because the id token contains a lot of user informations
>>>>> and must be used only by the client application.
>>>>>
>>>>>
>>>> It is fine to use id_token (or any other format supported by the server
>>>> that can be specified via subject_token_type) when doing the exchange.
>>>>
>>>> However, here is the interesting part. If you look our documentation we
>>>> should only support "access_token" and "jwt" as a subject_token_type. But
>>>> the implementation can also handle "id_token". The reason why "id_token"
>>>> works is that the validation of the token is done locally by Keycloak,
>>>> differently than when you are using an access_token where a request will be
>>>> sent to the user info endpoint on Cognito.
>>>>
>>>
>>> Oh! That's really interesting! :)
>>> About this point, in your opinion it will be feasible to call the token
>>> exchange endpoint every time a request comes to our backend side? Imagine
>>> this scenario:
>>>
>>> - User authenticates to Cognito via the Spa app
>>> - Spa app calls backend services (tipically contacting a gateway)
>>> - Gateway performs the token exchange on keycloak
>>> - Gateway forwards the request (adding the new access token in place of
>>> the Cognito one) to the underlying microservices...
>>>
>>> Do you see any performance issues? Does Keycloak caches something during
>>> the token exchange process?
>>>
>>
>> I would ask you to try it out and check latency and response times.
>> Unfortunately, benchmarking is something we are lacking so we depend on
>> feedback from the community.
>>
>
>> Maybe, another option you could consider is to aggregate your APIs so
>> that your SPA doesn't need to interact with multiple backend services
>> ? Where this API aggregator would be 1:1 mapped to your client and
>> responsible for all exchanges to access downstream services.
>>
>
> So the main idea is to put a gateway in front of all the microservices, so
> it will be the one who performs the token exchange process. To avoid
> performance issues we can think about a caching mechanism but in this way
> we need to investigate more (we're just evaluating the various options
> right now ;) ). By the way, IMHO, if a user calls the gateway 300 times in
> 3 minutes there's no reason to perform the token exchange process at every
> call.
>
>
>
>>
>> Or you could eventually use different scopes to gain access to these
>> different services and still use the same token obtained by the client
>> during the authentication. There is a caveat here regarding audience
>> though, so you could maybe include some audience that logically represent
>> your different APIs.
>>
>
> So, if i understood correctly:
> - Client contacts gateway with a Cognito access token
> - Gateway performs the token exchange process
> - Depending on which service needs to be called, the gateway requests
> scopes to Keycloak with the access token minted by Keycloak. Is this right?
> Or requesting scopes is done in the token exchange process?
>
> So i've some concepts regarding scopes that i read around on the Internet,
> and for what i've understood, a scope represents what an oauth2 client can
> do:
> - You need to call service A, perform a read operation
> - You request the custom scope read_service_a
>
> I'm lacking the link between those scopes and the authorization part. How
> are they linked with the authorization services in Keycloak? If a user
> isn't authorized to do something, will never receive the related scope? Or
> are they completely separated concepts?
>

There is no link between the two. I was considering a regular OAuth2
authorization where you would rely on the scopes granted by the server. I
think I missed the authorization services part in your first message :)


>
> The audience part is fine for me, also here for what i've understood, the
> audience represents the "target" resource protected by the server. Am i
> right?
>

Yes.


>
>
>>
>>>
>>>
>>>>
>>>> Regarding your last question, no it is not a good practice to use
>>>> id_token for bearer token authorization. In addition to privacy concerns
>>>> (which is not really different than when using JWTs in access tokens), ID
>>>> Token is about carrying the authentication context with specific
>>>> constraints. For instance, the audience is the client, not the backend. The
>>>> lifetime of ID Token is shorter as they are mainly important to
>>>> authenticate the user into a client, etc.
>>>>
>>>> So, you are right. You should try to use access tokens.
>>>>
>>>
>>> Ok thank you for the explanation. We'll try to use access tokens
>>> (probably we'll stop using the SRP flow in favour of an OAuth2 flow like
>>> Authorization Code Grant with PKCE (which is the one recommended for public
>>> Single page Applications)
>>>
>>
>> +1
>>
>>
>>>
>>>
>>>
>>>
>>>>
>>>>
>>>>> - Here's the curl of the token exchange process with the access token
>>>>> (i'm omitting some infos):
>>>>>
>>>>> curl -X POST \
>>>>> -d "client_id=test" \
>>>>> -d "client_secret=<client_secret>" \
>>>>> --data-urlencode
>>>>> "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
>>>>> -d "subject_issuer=<alias_of_the_identity_provider_in_keycloak>" \
>>>>> -d "subject_token=<access_token>" \
>>>>> --data-urlencode
>>>>> "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
>>>>> -d "audience=test" \
>>>>> http://localhost:8080/auth/realms/
>>>>> <realm_name>/protocol/openid-connect/token
>>>>>
>>>>>
>>>>> - Here's the curl of the token exchange process with the id token (i'm
>>>>> omitting some infos):
>>>>>
>>>>> curl -X POST \
>>>>> -d "client_id=test" \
>>>>> -d "client_secret=<client_secret>" \
>>>>> --data-urlencode
>>>>> "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
>>>>> -d "subject_issuer=<alias_of_the_identity_provider_in_keycloak>" \
>>>>> -d "subject_token=<id_token>" \
>>>>> --data-urlencode
>>>>> "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
>>>>> -d "audience=test" \
>>>>> http://localhost:8080/auth/realms/
>>>>> <realm_name>/protocol/openid-connect/token
>>>>>
>>>>> Let me know if you need more infos.
>>>>>
>>>>> Thank you again,
>>>>> Matteo Restelli
>>>>>
>>>>> On Wed, Apr 10, 2019 at 3:40 PM Pedro Igor Silva <psilva at redhat.com>
>>>>> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> So you are doing external to internal exchange. It is not clear to me
>>>>>> how you configured AWS Cognito as an identity provider and what/how the SRP
>>>>>> flow works. Could you provide more details, please? Is the token issued by
>>>>>> Cognito a JWT ?
>>>>>>
>>>>>> In addition to that, how your token exchange request looks like when
>>>>>> using both id_token and access_token as a subject_token ?
>>>>>>
>>>>>> On Wed, Apr 10, 2019 at 9:56 AM Matteo Restelli <mrestelli at cuebiq.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Any news on that?
>>>>>>>
>>>>>>> Thank you!
>>>>>>> Matteo
>>>>>>>
>>>>>>> =============================
>>>>>>>
>>>>>>>
>>>>>>> Hi all,
>>>>>>> We're using AWS Cognito as our Identity provider for our platform.
>>>>>>> We're
>>>>>>> trying to use an internal instance of Keycloak, in order to check the
>>>>>>> possibility to use KC for authorization purposes (this because
>>>>>>> Keycloak has
>>>>>>> a wonderful and powerful authorization system that fulfill our
>>>>>>> needs, and
>>>>>>> for that i want to say you "Thank you very much" :) ). For this
>>>>>>> reason we
>>>>>>> want to use the token exchange feature of Keycloak.
>>>>>>> More specifically we want to follow this flow:
>>>>>>>
>>>>>>> - User authenticates on AWS Cognito via SRP auth flow (which
>>>>>>> basically is
>>>>>>> not a standard OIDC/OAuth2 authentication flow)
>>>>>>> - User sends the access token to contact the backend service and, in
>>>>>>> the
>>>>>>> middle, this token is translated to an internal one, minted by
>>>>>>> Keycloak
>>>>>>>
>>>>>>> If we provide the AWS Cognito access token to the token exchange
>>>>>>> endpoint,
>>>>>>> with the subject_token_type parameter set to
>>>>>>> "urn:ietf:params:oauth:token-type:access_token", an error is returned
>>>>>>> stating that the access token doesn't contain the "openid" scope.
>>>>>>> Despite
>>>>>>> this we've tried another way, providing the id token to the token
>>>>>>> exchange
>>>>>>> endpoint with the subject_token_parameter set to
>>>>>>> "urn:ietf:params:oauth:token-type:id_token", and we discovered that
>>>>>>> this
>>>>>>> alternative way works. So, my questions are:
>>>>>>>
>>>>>>> - Is the "exchange with id token" approach a feasible and good one?
>>>>>>> Or is
>>>>>>> completely a bad approach?
>>>>>>> - From an OIDC point of view, can be a right approach accessing a
>>>>>>> backend
>>>>>>> resource from a single page application, using an id token? I've
>>>>>>> always
>>>>>>> read that if you want to access to a backend resource, from a client
>>>>>>> application, is better to use the access token, because the id token
>>>>>>> contains a lot of user informations and must be used only by the
>>>>>>> client
>>>>>>> application
>>>>>>>
>>>>>>> Thank you very much,
>>>>>>> Matteo
>>>>>>>
>>>>>>>
>>>>>>> PS:  As a side note, i want to clarify that if we follow an
>>>>>>> authorization
>>>>>>> code grant flow, or an implicit flow, during the authentication
>>>>>>> against AWS
>>>>>>> Cognito, the access token exchange works as expected. So this means
>>>>>>> that
>>>>>>> the problem is related to the shape of the token released by Cognito.
>>>>>>>
>>>>>>> --
>>>>>>>
>>>>>>> Like <https://www.facebook.com/cuebiq/> I Follow
>>>>>>> <https://twitter.com/Cuebiq>I Connect
>>>>>>> <https://www.linkedin.com/company/cuebiq>
>>>>>>>
>>>>>>>
>>>>>>> This email is reserved
>>>>>>> exclusively for sending and receiving messages inherent working
>>>>>>> activities,
>>>>>>> and is not intended nor authorized for personal use. Therefore, any
>>>>>>> outgoing messages or incoming response messages will be treated as
>>>>>>> company
>>>>>>> messages and will be subject to the corporate IT policy and may
>>>>>>> possibly to
>>>>>>> be read by persons other than by the subscriber of the box.
>>>>>>> Confidential
>>>>>>> information may be contained in this message. If you are not the
>>>>>>> address
>>>>>>> indicated in this message, please do not copy or deliver this
>>>>>>> message to
>>>>>>> anyone. In such case, you should notify the sender immediately and
>>>>>>> delete
>>>>>>> the original message.
>>>>>>>
>>>>>>> --
>>>>>>>
>>>>>>> Like <https://www.facebook.com/cuebiq/> I Follow
>>>>>>> <https://twitter.com/Cuebiq>I Connect
>>>>>>> <https://www.linkedin.com/company/cuebiq>
>>>>>>>
>>>>>>>
>>>>>>> This email is reserved
>>>>>>> exclusively for sending and receiving messages inherent working
>>>>>>> activities,
>>>>>>> and is not intended nor authorized for personal use. Therefore, any
>>>>>>> outgoing messages or incoming response messages will be treated as
>>>>>>> company
>>>>>>> messages and will be subject to the corporate IT policy and may
>>>>>>> possibly to
>>>>>>> be read by persons other than by the subscriber of the box.
>>>>>>> Confidential
>>>>>>> information may be contained in this message. If you are not the
>>>>>>> address
>>>>>>> indicated in this message, please do not copy or deliver this
>>>>>>> message to
>>>>>>> anyone. In such case, you should notify the sender immediately and
>>>>>>> delete
>>>>>>> the original message.
>>>>>>> _______________________________________________
>>>>>>> keycloak-user mailing list
>>>>>>> keycloak-user at lists.jboss.org
>>>>>>> https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>>>>>
>>>>>>
>>>>> Like <https://www.facebook.com/cuebiq/> I Follow
>>>>> <https://twitter.com/Cuebiq>I Connect
>>>>> <https://www.linkedin.com/company/cuebiq>
>>>>>
>>>>> This email is reserved exclusively for sending and receiving messages
>>>>> inherent working activities, and is not intended nor authorized for
>>>>> personal use. Therefore, any outgoing messages or incoming response
>>>>> messages will be treated as company messages and will be subject to the
>>>>> corporate IT policy and may possibly to be read by persons other than by
>>>>> the subscriber of the box. Confidential information may be contained in
>>>>> this message. If you are not the address indicated in this message, please
>>>>> do not copy or deliver this message to anyone. In such case, you should
>>>>> notify the sender immediately and delete the original message.
>>>>>
>>>>
>>> Like <https://www.facebook.com/cuebiq/> I Follow
>>> <https://twitter.com/Cuebiq>I Connect
>>> <https://www.linkedin.com/company/cuebiq>
>>>
>>> This email is reserved exclusively for sending and receiving messages
>>> inherent working activities, and is not intended nor authorized for
>>> personal use. Therefore, any outgoing messages or incoming response
>>> messages will be treated as company messages and will be subject to the
>>> corporate IT policy and may possibly to be read by persons other than by
>>> the subscriber of the box. Confidential information may be contained in
>>> this message. If you are not the address indicated in this message, please
>>> do not copy or deliver this message to anyone. In such case, you should
>>> notify the sender immediately and delete the original message.
>>>
>>
> Like <https://www.facebook.com/cuebiq/> I Follow
> <https://twitter.com/Cuebiq>I Connect
> <https://www.linkedin.com/company/cuebiq>
>
> This email is reserved exclusively for sending and receiving messages
> inherent working activities, and is not intended nor authorized for
> personal use. Therefore, any outgoing messages or incoming response
> messages will be treated as company messages and will be subject to the
> corporate IT policy and may possibly to be read by persons other than by
> the subscriber of the box. Confidential information may be contained in
> this message. If you are not the address indicated in this message, please
> do not copy or deliver this message to anyone. In such case, you should
> notify the sender immediately and delete the original message.
>


More information about the keycloak-user mailing list