> For the complete documentation index, see [llms.txt](/docs/llms.txt)

# WebApp OAuth Login Using Authorization Code Grant With JWTs And Refresh Tokens - Recommended | FusionAuth Docs

An explanation of webapp login using FusionAuth OAuth interface with the authorization code grant with JWTs and refresh tokens in cookies

# WebApp OAuth Login Using Authorization Code Grant With JWTs And Refresh Tokens - Recommended

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[#](#diagram)

**Legend**

```
() --> request/response bodies
{} --> request parameters
[] --> cookies
```

```mermaid
sequenceDiagram
  autonumber
  participant Browser
  participant Store
  participant Forums
  participant FusionAuth
  participant Hacker
  Note over Browser,Hacker: Initialize
  Browser->>Store: GET /
  Store->>Browser: (HTML, CSS & JavaScript - with login link)

  Note over Browser,Hacker: Login (browser navigates away from WebApp)
  Browser->>FusionAuth: GET /oauth2/authorize {response_type=code}
  FusionAuth->>Browser: (Login form HTML)
  Browser->>FusionAuth: POST /oauth2/authorize (response_type=code)
  FusionAuth->>Browser: 302 Location: {redirect_uri w/ code}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  Browser->>Store: GET {redirect_uri w/ code}
  Store->>FusionAuth: POST /oauth2/token<br/>(code, client_secret)
  FusionAuth->>Store: 200 Ok<br/>(Refresh token and JWT)
  Store->>Browser: 302 Location: /shopping-cart<br/>[Refresh token and JWT HttpOnly w/ domain: store.example.com]

  Note over Browser,Hacker: Shopping cart load
  Browser->>Store: GET /shopping-cart<br/>[Refresh token and JWT HttpOnly w/ domain: store.example.com]
  Store->>Browser: (Shopping cart HTML)

  Note over Browser: JWT expires
  
  Note over Browser,Hacker: Shopping cart load
  Browser->>Store: GET /shopping-cart<br/>[Refresh token and JWT HttpOnly w/ domain: store.example.com]
  Store->>FusionAuth: POST /oauth2/token or POST /api/jwt/refresh<br/>(grant_type=refresh and refresh token)
  FusionAuth->>Store: (JWT)
  Store->>Browser: (Shopping cart HTML)<br/>[New JWT HttpOnly w/ domain: store.example.com]

  Note over Browser: Refresh token expires
  
  Note over Browser,Hacker: Re-login
  Browser->>Store: GET /shopping-cart<br/>[Refresh token and JWT HttpOnly w/ domain: store.example.com]
  Store->>FusionAuth: POST /oauth2/token or POST /api/jwt/refresh<br/>(grant_type=refresh and refresh token)
  FusionAuth->>Store: 404 Missing
  Store->>Browser: 302 Location: /login
  Browser->>Browser: Login same as above

  Note over Browser,Hacker: SSO login to forums

  Note over Browser,Hacker: Initialize
  Browser->>Forums: GET /<br/>[No cookies]
  Forums->>Browser: (HTML, CSS & JavaScript - with login link)
  
  Note over Browser,Hacker: Login (browser navigates away from WebApp but auto-logs-in since the session exists)
  Browser->>FusionAuth: GET /oauth2/authorize {response_type=code}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  FusionAuth->>Browser: 302 Location: {redirect_uri w/ code}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  Browser->>Forums: GET {redirect_uri w/ code}
  Forums->>FusionAuth: POST /oauth2/token<br/>(code, client_secret)
  FusionAuth->>Forums: 200 Ok<br/>(Refresh token and JWT)
  Forums->>Browser: 302 Location: /posts<br/>[Refresh token and JWT HttpOnly w/ domain: forums.example.com]

  Note over Browser,Hacker: Forum load
  Browser->>Forums: GET /posts<br/>[Refresh token and JWT HttpOnly w/ domain: forums.example.com]
  Forums->>Browser: (Forum posts HTML)

  Note over Browser,Hacker: Attack vectors

  Note over Browser,Hacker: Stolen refresh token
  Hacker->>Store: GET /shopping-cart<br/>[Refresh token and bad JWT HttpOnly w/ domain: store.example.com]
  Store->>FusionAuth: POST /oauth2/token or POST /api/jwt/refresh<br/>(grant_type=refresh and refresh token)
  FusionAuth->>Store: (JWT)
  Store->>Hacker: (Shopping cart HTML)<br/>[New JWT HttpOnly w/ domain: store.example.com]

  Note over Browser,Hacker: Stolen JWT
  Hacker->>Store: GET /shopping-cart<br/>[JWT HttpOnly w/ domain: store.example.com]
  Store->>Hacker: (Shopping cart HTML)
```

## Explanation[#](#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 `POST`s 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. It returns a redirect to the browser instructing it to navigate to the user's shopping cart. 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
11.  The browser requests the user's shopping cart from the application backend and includes the JWT and refresh token cookies
12.  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 as HTML, CSS & JavaScript that the browser renders
13.  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 and sends the JWT and refresh token to the application backend
14.  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.
15.  FusionAuth looks up the refresh token and returns a new JWT
16.  The application backend responds with the user's shopping cart HTML, CSS & JavaScript that the browser renders. It also includes the new JWT as a cookie that replaces the old JWT in the browser
17.  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 and sends the JWT and refresh token to the application backend
18.  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
19.  Since the refresh token has expired, FusionAuth returns a 404 status code
20.  Since FusionAuth returned a 404 status code, the application backend returns a redirect to the browser that sends the user to the login page
21.  The user can log in the same way they did above
22.  The browser requests the forum webapp's homepage from the application backend. This is a standard SSO login that is fully supported by FusionAuth
23.  The application backend responds with the HTML, CSS & JavaScript of the homepage
24.  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
25.  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
26.  The browser requests the application backend's OAuth `redirect_uri` with the authorization code from FusionAuth
27.  The application backend calls FusionAuth's OAuth 2 token endpoint with the authorization code and optionally the `client_secret`
28.  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
29.  The application backend receives the 200 from FusionAuth. It returns a redirect to the browser instructing it to navigate to the user's forum posts. 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
30.  The browser requests the user's forum posts from the application backend 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 as HTML, CSS & JavaScript that the browser renders
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 browser 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 HTML, CSS & JavaScript. 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 attack 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 as HTML, CSS & JavaScript to the attacker

## Security considerations[#](#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. Finally, the JWT and refresh tokens are HttpOnly cookies that are domain locked to the application backend that needs them.

## APIs used[#](#apis-used)

Here are the FusionAuth APIs used in this example:

*   [/oauth2/authorize](/docs/apis/oauth/authorize)
*   [/oauth2/token](/docs/apis/oauth/token#refresh-token-grant-request)
*   [/api/jwt/refresh](/docs/apis/jwt/refresh-a-jwt)