[keycloak-dev] Better support for "scope" in adapters

Pedro Igor Silva psilva at redhat.com
Thu Jan 31 10:44:34 EST 2019


+1

On Thu, Jan 31, 2019 at 11:14 AM Marek Posolda <mposolda at redhat.com> wrote:

> On 31/01/2019 14:07, Pedro Igor Silva wrote:
>
> This what I like most about client scopes (in addition to all mapping you
> do to claims in the tokens) :) Would also make sense to do the same thing
> to client scopes ? So clients requesting "foo" would also get "bar" and
> "xpto", for instance ?
>
> Maybe this could avoid the client to request 10 scopes but just a more
> coarse-grained scope representing all of them.
>
> There is opened JIRA for client scopes inheritance
> https://issues.jboss.org/browse/KEYCLOAK-6633 . I believe this will cover
> what you have in mind? It's just not yet done...
>
> Marek
>
>
>
> On Thu, Jan 31, 2019 at 10:43 AM Marek Posolda <mposolda at redhat.com>
> wrote:
>
>> On 30/01/2019 14:40, Pedro Igor Silva wrote:
>>
>>
>>
>> On Wed, Jan 30, 2019 at 5:25 AM Marek Posolda <mposolda at redhat.com>
>> wrote:
>>
>>> On 29/01/2019 19:49, Pedro Igor Silva wrote:
>>>
>>> I'm not sure if we need to consider that in our adapters.
>>>
>>> Usually, the front-end knows the set of scopes that it needs to interact
>>> with the backend and stay functional.
>>>
>>> Maybe. I am personally not sure how people expect to use "scope"
>>> parameters in their frontend applications. Maybe 90% of frontend clients
>>> don't need to use "scope" parameter at all. And from the remaining, they
>>> will be fine with the current support of the "scope" parameter.
>>>
>> I would say so, mainly because I think people are still using RBAC to
>> enforce access to APIs. Enterprise scenarios don't really use scopes as
>> they are more related with delegation.
>>
>> Yeah, maybe. Just a note that our client scopes support also allows to
>> limit roles in the token. For example you can define role scope mappings of
>> client scope "service1" to have role "service1.my-service1-role" . So by
>> requesting "scope=service1", you will also receive this role in the token
>> and hence can be used for RBAC based authorization.
>>
>> But anyway, I probably won't create any JIRAs for now. Will wait if there
>> is some more feedback or some more requests for better support of "scope"
>> parameter in the adapters.
>>
>> Thanks for the feedback Pedro!
>>
>> Marek
>>
>> One possibility, where I can see usage of this, is when frontend client
>>> wants to invoke multiple different services and he wants to use different
>>> access tokens with properly "isolated" audiences. So for example you want
>>> to have:
>>>
>>> - access token with "scope=service1", which will have in itself audience
>>> claim "aud: service1" and you will use it to invoke backend service1
>>> - access token with "scope=service2", which will have in itself audience
>>> claim "aud: service2" and you will use it to invoke backend service2
>>>
>>> In this case, having the possibility for adapters to "cache" multiple
>>> tokens for various scopes can be beneficial IMO, so client can easily
>>> retrieve proper access token based on the service he wants to invoke.
>>>
>>> And the backend by itself is free to exchange tokens to call other
>>> services (service chaining).
>>>
>>> Don't think that brings a lot of complexity to the front-end and,
>> probably, indicates a bad design?
>>
>>> IMO in many cases, you're right. For example when frontend client uses
>>> access token to invoke backend "service1", this backend may want to
>>> retrieve some other data from "service11". So service1 backend needs to
>>> reuse the token or he wants to exchange this token.
>>>
>>> But in many cases, you want to avoid this. So in my example above, when
>>> you have access token with "aud: service1", you want this access token to
>>> be used solely to invoke service1. You don't want to have one huge access
>>> token, which will have all the audiences like:
>>>
>>> aud: [ "service1", "service2" ]
>>>
>> The access token is also tied with the client, what means "this client is
>> allowed to invoke service1 and service2". I usually don't see a problem on
>> that if you consider that multiple audiences mean that a high degree of
>> trust between the parties involved. What I think is true for most
>> enterprise use cases where the front-end is basically accessing internal
>> services.
>>
>> It is also worthy to consider, IMO, that the fact that you have distinct
>> services, does not mean they are not part of the same API, thus the same
>> audience.
>>
>>> You may want separate access tokens with isolated audiences exactly
>>> because you don't want service1 and service2 to be able to invoke each
>>> other. IMO this isolation is one of the main usages of the "aud" claim in
>>> the tokens.
>>>
>>>
>>> One thing that makes sense though is "incremental authorization" and
>>> allow apps to request additional scopes during an authentication session,
>>> so the app gets what was previously granted and the new scopes (depending
>>> on user consent). But I think we support that already, right ?
>>>
>>> We don't support it directly and maybe this is something to improve. ATM
>>> you will need programatically do something like this:
>>>
>>> String existingScope = existingAccessToken.getScope();
>>>
>>> // I want to use existingScope and add "phone" scope to it
>>> String newScope = existingScope + " phone";
>>>
>>> // Request the login for new scope now
>>>
>>>
>>> The part of "requesting login for new scope" is possible with javascript
>>> adapter, but not easily with the "java" adapter. With java adapter, there
>>> is no easy way to manually "build" URL to sent to OIDC authentication
>>> endpoint (equivalent of keycloak.js method "createLoginUrl"). That's also
>>> one of the things, which I proposed to improve in this email thread.
>>>
>> Marek
>>>
>>>
>>> Regards.
>>> Pedro Igor
>>>
>>> On Tue, Jan 29, 2019 at 9:36 AM Marek Posolda <mposolda at redhat.com>
>>> wrote:
>>>
>>>> During my work on Client Scopes last year, I did not any work on the
>>>> adapters side. I think there is a space for improvement here. I will
>>>> try
>>>> to summary current issues and some initial proposals for improve
>>>> things.
>>>> Suggestions welcomed! And sorry for longer email :)
>>>>
>>>>
>>>> Both javascript adapter and servlet adapter has some way for requesting
>>>> the additional "scope" and ensure that that initial OIDC authentication
>>>> request sent to Keycloak will contain some custom "scope" parameter.
>>>> The
>>>> javascript adapter has support for "scope" as an option of the "login"
>>>> method [1]. The servlet adapter has a possibility to inject custom
>>>> "scope" with parameters forwarding [2]. I am not sure about node.js and
>>>> other adapters TBH. But even for javascript and servlet adapter, the
>>>> current support is quite limited for few reasons. For example:
>>>>
>>>> - The approach of parameters forwarding used in servlet adapters
>>>> requires to logout before requesting the additional scope. Because
>>>> when
>>>> I am already authenticated in the application and I open secured URL
>>>> like http://localhost/app/secured?scope=some-custom-scope, the adapter
>>>> will just ignore it in case that user is already logged-in and it will
>>>> automatically forward to the application.
>>>>
>>>> - Both servlet and javascript adapters support to have just single
>>>> "triplet" of tokens per browser session. In this context "triplet"
>>>> means
>>>> the single set of 3 tokens (ID token , Access Token , Refresh token).
>>>> So
>>>> for example when I want to request the custom scope for being able to
>>>> invoke "service1", I can use "scope=service1". However after Keycloak
>>>> redirects me back to the application, the existing triplet of tokens is
>>>> just replaced with the new one for "service1" . Then when I want to
>>>> later invoke another service like "service2", I need to request the
>>>> additional scope "scope=service2", which will replace my tokens on the
>>>> adapter's side with the "service2" tokens . But then later when I want
>>>> to switch again to "service1", I need to redirect to Keycloak again as
>>>> the current triplet of tokens for "service1" etc.
>>>>
>>>>
>>>> To improve this limitation, I think that it will be good if adapters
>>>> easily support the following:
>>>>
>>>> - Instead of having single triplet of tokens, it will be good if
>>>> adapters can contain Map of tokens. Key of the map can be "scope"
>>>> parameter. So for example, the adapter will have "default" tokens
>>>> (those, which were used for initial login), the tokens for "service1"
>>>> and the tokens for "service2" .
>>>>
>>>> - It will be nice if there is easy way to ask adapter for "service1"
>>>> scope. In case that I don't have yet this scope, adapter will redirect
>>>> me to Keycloak with "scope=service1". If I already have it, adapter
>>>> will
>>>> return me an existing token. If existing access token is expired,
>>>> adapter will refresh the access token for requested scope in the
>>>> background and return me the "updated" token.
>>>>
>>>> - When I want to invoke service1 and I need to use "scope=service1", I
>>>> usually need just access token and refresh token. I don't need ID Token
>>>> anymore. I also don't need the "profile" and "email" claims to be
>>>> returned in the new access token. This is related to the JIRA of having
>>>> the server-side support for client scopes like (always, default,
>>>> optional) instead of current (default, optional) [3]. In other words,
>>>> the client scopes "profile" and "email" will be default client scopes,
>>>> which means that if I don't use "scope=openid" in the OIDC initial
>>>> request, the "profile" and "email" will be ommited from the response as
>>>> well as the ID Token will be ommited from the response.
>>>>
>>>>
>>>> So how to support this on adapters? For Keycloak.js, I can think about
>>>> some variation of existing "update" method like this:
>>>>
>>>>
>>>> keycloak.updateTokenWithScope('service1',
>>>> 5).success(function(accessToken, refreshed) {
>>>>          if (refreshed) {
>>>>              alert("I had existing accessToken for scope 'service1',
>>>> but
>>>> it needed to be refreshed as it was expired or about to expire in less
>>>> than 5 seconds");
>>>>          } else {
>>>>              alert('I have accessToken for 'service1',  which didn't
>>>> need to be refreshed');
>>>>          }
>>>>          // I can invoke REST service now with the accessToken
>>>>          ...
>>>>      }).error(function() {
>>>>          alert("Failed to refresh the token OR I don't have yet scope
>>>> for 'service1' .");
>>>>          // User usually need to call keycloak.login with the requested
>>>> scope now...
>>>>      });
>>>>
>>>>
>>>>
>>>>
>>>> For servlet adapter something like:
>>>>
>>>> KeycloakSecurityContext ctx = ... // Retrieved from HttpServletRequest
>>>> or Principal as currently
>>>>
>>>> if (ctx.hasScope("service1")) {
>>>>      try {
>>>>          String accessToken = ctx.getScope("service1");
>>>>          // Invoke service1 with accessToken now
>>>>      } catch (TokenRefreshException ex) {
>>>>          log.error("I already had scope for service1, but failed to
>>>> refresh the token. Need to re-login for the scope service1");
>>>>
>>>>          // See method below
>>>>          redirectToKeycloakWithService1Scope();
>>>>      }
>>>> } else {
>>>>      // See method below
>>>>      redirectToKeycloakWithService1Scope();
>>>> }
>>>>
>>>>
>>>> private void redirectToKeycloakWithService1Scope() {
>>>>      KeycloakRedirectUriBuilder builder = ctx.createLoginUrl();
>>>>      URL url = builder.scope("service1").build();
>>>>      httpServletResponse.sendRedirect(url);
>>>> }
>>>>
>>>>
>>>> Regarding the class KeycloakRedirectUriBuilder, I was thinking about
>>>> this class so that servlet adapter are able to easily create login URL
>>>> with custom values for things like scope, prompt, max_age etc. This
>>>> capability is currently missing in servlet adapters and the current
>>>> approach based on parameters forwarding is a bit clunky for few
>>>> reasons.
>>>> One reason is usability and the other is, that you need to logout first.
>>>>
>>>>
>>>> [1]
>>>>
>>>> https://www.keycloak.org/docs/latest/securing_apps/index.html#javascript-adapter-reference
>>>> [2]
>>>>
>>>> https://www.keycloak.org/docs/latest/securing_apps/index.html#_params_forwarding
>>>> [3] https://issues.jboss.org/browse/KEYCLOAK-8323
>>>>
>>>> Marek
>>>>
>>>> _______________________________________________
>>>> keycloak-dev mailing list
>>>> keycloak-dev at lists.jboss.org
>>>> https://lists.jboss.org/mailman/listinfo/keycloak-dev
>>>
>>>
>>>
>>
>


More information about the keycloak-dev mailing list