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.
. 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(a)redhat.com
<mailto:mposolda@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(a)redhat.com <mailto:mposolda@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(a)redhat.com <mailto:mposolda@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-...
>> [2]
>>
https://www.keycloak.org/docs/latest/securing_apps/index.html#_params_for...
>> [3]
https://issues.jboss.org/browse/KEYCLOAK-8323
>>
>> Marek
>>
>> _______________________________________________
>> keycloak-dev mailing list
>> keycloak-dev(a)lists.jboss.org
>> <mailto:keycloak-dev@lists.jboss.org>
>>
https://lists.jboss.org/mailman/listinfo/keycloak-dev
>>
>