7
votes

I wonder how to deal with refreshing token in Oauth2 Implicit grant flow in 2019 when major browsers have 3rd party cookies disabled by default.

Some details:

Current setup:

  • UI SPA app under ui.example.com

  • Identity Provider (UAA by CloudFoundry) under uaa.api.example.com

Scenario:

  • when user signs in, Identity Provider sets cookie with user details for domain uaa.api.example.com and returns JWT in the redirect's Location header.

  • JWT is stored in the local storage (for ui.example.com), but it's valid only for 1h so I'd like to refresh it.

  • refreshing is possible with prompt=none query param sent to IDP authorization endpoint (process is well described in Auth0 guide (it's not UAA but flow is the same)

  • in every 20m hidden iframe with src set to uaa.api.exmaple.com/oauth/authorize?prompt=none is created what starts the signing in process without requiring user to provide his credentials. When process ends, new JWT returned in the response is stored again in the local storage.

Problem:

  • When third party cookies are allowed, browser adds the IDP's cookies to the request made by an iframe, so flow works and I get new token in the response.

  • When third Party Cookies are disabled in the browser's settings, iframe doesn't have an access to its own cookies, so instead of new JWT, error login_required is returned. Inability to access cookies by iframe makes token renewal impossible to use

Question:

Is there any solution for my issue with 3rd party cookies?

If not, are there any alternatives for Implicit Grant flow and SPA that I could use to sign in and refresh tokens?

4

4 Answers

1
votes

As your application and identity server hosted in a different domain. It implies that your application is doing cross-origin authentication. Cross-origin authentication is achieved using third-party cookies, disabling third-party cookies will make cross-origin authentication fail.

Answering your questions:

Is there any solution for my issue with 3rd party cookies?

Host both your application and the identity server under the same domain. You can use the subdomain in that case.

If not, are there any alternatives for Implicit Grant flow and SPA that I could use to sign in and refresh tokens?

No

Solution:

I am not familiar with the CloudFoundry. Not sure they support it or not. You can solve the issue by enabling custom domain in the identity provider side. Thus, both of your application and the Identity Provider will be in the same domain and the cookies will be considered as the first party. For example, Host your application at https://acme.com and set your identity provider custom domain as https://login.acme.com

0
votes

Question:

Is there any solution for my issue with 3rd party cookies?

If you use the same top-level domain between your app and your IDP then you should have no problems when 3rd party cookies are disabled. This link also details using cross-origin policies with mixed success.

If not, are there any alternatives for Implicit Grant flow and SPA that I could use to sign in and refresh tokens?

I've not used CloudFoundry before, but most big OAuth2.0 providers offer public client functionality where a public client (e.g. your SPA) does not require a client secret to obtain an access / refresh token. This allows public clients to use the Authorisation Code Grant which can allow tokens to be refreshed via a refresh token thus avoiding the silent auth technique of using HTTP redirects and cookies.

0
votes

The root of the problem is the use of iframe and Implicit grant type.

I think your reason for using the iframe is to access the cookie across domains. Now, the simplest way to avoid using iframe is, setting the domain of the cookie as Domain=example.com and have both the UI and the Authorization server on example.com. If for some reason, you can't do that, you need to go with the following approach.


Recommended Option

Implicit grant type is not secured. Although this question is not about the pros and cos of the grant types, in order to set the background for the option I'm going to explain, let me briefly enumerate the reasons why I say Implicit flow is not secured:

  1. The client authentication step by providing client secret and authorization code is missing. So less security
  2. The access token is sent back as a URL fragment (so that the token doesn't go to the server) which will continue to stay in browser history
  3. If XSS attack occurs, the malicious script can very well send the token to a remote server in control of an attacker

Therefore, the recommended option is to use Authorization Code grant type. One of the reasons for not using Authorization Code in a SPA (Single Page Application) is, it requires the client secret to be stored in the browser and we know that browser can't keep secrets. This risk can be very easily mitigated by having a proxy component (could be embedded in the resource server) on the server side to hold the client secret and act as a proxy between the SPA and the Authorization server.

Here (in the authorization code grant type) the flow looks like below:

  1. the user clicks on the login button on the SPA landing page
  2. the user is redirected to the authorization server login page. The client id is provided in the URL query parameter
  3. The user enters his / her credentials and clicks on the login button. The username and password will be sent to the authorization server using HTTP POST. The credentials should be sent in the request body or header and NOT in the URL (as URLs are logged in browser history and application server). Also, the proper caching HTTP headers should be set, so that the credentials are not cached: Cache-Control: no-cache, no-store, Pragma: no-cache, Expires: 0

  4. The authorization server authenticates the user against a user database (say, LDAP server) where the username and the hash of the user password (hashing algorithms like Argon2, PBKDF2, Bcrypt or Scrypt) is stored with a random salt

  5. On successful authentication, the authorization server would retrieve from its database the redirect URL against the provided client id in the URL query parameter. The redirect URL is the resource server URL
  6. The user will then be redirected to a resource server endpoint with a authorization code in the URL query parameter
  7. The resource server will then do an HTTP POST request to the authorization server for access token. The authorization code, client id, client secret should go in the request body. (Appropriate caching headers as above should be used)
  8. The authorization server would return the access token and the refresh token in response body or header (with the appropriate caching header as mentioned above)
  9. The resource server will now redirect the user (HTTP response code 302) to the SPA URL by setting appropriate cookies with domain attribute as Domain=example.com (assuming both the resource server and the UI are both on sub-domains of example.com). The domain of Authorization server doesn't matter because it is not setting any cookie.

In the same way, the request for access token refresh can be sent to the proxy component, which will read the refresh and access tokens from the cookie and call the Authorization server api with the tokens thus extracted, the client id and the client secret.

0
votes

Finally, we decided to go with a different solution. When JWT lifetime ends, we display a modal informing that session has timed out and with 2 buttons, one to logout and one to keep the session. When user hits "keep the session" new tab/popup-window is opened where user is re-authenticated in IDP either by providing his credentials again or automatically if the IDP session is still active.

So the flow is:

JWT lifetime ends -> 'keep session' in modal chose -> open new tab/popup-window with IDP login form -> successfully authenticated -> redirect back to app -> store token in browser's storage -> close popup-window/tab with window.close() -> get new token from storage and use it in next calls

Because we use new popup-window/tab to re-authenticate, there is not problem with 3rd party cookies.

This also gives one huge advantage. User will not lose his work no matter when he goes back to the application, because modal will be waiting there. I think, additionally it let us meet the Re-authenticing accessibility success criterion (level AAA)

Success Criterion 2.2.5 Re-authenticating

When an authenticated session expires, the user can continue the activity without loss of data after re-authenticating.