Ok, option #3 where I would pass an access token to initiate the link
won't work as the access token could be leaked by a CSRF attack on the
client. My new solution is to do a custom protocol. The goal of this
protocol is for the server to verify that the client initiated the
account link and not some rogue site.
1. Client does a background bearer-token authenticated to the Keycloak
operation /initiate-link using the access token it got from login.
2. /initiate-link gets the ClientSessionModel associated with the
bearer-token. It generates a server-nonce and stores it within the
3. keycloak /initiate-link returns a response that contains the server-nonce
4. Client generates its own client-nonce. This client-nonce is stored
in its HttpSession.
5. Client generates a nonce-hash of server-nonce + client-nonce +
6. Client redirects the browser to Keycloak /link operation passing the
client-nonce, nonce-hash, provider-id, client-id, and redirect-uri as
7. Keycloak /link operation validates that the browser is logged in. If
the user is not logged, an error page is shown. We DO NOT redirect back
to the client when there is an error as this is an attack vector.
8. Keycloak verifies clientid, redirect uri, and provider id.
9. Keycloak loops through the browser's UserSessionModel looking for
ClientSessionModels that match the clientId. Checks to see if this
ClientSession has a server-nonce within it. If so, it validates the
hash. We DO NOT redirect back to the client when there is an error as
this is an attack vector. The server-nonce client session attribute is
cleared so that it can only be used once.
10. Keycloak initiates the social provider login
11. Keycloak redirects back to the client using the redirect_uri
What isn't protected here is the client. If the client is a traditional
web app, the client is responsible for protecting itself from CSRF. The
Account Service does this with a cookie nonce plus the nonce in the
rendered page links, and it also rejects any CORS requests. The client
affected unless there is a URL you can redirect to that triggers this flow.
Again, I'm not even sure we have to do this sort of thing. All a CSRF
attack could do is request that the user link their Keycloak account.
The attacker can't get any information about the user.
On 2/24/17 9:10 AM, Bill Burke wrote:
Account linking *MUST* be browser based as you need to delegate
authentication to the IDP you are brokering with.
On 2/24/17 3:21 AM, Marek Posolda wrote:
> Don't we have a plan to rewrite AccountService to use Angular+REST? If
> yes, then we are fine though. We will have REST endpoint for link
> broker, which can be invoked with the bearer token as long as the
> token has "manage-account" scope. We don't need to care about CSRF
> I can see the only reason to keep support browser+cookie based flows -
> the case when client application doesn't have Keycloak token. But from
> what you mentioned in the last paragraph, it looks that their client
> has our access token?
> On 23/02/17 23:24, Bill Burke wrote:
>> A customer has asked us to implement a feature in which there is a
>> browser endpoint on keycloak. This URL can be told to link to a
>> specific identity broker provider (Google, Facebook, etc.) and then the
>> browser would be redirected back to the client. Pretty much what exists
>> in the Account Service console, but without having to look at the
>> Account Service. The reason for this is that they are doing integration
>> with a specific social provider and don't want to have to go through the
>> Account Service pages. Seems pretty reasonable and valid use case...
>> I'm worried about a couple of things:
>> * The design of it
>> * The security implications.
>> The implementation would be simple enough, it would just extract code
>> from the accoutn service. The endpoint would take a "providerId"
>> paramter that would be the idp linking to, a "clientId" for the client
>> requesting the link, and a "redirect_uri" to redirect back to after
>> link is successful. Obviously the redirect_uri would be validated
>> against the clientId.
>> Now comes the interesting part, the security implications. Account
>> linking in the Account Service is fine and dandy and doesn't have any
>> security holes because we guarantee that the Account Service initiated
>> the linking via a CSRF check. For this new Client-Requested-Linking
>> feature, if we do nothing, and just model it as above, we cannot
>> guarantee that the client initiated the linking request. What are the
>> implications of this? This feature would be vulnerable to CSRF. A
>> rogue website could initiate account linking to a specific configured
>> provider. Is this bad? I don't know. We can guarantee that the
>> redirect uri is valid. The browser would never get back to the rogue
>> website. So what can we do to improve this?
>> * We could do nothing and hope its ok that anybody can initiate a link.
>> * We could add a consent screen like this: "Application 'clientId'
>> requesting that you link your user account to 'providerId'. Do you
>> agree to this? You will be redirected to this provider if you click
>> ok.". This of course relies on the user to actually read the page. Is
>> this good enough?
>> * My last thought would be OIDC specific. We could use the POST binding
>> trick that SAML does to do a browser redirect via a POST call. THe POST
>> would contain the access token the client has. We match this token up
>> against the browser cookie to make sure its the same session. We also
>> make sure that the access token has permission to request a link. There
>> are 2 downsides to this approach a) This requires some code on the
>> client side to obtain the access token and then to package it up into an
>> HTML document so the POST binding trick can be done. b) This will only
>> work for OIDC clients. SAML clients will be left in the dust, although
>> I guess we could allow SAML to push a signed assertion back.
>> keycloak-dev mailing list
keycloak-dev mailing list