Login & Auth Workflows

WebApp OAuth Login Using Authorization Code Grant With Sessions And Refresh Tokens

By Brian Pontarelli

This workflow is used by web applications using the FusionAuth OAuth login interface. The web application navigates over to FusionAuth and then FusionAuth redirects back to the web application at the end of the OAuth workflow. Below is a diagram that describes the primary components of this workflow and how they interact. Keep in mind that not every interaction is covered here, just the primary login interactions. At the bottom of the diagram is a discussion of the key steps.

For all of our examples, we use a store and a forum for the same company. The store requires a user to login to view their shopping cart and the forum requires the user to login to view forum posts. We also provide a couple of example attack vectors that hackers could use if portions of the system are compromised. These cases might be theoretical or based on known exploits such as XSS (cross-site scripting).

Diagram

Legend

() --> request/response bodies
{} --> request parameters
[] --> cookies
BrowserStoreForumsFusionAuthHackerInitializeLogin (browser navigates away from WebApp)Shopping cart loadSession expiresRefresh token still validShopping cart loadSession expiresRefresh token expiresRe-loginSSO login to forumsInitializeLogin (browser navigates away from WebApp but auto-logs-in since the session exists)Forum loadAttack vectorsStolen refresh tokenStolen session idGET /1(HTML, CSS & JavaScript - with login link)2GET /oauth2/authorize {response_type=code}3(Login form HTML)4POST /oauth2/authorize (response_type=code)5302 Location: {redirect_uri w/ code}[SessionId HttpOnly w/ domain: example.fusionauth.io]6GET {redirect_uri w/ code}7POST /oauth2/token(code, client_secret)8200 Ok(Refresh token and JWT)9Create session andstore User in it10302 Location: /shopping-cart[SessionId and Refresh token HttpOnly w/ domain: store.example.com]11GET /shopping-cart[SessionId and Refresh token HttpOnly w/ domain: store.example.com]12Session extended13(Shopping cart HTML)14GET /shopping-cart[SessionId and Refresh token HttpOnly w/ domain: store.example.com]15POST /oauth2/token or POST /api/jwt/refresh(grant_type=refresh and refresh token)16(JWT)17Create new session andstore User in it18(Shopping cart contents)[New SessionId HttpOnly w/ domain: store.example.com]19GET /shopping-cart[SessionId and Refresh token HttpOnly w/ domain: store.example.com]20POST /oauth2/token or POST /api/jwt/refresh(grant_type=refresh and refresh token)21404 Missing22302 Location: /login23Login same as above24GET /[No cookies]25(HTML, CSS & JavaScript - with login link)26GET /oauth2/authorize {response_type=code}[SessionId HttpOnly w/ domain: example.fusionauth.io]27302 Location: {redirect_uri w/ code}[SessionId HttpOnly w/ domain: example.fusionauth.io]28GET {redirect_uri w/ code}29POST /oauth2/token(code, client_secret)30200 Ok(Refresh token and JWT)31Create session andstore User in it32302 Location: /posts[SessionId and Refresh token HttpOnly w/ domain: forums.example.com]33GET /posts[SessionId and Refresh token HttpOnly w/ domain: forums.example.com]34Session extended35(Forum posts HTML)36GET /shopping-cart[Refresh token and bad session id HttpOnly w/ domain: store.example.com]37POST /oauth2/token or POST /api/jwt/refresh(grant_type=refresh and refresh token)38(JWT)39Create session andstore User in40(Shopping cart HTML)[New JWT HttpOnly w/ domain: store.example.com]41GET /shopping-cart[SessionId HttpOnly w/ domain: store.example.com]42Session extended43(Shopping cart HTML)44BrowserStoreForumsFusionAuthHacker

Explanation

  1. The browser requests the shopping cart webapp's homepage from the application backend
  2. The application backend responds with the HTML, CSS & JavaScript of the homepage
  3. The user clicks the login link and the browser navigates away from the webapp to FusionAuth's OAuth 2 interface. The browser requests the OAuth 2 login page from FusionAuth with a response_type of code indicating that it is using the authorization code grant
  4. FusionAuth responds with the HTML, CSS & JavaScript of the login page (including the form)
  5. The user inputs their credentials and clicks the submit button. The browser POSTs the form data to FusionAuth
  6. FusionAuth returns a redirect to the application backend's OAuth 2 redirect_uri. This redirect includes the authorization code from FusionAuth. Also, this response includes a session id for the FusionAuth OAuth 2 interface as an HTTP cookie. This cookie is HttpOnly, which prevents JavaScript from accessing it, making it less vulnerable to theft
  7. The browser requests the application backend's OAuth redirect_uri with the authorization code from FusionAuth
  8. The application backend calls FusionAuth's OAuth 2 token endpoint with the authorization code and optionally the client_secret
  9. FusionAuth verifies the authorization code and client_secret. It returns a 200 along with a JWT and refresh token in JSON
  10. The application backend receives the 200 from FusionAuth and creates a server-side session and stores the User object (or JWT) in it
  11. The application backend returns a redirect to the browser instructing it to navigate to the user's shopping cart. The id for the server-side session is written back to the browser in an HTTP cookie. The refresh token from FusionAuth is also written back to the browser in an HTTP cookie. These cookies are HttpOnly, which prevents JavaScript from accessing them, making them less vulnerable to theft. Additionally, all requests from the browser to the application backend will include these cookies so that the backend can retrieve the User object from the server-side session and refresh their session if it expires
  12. The browser requests the user's shopping cart from the application backend and includes the session and refresh token cookies
  13. The application backend looks up the server-side session associated with the session cookie and extends the expiration date
  14. The application backend loads the User object (or JWT) from the server-side session. The backend then looks up the user's shopping cart from the database (or similar location). Finally, the application backend returns the user's shopping cart as HTML, CSS & JavaScript that the browser renders
  15. A while later, the user's session expires and the user clicks on their shopping cart again. The browser requests the shopping cart from the application backend and sends the session id and refresh token cookies to the application backend
  16. The application backend attempts to load the server-side session associated with session cookie and realizes it is expired. Since the browser also sent across the refresh token, the application backend calls the JWT refresh API in FusionAuth with the refresh token
  17. FusionAuth looks up the refresh token and returns a new JWT
  18. The application backend receives the 200 from FusionAuth and creates a server-side session and stores the User object (or JWT) in it
  19. The application backend responds with the user's shopping cart HTML, CSS & JavaScript that the browser renders. It also includes the new session id as a cookie that replaces the old session id in the browser
  20. A while later, the user's server-side session and refresh token both expire and the user clicks on their shopping cart again. The browser requests the shopping cart from the application backend and sends the session and refresh token cookies to the application backend
  21. The application backend attempts to load the server-side session associated with session cookie and realizes it is expired. Since the browser also sent across the refresh token, the application backend calls the JWT refresh API in FusionAuth with the refresh token
  22. Since the refresh token has expired, FusionAuth returns a 404 status code
  23. Since FusionAuth returned a 404 status code, the application backend returns a redirect to the browser that sends the user to the login page
  24. The user can log in the same way they did above
  25. The browser requests the forum webapp's homepage from the application backend. This is a standard SSO login that is fully supported by FusionAuth
  26. The application backend responds with the HTML, CSS & JavaScript of the homepage
  27. The user clicks the login link and the browser navigates away from the webapp to FusionAuth's OAuth 2 interface. The browser requests the OAuth 2 login page from FusionAuth with a response_type of code indicating that it is using the authorization code grant. Additionally, the session cookie that was set during the first login is also sent by the browser to FusionAuth
  28. FusionAuth realizes that the user already has a session and is already logged in. Therefore, it returns a redirect to the application backend's OAuth 2 redirect_uri. This redirect includes the authorization code from FusionAuth
  29. The browser requests the application backend's OAuth redirect_uri with the authorization code from FusionAuth
  30. The application backend calls FusionAuth's OAuth 2 token endpoint with the authorization code and optionally the client_secret
  31. FusionAuth verifies the authorization code and client_secret. It returns a 200 along with a JWT and refresh token in JSON. **NOTE**: all of this happens without any user interaction, hence the SSO nature of this login
  32. The application backend receives the 200 from FusionAuth and creates a server-side session and stores the User object (or JWT) in it
  33. The application backend returns a redirect to the browser instructing it to navigate to the user's forum posts. The id for the server-side session is written back to the browser in an HTTP cookie. The refresh token from FusionAuth is also written back to the browser in an HTTP cookie. These cookies are HttpOnly, which prevents JavaScript from accessing them, making them less vulnerable to theft. Additionally, all requests from the browser to the application backend will include these cookies so that the backend can retrieve the User object from the server-side session and refresh their session if it expires
  34. The browser requests the user's forum posts from the application backend and includes the session and refresh token cookies
  35. The application backend looks up the server-side session associated with the session cookie and extends the expiration date
  36. The application backend loads the User object (or JWT) from the session associated with the session cookie. The backend then looks up the user's forum posts from the database (or similar location). Finally, the application backend returns the user's forum posts as HTML, CSS & JavaScript that the browser renders
  37. This is an attack vector where the attacker has stolen the user's refresh token. Here, the attacker requests the user's shopping cart with the stolen refresh token and an invalid session id
  38. The application backend verifies the session id and realizes it is invalid. Since the browser also sent across the refresh token, the application backend calls the JWT refresh API in FusionAuth with the refresh token
  39. FusionAuth looks up the refresh token and returns a new JWT
  40. The application backend receives the 200 from FusionAuth and creates a server-side session and stores the User object (or JWT) in it
  41. The application backend uses the JWT to look up the user's shopping cart. It responds to the attacker with the user's shopping cart HTML, CSS & JavaScript. It also includes the new session id as a cookie that attacker can now use
  42. This is an attack vector where the attacker has stolen the user's session cookie. Here, the attacker requests the user's shopping cart with the stolen session cookie
  43. The application backend looks up the server-side session associated with the session cookie and extends the expiration date
  44. The application backend uses the session to look up the user's shopping cart. It responds to the attacker with the user's shopping cart HTML, CSS & JavaScript

Security considerations

This is one of the safest and most feature rich login workflow in FusionAuth. It has the benefit that passwords are only ever provided directly to FusionAuth. It also has the benefit of full SSO capabilities when the user is automatically logged into the forum application by FusionAuth. Also, the session and refresh tokens are HttpOnly cookies that are domain locked to the application backend that needs them. Plus, the User object (or JWT) is secured on the server inside a server-side session.

APIs used

Here are the FusionAuth APIs used in this example: