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

# Single-Page Application OAuth Login Using Implicit Grant With Session | FusionAuth Docs

An explanation of single-page application login using FusionAuth OAuth interface with the implicit grant with server-side sessions

# Single-Page Application OAuth Login Using Implicit Grant With Session

By Brian Pontarelli

This workflow is used by single-page applications using the FusionAuth OAuth login interface. The single-page application navigates away from its interface and over to FusionAuth’s OAuth interface. Once the user completes their login, FusionAuth redirects back to the single-page application. This requires that the single-page application re-initialize itself, but the browser should cache the application files and be able to restart it quickly. 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
  autonumber
  Browser->>Store: GET /
  Store->>Browser: (SPA HTML, CSS & JavaScript)
  Browser->>Store: AJAX GET /api/user<br/>[No cookies]
  Store->>Browser: 404 Missing

  Note over Browser,Hacker: Login (browser navigates away from SPA)
  Browser->>FusionAuth: GET /oauth2/authorize {response_type=token}
  FusionAuth->>Browser: (Login form HTML)
  Browser->>FusionAuth: POST /oauth2/authorize (response_type=token)
  FusionAuth->>Browser: 302 Location: {redirect_uri w/ JWT in redirect URI and no refresh token}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  Browser->>Store: GET {redirect_uri w/o JWT in URI}
  Store->>Browser: (SPA HTML, CSS & JavaScript)

  Note over Browser,Hacker: Start session
  Browser->>Store: AJAX POST /api/start-session<br/>(JWT from redirect_uri)
  Store->>Store: Optionally store JWT in session
  Store->>Browser: [SessionId HttpOnly w/ domain: store.example.com]

  Note over Browser,Hacker: Shopping cart load
  Browser->>Store: AJAX GET /api/load-shopping-cart<br/>[SessionId HttpOnly w/ domain: store.example.com]
  Store->>Store: Session extended
  Store->>Browser: (Shopping cart contents)

  Note over Browser: Session expires

  Note over Browser,Hacker: Shopping cart load
  Browser->>Store: AJAX GET /api/load-shopping-cart<br/>[SessionId HttpOnly w/ domain: store.example.com]
  Store->>Browser: 401 Not Authorized

  Note over Browser,Hacker: Automatic re-login (since session exists)
  Browser->>FusionAuth: GET /oauth2/authorize {response_type=token}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  FusionAuth->>Browser: 302 Location: {redirect_uri w/ JWT in redirect URI and no refresh token}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  Browser->>Store: GET {redirect_uri w/o JWT in URI}
  Store->>Browser: (SPA HTML, CSS & JavaScript)

  Note over Browser,Hacker: Start session
  Browser->>Store: AJAX POST /api/start-session<br/>(JWT from redirect_uri)
  Store->>Store: Optionally store JWT in session
  Store->>Browser: [SessionId HttpOnly w/ domain: store.example.com]

  Note over Browser,Hacker: Shopping cart load
  Browser->>Store: AJAX GET /api/load-shopping-cart<br/>[SessionId HttpOnly w/ domain: store.example.com]
  Store->>Store: Session extended
  Store->>Browser: (Shopping cart contents)

  Note over Browser,Hacker: SSO login to forums

  Note over Browser,Hacker: Initialize
  Browser->>Forums: GET /<br/>[No cookies]
  Forums->>Browser: (SPA HTML, CSS & JavaScript)
  Browser->>Forums: GET /api/user<br/>[No cookies]
  Forums->>Browser: 404 Missing
  
  Note over Browser,Hacker: Login (browser navigates away from SPA but auto-logs-in since the session exists)
  Browser->>FusionAuth: GET /oauth2/authorize {response_type=token}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  FusionAuth->>Browser: 302 Location: {redirect_uri w/ JWT in redirect URI and no refresh token}<br/>[SessionId HttpOnly w/ domain: example.fusionauth.io]
  Browser->>Forums: GET {redirect_uri w/o JWT in URI}
  Forums->>Browser: (SPA HTML, CSS & JavaScript)

  Note over Browser,Hacker: Start session
  Browser->>Forums: AJAX POST /api/start-session<br/>(JWT from redirect_uri)
  Forums->>Forums: Optionally store JWT in session
  Forums->>Browser: [SessionId HttpOnly w/ domain: forums.example.com]

  Note over Browser,Hacker: Forum load
  Browser->>Forums: AJAX GET /api/load-posts<br/>[SessionId HttpOnly w/ domain: forums.example.com]
  Forums->>Forums: Session extended
  Forums->>Browser: (Forum posts)

  Note over Browser,Hacker: Attack vectors

  Note over Browser,Hacker: Stolen session id
  Hacker->>Store: GET /api/load-shopping-cart<br/>[SessionId HttpOnly w/ domain: store.example.com]
  Store->>Store: Session extended
  Store->>Hacker: (Shopping cart contents)
```

## Explanation[#](#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 user clicks the login link and the browser navigates away from the single-page application to FusionAuth's OAuth 2 interface. The browser requests the OAuth 2 login page from FusionAuth with a `response_type` of `token` indicating that it is using the implicit grant
6.  FusionAuth responds with the HTML, CSS & JavaScript of the login page (including the form)
7.  The user inputs their credentials and clicks the submit button. The browser `POST`s the form data to FusionAuth
8.  FusionAuth returns a redirect to the application backend's OAuth 2 `redirect_uri`. This redirect includes the access token (in our case a JWT) 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
9.  The browser requests the application backend's OAuth `redirect_uri`. This request does not include the JWT because it is after the `#` in the URL, which means the browser will not send it in the HTTP request to the application backend
10.  The application backend responds with the HTML, CSS & JavaScript of the application. During this step, the browser will initialize the single-page application. As part of the initialization of the application, the JWT will be pulled from the current URL of the browser
11.  The browser extracts the JWT from the current URL and then sends it via an AJAX `POST` to the Start Session API in application backend
12.  The application backend creates a server-side session and stores the JWT in it
13.  The application backend responds with a 200 and an HttpOnly cookie that contains the session id of the server-side session
14.  The browser requests the user's shopping cart from the application backend via AJAX and includes the session cookie
15.  The application backend looks up the server-side session associated with the session cookie and extends the expiration date
16.  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 shopping cart from the database (or similar location). Finally, the application backend returns the user's shopping cart (usually as JSON)
17.  A while later, the user's server-side session 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 session cookie to the application backend
18.  The application backend attempts to load the server-side session associated with session cookie and realizes it is expired and returns a 401 indicating the user is no longer logged in
19.  The application might automatically redirect the browser back to FusionAuth's OAuth 2 interface or wait until the user clicks the login link. Regardless, the browser requests the OAuth 2 login page from FusionAuth with a `response_type` of `token` indicating that it is using the implicit grant
20.  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 a new JWT from FusionAuth in the URI
21.  The browser requests the application backend's OAuth `redirect_uri`. This request does not include the JWT because it is after the `#` in the URL, which means the browser will not send it in the HTTP request to the application backend
22.  The application backend responds with the HTML, CSS & JavaScript of the application. During this step, the browser will initialize the single-page application. As part of the initialization of the application, the JWT will be pulled from the current URL of the browser
23.  The browser extracts the JWT from the current URL and then sends it via an AJAX `POST` to the Start Session API in application backend
24.  The application backend creates a server-side session and stores the JWT in it
25.  The application backend responds with a 200 and an HttpOnly cookie that contains the session id of the server-side session
26.  The browser requests the user's shopping cart from the application backend via AJAX and includes the session cookie
27.  The application backend looks up the server-side session associated with the session cookie and extends the expiration date
28.  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 shopping cart from the database (or similar location). Finally, the application backend returns the user's shopping cart (usually as JSON)
29.  The browser requests the forums single-page application from the application backend. This is a standard SSO login that is fully supported by FusionAuth
30.  The application backend responds with the HTML, CSS & JavaScript of the application
31.  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
32.  The application backend responds with a 404 indicating the user is not logged in
33.  The application might automatically redirect the browser back to FusionAuth's OAuth 2 interface or wait until the user clicks the login link. Regardless, the browser requests the OAuth 2 login page from FusionAuth with a `response_type` of `token` indicating that it is using the implicit grant
34.  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 a new JWT from FusionAuth in the URI
35.  The browser requests the application backend's OAuth `redirect_uri`. This request does not include the JWT because it is after the `#` in the URL, which means the browser will not send it in the HTTP request to the application backend
36.  The application backend responds with the HTML, CSS & JavaScript of the application. During this step, the browser will initialize the single-page application. As part of the initialization of the application, the JWT will be pulled from the current URL of the browser
37.  The browser extracts the JWT from the current URL and then sends it via an AJAX `POST` to the Start Session API in application backend
38.  The application backend creates a server-side session and stores the JWT in it
39.  The application backend responds with a 200 and an HttpOnly cookie that contains the session id of the server-side session
40.  The browser requests the user's forum posts from the application backend via AJAX and includes the session cookie
41.  The application backend looks up the server-side session associated with the session cookie and extends the expiration date
42.  The application backend loads the User object (or JWT) from the session associated with the session cookie. 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 (usually as JSON)
43.  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
44.  The application backend looks up the server-side session associated with the session cookie and extends the expiration date
45.  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 contents (usually as JSON)

## Security considerations[#](#security-considerations)

This workflow is less secure than other workflows because the JWT is available to JavaScript during the start session process. While this is a small window of time, it is still possible that malicious JavaScript running in the application could gain access to the JWT. If an attacker can inject JavaScript into the page, they can begin stealing user's JWTs. The attacker might introduce JavaScript into an open source project through obfuscated code or through a backend exploit of some kind. Many platforms like Wordpress also allow plugins to add JavaScript includes to websites as well. Therefore, ensuring that your JavaScript is secure can be extremely difficult.

This workflow might still be a good solution for some applications. Developers should just weigh the risks associated with JWTs accessible to JavaScript versus the other workflows we have documented.

Additionally, since this workflow does not use refresh tokens (and cannot use refresh tokens according to the specification). Therefore, when the user's session expires, they will need to log into the application again. This could be an automatic login, but it still requires the browser to take the user to the FusionAuth OAuth interface.

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

Here are the FusionAuth APIs used in this example:

*   [/oauth2/authorize](/docs/apis/oauth/authorize)