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.
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).
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" ]
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