On Thu, 7 Feb 2019 at 09:40, Marek Posolda <mposolda(a)redhat.com
<mailto:mposolda@redhat.com>> wrote:
On 06/02/2019 15:26, Stian Thorgersen wrote:
>
>
> On Wed, 6 Feb 2019 at 11:53, Marek Posolda <mposolda(a)redhat.com
> <mailto:mposolda@redhat.com>> wrote:
>
> On 05/02/2019 09:39, Stian Thorgersen wrote:
>> Tried to filter out implementation details here, so may have
>> lost some details. It would be good if we can try to keep
>> discussions at a higher level at least initially as it makes
>> it much easier to follow the discussion.
> Point taken :) Will try to improve next time.
>>
>> For scopes I can see the most common use-case will be the
>> ability to do incremental scopes. By that I mean the
>> application doesn't request all permissions it may need, but
>> rather starts small and asks for more as the user extends
>> use of the application. This is mostly relevant to
>> applications that require consent.
>>
>> Now with regards to the application being able to have
>> different tokens to invoke different services I'm not
>> convinced this is needed so we should rather wait for demand
>> here. There's two ways a single app can consume multiple
>> services:
>>
>> 1) Application directly invokes multiple services - in this
>> case the application should be able to use scopes or token
>> exchange to obtain tokens to invoke different services. In
>> fact I'd say token exchange is probably what is wanted here
>> rather than scopes.
>> 2) Application invokes a backend service that aggregates
>> multiple services - in this case token exchange comes in as
>> the backend service needs to be able to obtain tokens to
>> invoke the different services
>>
>> I would think option 2 is the best approach as it allows
>> implementing the complex code in server side code and also
>> makes the application more transparent to API changes.
> This seems to be inline with what Pedro mentioned as well. +1
> for waiting for a demand for this.
>>
>> With regards to incremental scope support we need to be able
>> to do that without requiring logout. For JS adapter that
>> should already work, but it has one issue and that is you
>> can't set the scope to use with automated login. We should
>> probably make the scope configurable in the init function as
>> well as when invoking login function directly. For servlet
>> we should probably also have a way to configure the initial
>> scope and expose a method to obtain additional scopes
>> without requiring logout.
>
> +1
>
> Regarding JS adapter, there are few other things, which you
> can't do at "init" method. For example adding the
"prompt" .
> Maybe all the current options of "login" method can be just
> used as arguments to "init" method?
>
> Or perhaps it should be something like defaultLoginOptions which
> is an object that we simply pass to the login function?
Was thinking about that as well :) It will be probably better as
if someone wants to call "keycloak.login" and override just single
thing (EG. scope), he can clone the defaultLoginOptions and
override just scope.
Can you create a JIRA for it?
> For the servlet adapter, it will be probably good to have
> something like JS adapter "keycloak.login" method or
> "keycloak.createLoginUrl", which allows to add things like
> custom scope, prompt etc. I proposed something like
> KeycloakLoginUrlBuilder, which will allow to easy add things
> like "scope" or "prompt" in the adapters code.
>
> Isn't really what you want is server-side action that returns the
> redirect rather than directly adding the login url to the page?
> The reason I'm saying that is that the adapter needs to generate
> the state param and such, which it can't do if the login url is
> just placed directly on the page.
I did not mean to directly add URL to the page, but rather
something along the lines of:
KeycloakLoginRedirectURL loginRedirect =
KeycloakLoginRedirectURLBuilder
.prompt("consent")
.scope(accessToken.getScope() + " phone")
.build();
getKeycloakSecurityContext().sendRedirectToLogin(loginRedirect);
Note that when you call "sendRedirectToLogin", it will the
responsibility of the adapter to generate "state" and add
"redirect_uri" from the keycloak.json config etc. Maybe
KeycloakLoginRedirectURL is not the best name for this, rather
something like "KeycloakLoginRedirect" or
"KeycloakLoginRedirectState", but that's an implementation detail.
Got it. That makes sense. Can you create a JIRA?
Marek
>>
>> I'm not convinced about client scope inheritance. It has an
>> additional implementation complexity, but most importantly
>> usability with regards to understand what is actually
>> included in the token when you have inheritance. It may also
>> have some strange side-effects like how do you make sure the
>> order of what is applied is correct. Again, probably
>> something best left until there is demand for it.
>
> We already have "priority" on protocolMappers, so ordering
> likely won't be an issue. You can add protocolMappers of all
> the "effective" client scopes and sort the protocol mappers.
> But agree that will be good to wait for more demand before
> adding this.
>
> Marek
>
>> On Thu, 31 Jan 2019 at 17:04, Pedro Igor Silva
>> <psilva(a)redhat.com <mailto:psilva@redhat.com>> wrote:
>>
>> +1
>>
>> On Thu, Jan 31, 2019 at 11:14 AM Marek Posolda
>> <mposolda(a)redhat.com <mailto:mposolda@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(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
>> >>>
>> >>>
>> >>>
>> >>
>> >
>> _______________________________________________
>> keycloak-dev mailing list
>> keycloak-dev(a)lists.jboss.org
>> <mailto:keycloak-dev@lists.jboss.org>
>>
https://lists.jboss.org/mailman/listinfo/keycloak-dev
>>
>