[keycloak-dev] Better support for "scope" in adapters
Marek Posolda
mposolda at redhat.com
Thu Jan 31 08:14:53 EST 2019
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
> <mailto: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 <mailto: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 <mailto: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
>>> <mailto:keycloak-dev at lists.jboss.org>
>>> https://lists.jboss.org/mailman/listinfo/keycloak-dev
>>>
>>
>
More information about the keycloak-dev
mailing list