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:
- The client authentication step by providing client secret and authorization code is missing. So less security
- 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
- 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:
- the user clicks on the login button on the SPA landing page
- the user is redirected to the authorization server login page. The client id is provided in the URL query parameter
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
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
- 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
- The user will then be redirected to a resource server endpoint with a authorization code in the URL query parameter
- 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)
- 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)
- 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.