Single-page application OAuth login using resource owner password credentials grant

ARTICLE BY

This workflow is used by single-page applications using a native login form inside the webapp. This login form uses an AJAX POST to send the user’s credentials (email and password) to the backend of the application. The application backend then in turn calls to FusionAuth. 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

() --> indicate request/response bodies
{} --> indicate request parameters
[] --> indicate cookies
Sequence diagram for the workflow
Sequence diagram for the workflow

Explanation

  1. The browser requests the shopping cart single-page application from the application backend
  2. The application backend responds with the HTML, CSS & JavaScript of the application
  3. The browser loads the application and as part of the initialization process, it makes a request to the application backend to see if the user is logged in
  4. The application backend responds with a 404 indicating the user is not logged in
  5. The application renders the login form
  6. The user inputs their credentials and clicks the submit button. The browser AJAX POSTs the form data to the application backend
  7. The application backend calls the OAuth token endpoint in FusionAuth by passing in the credentials it received plus a grant_type of password, which indicates it is using the resource owner password credentials grant in FusionAuth’s OAuth 2 backend
  8. FusionAuth returns a 200 status code stating that the credentials were okay. It also returns a JWT and a refresh token in JSON
  9. The application backend receives the 200 from FusionAuth. The JWT and refresh token from FusionAuth are written back to the browser in HTTP cookies. 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 use them. Also, if the application needs the user object, the application backend can send it back in JSON
  10. The browser requests the user’s shopping cart via AJAX from the application backend and includes the JWT and refresh token cookies
  11. The application backend verifies the JWT and then uses the JWT to identify the user. Once the user is identified, the backend looks up the user’s shopping cart from the database (or similar location). Finally, the application backend returns the user’s shopping cart contents (usually as JSON)
  12. A while later, the user’s JWT expires and the user clicks on their shopping cart again. The browser requests the shopping cart from the application backend via AJAX and sends the JWT and refresh token to the application backend
  13. The application backend verifies the JWT 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
  14. FusionAuth looks up the refresh token and returns a new JWT
  15. The application backend responds with the user’s shopping cart contents (usually as JSON) that the browser renders. It also includes the new JWT as a cookie that replaces the old JWT in the browser
  16. A while later, the user’s refresh token expires and the user clicks on their shopping cart again. The browser requests the shopping cart from the application backend via AJAX and sends the JWT and refresh token to the application backend
  17. The application backend verifies the JWT 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
  18. Since the refresh token has expired, FusionAuth returns a 404 status code
  19. Since FusionAuth returned a 404 status code, the application backend returns a 401 that indicates the user is no longer logged in
  20. At this point, the application can allow the user to log in the same way they did above
  21. The browser requests the forums single-page application from the application backend. This is a standard SSO login that is fully supported by FusionAuth
  22. The application backend responds with the HTML, CSS & JavaScript of the application
  23. The browser loads the application and as part of the initialization process, it makes a request to the application backend to see if the user is logged in
  24. The application backend responds with a 404 indicating the user is not logged in
  25. The application renders the login form
  26. The user inputs their credentials and clicks the submit button. The browser AJAX POSTs the form data to the application backend
  27. The application backend calls the OAuth token endpoint in FusionAuth by passing in the credentials it received plus a grant_type of password, which indicates it is using the resource owner password credentials grant in FusionAuth’s OAuth 2 backend
  28. FusionAuth returns a 200 status code stating that the credentials were okay. It also returns a JWT and a refresh token in JSON
  29. The application backend receives the 200 from FusionAuth. The JWT and refresh token from FusionAuth are written back to the browser in HTTP cookies. 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 use them. Also, if the application needs the user object, the application backend can send it back in JSON
  30. The browser requests the user’s forum posts from the application backend via AJAX and includes the JWT and refresh token cookies
  31. The application backend verifies the JWT and then uses the JWT to identify the user. Once the user is identified, the backend looks up the user’s forum posts from the database (or similar location). Finally, the application backend returns the user’s forum posts that the browser renders (usually as JSON)
  32. 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 JWT
  33. The application backend verifies the JWT and realizes it is invalid. Since the attacker also sent across the refresh token, the application backend calls the JWT refresh API in FusionAuth with the refresh token
  34. FusionAuth looks up the refresh token and returns a new JWT
  35. 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 (usually as JSON). It also includes the new JWT as a cookie that attacker can now use
  36. This is an attack vector where the attacker has stolen the user’s JWT. Here, the attacker requests the user’s shopping cart with the stolen JWT
  37. The application backend verifies the JWT and then uses the JWT to identify the user. Once the user is identified, the backend looks up the user’s shopping cart from the database (or similar location). Finally, the application backend returns the user’s shopping cart to the attacker (usually as JSON)

Security considerations

This workflow is one of the more secure methods of authenticating users. One downside is that the application backend receives passwords from the browser. While this isn’t an issue if TLS is used and the passwords are not stored by the application backend, developers that do not want to be part of the password chain of responsibility should consider other workflows.

APIs used

Here are the FusionAuth APIs used in this example:

View All Types