How Authentication and Authorization Work for SPAs
Tags: security, webdev, beginners, javascript
Published: 2023-04-12 15:02:01

---
title: How Authentication and Authorization Work for SPAs
published: true
description: Authentication and authorization in public clients like single-page applications can be complicated! In this post, we'll walk through the Authorization Code flow with Proof Key for Code Exchange extension to better understand how it works and what do with the auth tokens you get back.
tags: security,webdev,beginners,javascript
canonical_url: https://developer.okta.com/blog/2023/04/04/spa-auth-tokens
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzv233goa1573p63tiyd.jpg
---

Adding authentication to public clients such as Single Page Applications (SPA) and JavaScript applications can be a source of confusion. Identity Providers like Okta try to help you via multiple support systems. Still, it can feel like a lot of work. Especially since you’re responsible for way more than authentication alone in the applications you work on!

As part of authentication, your client application makes multiple calls to an authorization server, and you get back several strings, which are tokens. Let’s demystify what’s going on behind the scenes and closely examine what those tokens are and how you use them within your client application.

**Table of Contents**
* [Authentication and Authorization using OAuth 2.0 + OpenID Connect (OIDC)](#oauth-oidc)
* [OAuth 2.0 + OIDC for JavaScript clients and SPA](#oauth-oidc-spa)
* [Auth tokens in OAuth 2.0 + OIDC](#injector)
  * [ID token](#providing-injectors)
  * [Access token](#injection-tokens)
  * [Refresh token](#configure-providers)
* [Create an OAuth 2.0 + OIDC Compliant Authorization server](#configure-useclass)
* [View the well-known endpoint for OIDC Discovery](#configure-useexisting)
* [Debug the authorization code and token requests](#configure-usevalue)
* [Add authentication to your SPA](#configure-usefactory)
* [Use auth tokens in SPAs]()
  * [How to use the access token]()
  * [How to use the ID token]()
* [Learn more about OAuth, OIDC, tokens, and authentication best practices](#learn-more)

There’s a [companion video for this post](https://youtu.be/UZQ_ke8BuRU) if you want to check out how to run a SPA sample application, look at browser storage, and inspect network calls.

{% embed https://youtu.be/UZQ_ke8BuRU %}

## Authentication and Authorization using OAuth 2.0 + OpenID Connect (OIDC) <a name="oauth-oidc"></a>

OAuth 2.0 with OIDC is the best practice for adding authentication and authorization to your software applications. Authentication verifies the identity of who you claim to be, and authorization verifies you have access to data you want to see or actions you want to perform. It is lightweight with less effort to set up and use than Security Assertion Markup Language (SAML), an alternate authentication and authorization mechanism that pre-existing systems may use. For newer systems, you’ll want to use OAuth 2.0 + OIDC.

![OAuth and OIDC logos](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/oauth-oidc-logos-9fbab6236e55b4441c5f404ebe8224a3ae3d77db0e65e87eb184afe52bf3829c.jpg)

OAuth 2.0 handles authorization to resources, such as when your front-end application gets data from a backend API by making an HTTP request. OAuth 2.0 standards have the flexibility to support authorization across your entire application system through different flows and grant types. For example, different OAuth 2.0 flows support JavaScript-based front-end applications, for your APIs and back-end services to communicate, and even for IOT devices in your home automation system. OpenID Connect (OIDC) is an identity layer on OAuth 2.0 that provides standardized identity information.

## OAuth 2.0 + OIDC for JavaScript clients and SPA <a name="oauth-oidc-spa"></a>

SPAs and other JavaScript front-ends are public clients, which means they can’t maintain secret information for authorization, unlike a confidential client. A confidential client (such as a traditional server-rendered web app) can keep super secret information such as a Client Secret for authorization. In both application types, we should use a flow called Authorization Code and an extension to the flow called Proof Key for Code Exchange (PKCE).

Let’s see how this flow works step-by-step following what happens when a cute dinosaur named Sunny prepares to sign in to the “Rawr” app.

![Sunny, a cute smiley dinosaur, is ready to sign in to the Rawr app](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-1-sign-in-start-4159efa8c84ef5704b1ed1524d7eba447f1f660548356cef93d7bb283e69d448.jpg)

The client application starts the process and generates a random, long string — the code verifier. Then it creates a code challenge from the code verifier. This step is part of the PKCE extension.

![The client application processing the sign-in to create a code verifier and code challenge](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-2-client-pkce-d6db0e35bd725c0082833949cc8a8d6af053e6b8e65860263613bb6d9329fa4d.jpg)

The client application holds on to the code verifier. It then makes an authorization code request to your authorization server, in this case, represented by Okta. The authorization code request includes the code challenge along with some critical pieces of information, such as
* Client ID
* Scopes, including at least the following:
  *  `openid` required for specifying the app uses OIDC in verifying the user's identity
  *  `profile` for accessing the user's profile information, such as their name
  *  `offline_access` to request a Refresh Token as part of this call
* Redirect URI for the callback location within the client application when sign-in is complete
* A random alphanumeric string for preventing Cross-Site Request Forgery (CSRF)

![The client application making an authorization code request to the authorization server, Okta, with the information mentioned above](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-3-authorization-code-request-1d7c92924115429ce915ddf698d3ad2cbf8d0957e2226cf0246e135694328eca.jpg)

The authorization server redirects the user, Sunny, to a web page it hosts to sign in and provide authorization consent.

![The client application redirects to Okta hosted sign-in screen, and Sunny authenticates and grant consent](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-4-authorization-code-redirect-fd98b56e9431674999d2cb065035cff26dc71e7907c98928a4ed28facede93c3.jpg)

Yay! Sign-in success! The authorization server returns an authorization code to the client application. 

![The authorization server redirects back to the client application and returns an authorization code](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-5-authorization-code-response-f0a267c5727137c55bb048030e0537e3d880062121f5bdbd53b5cef8c0b648e4.jpg)

Now that there's an authorization code, the client application makes a request to the token endpoint of the authorization server and sends the authorization code and the code verifier. 

![The client making a token request and sending the information listed above in the payload](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-6-token-request-c26655a9ea063c93326cc39e093bbca74a2d372348c6ea068955cd59f5ccdced.jpg)

To process the token endpoint request the authorization server ensures the authorization code is still valid and that the code verifier matches the code challenge sent in the authorization request. This step is part of the PKCE extension.

![The authorization server verifying the authorization code and the code verifier](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-7-token-pkce-2e9ff79af70f2a5dea2861f8342c68e2bbbeb2a27fda39f9ff166c463a11a3a7.jpg)

Yay! Success! The authorization server returns three tokens:
1. Access token
2. ID token
3. Refresh token

![The authorization server responds to the token request with three tokens](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-8-token-response-3dbe6c1ad04169e977c5276c08fa1fd507dabab293e735393ebf88f010b9adc2.jpg)

Now that Sunny successfully signed in, they can continue their rawring lessons!

![A happy dinosaur getting ready to start a lesson in how to rawr](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/flow-9-signed-in-bb8edf5bdb04a69acd21f07769e80715d801339a21dc0d4f91a764b9a6c10e81.jpg)

Now that you have these three tokens let's better understand what each one is for.

## Auth tokens in OAuth 2.0 + OIDC <a name="oauth-oidc-tokens"></a>
These three tokens provide crucial information about your identity, access to resources, and the ability to stay authenticated securely.

### ID token <a name="oauth-oidc-id-token"></a>
The ID token is about the user, so information about Sunny in this case. This authentication token is returned from the OpenID Connect layer. It contains standardized identity information such as email, name, and issuing party, called claims. The ID token may include extra claims for information critical to Sunny's identity, such as their dinosaur family, which is a Smileasaur of course.

### Access token <a name="oauth-oidc-access-token"></a>
The access token is a key that grants access to data or to perform an action. This authorization token is returned from the OAuth layer. The token has metadata about the token itself, such as the issuing party, information about requested scopes made in the original request, and the expiration time. Access tokens are intentionally short-lived for public clients and are a safety mechanism since it guards access to resources, and it's dangerous if it falls into the wrong hands!

### Refresh token <a name="oauth-oidc-refresh-token"></a>
The Refresh token allows us to exchange it for new, shiny tokens. This optional `offline_access` scope we added in the original authorization code request allows the access token to be short-lived but does not require Sunny to authenticate to get a new token repeatedly. Refresh tokens can be longer-lived than access tokens, but for public clients, the lifetime of Refresh tokens should also be short. We want to make sure bad actors can't get a hold of a Refresh token to gain access to an access token! For public clients like SPA, it's a best practice also to use **Refresh Token rotation**, which improves security by rotating refresh tokens after each use.

While each step of this OAuth flow to get the tokens is critical to ensure a secure authentication and authorization process, let's inspect the two requests in more detail. 

## Create an OAuth 2.0 + OIDC Compliant Authorization server <a name="oauth-oidc-authorization-server"></a>

We'll start by setting up an authorization server in Okta and use the [OpenID Connect Debugger](https://oidcdebugger.com/) tool to inspect the Network requests. This authorization server is OAuth 2.0 and OIDC compliant so we can use it within applications that conform to those specs.

Before you begin, you’ll need a free Okta developer account. Install the [Okta CLI](https://cli.okta.com/) and run `okta register` to sign up for a new account. If you already have an account, run `okta login`. Then, run `okta apps create`. Select the default app name, or change it as you see fit. Choose **Single-Page App** and press **Enter**.

Use `https://oidcdebugger.com/debug` for the Redirect URI and set the Logout Redirect URI to `https://oidcdebugger.com/debug`.

{% collapsible What does the Okta CLI do? %}
The Okta CLI will create an OIDC Single-Page App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. It will also add a trusted origin for `https://oidcdebugger.com/debug`. You will see output like the following when it’s finished:

```markup
Okta application configuration:
Issuer:    https://dev-133337.okta.com/oauth2/default
Client ID: 0oab8eb55Kb9jdMIr5d6
```

**NOTE**: You can also use the Okta Admin Console to create your app. See [Create a Single-Page App](https://developer.okta.com/docs/guides/sign-into-spa/-/create-okta-application/) for more information.
{% endcollapsible %}

Make a note of the `Issuer` and `Client ID`. In the upcoming steps, you'll need those values to configure Okta in your SPA client.

## View the well-known endpoint for OIDC Discovery <a name="oidc-discovery"></a>

Authorization servers have standard, public endpoints for discovery by clients. Let's take a peek at the OIDC discovery document.

The OpenID Connect specification requires a standardized mechanism for client discovery. You can find it at `{Issuer}/.well-known/openid-configuration`. Open up a browser tab to the OIDC discovery endpoint. It is also JSON formatted data that looks something like this:

![Example OIDC well-known discovery document](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/well-known-oidc-2533a86ae2598a4279402b72d9301117178a44cddedcedb9b0a91e40a03bc759.jpg)

In the discovery response, we have the endpoints for the authorization and token requests which we'll use in the following steps. We also have a user info endpoint to query for user information and endpoints to validate the ID token. If you find the property for `claims_supported`, you'll see the claims cover various identifying information about the user. 

## Debug the authorization code and token requests <a name="debug-requests"></a>

We can see this OAuth flow using the [OpenID Connect Debugger](https://oidcdebugger.com/) tool. Open the site on a browser with good developer debugging capabilities. You'll see something like this:

![OIDC debugger site showing form fields](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/oidc-debugger-b98e9dd986bd11faf17738aeaa9a46a7bfa7b01ce8707ae02a37b12770f4e2a0.jpg)

Some fields are pre-populated, which is helpful. Add the other key information needed:
* **Authorize URI (required)** - the authorize endpoint from the discovery doc
* **Client ID (required)** - the Client ID value from Okta CLI
* **Scope (required)** - `openid` is already added for us. Add `profile` and `offline_access` with spaces in between. The form field should look like `openid profile offline_access`.
* **Response type (required)** - select "Use PKCE?" to unlock a few new fields, but everything is auto-populated. The debugger discovers the token endpoint automatically for us. 😎

Open debugging tools in your browser to watch for redirection and network requests. You'll want to make sure you're preserving logs between page refreshes. 

At the bottom of the page, you'll see how the authorization code request is formed. Press **Send Request** to start the flow.

First, you'll authenticate using Okta if you still need to sign in by redirecting to an Okta-hosted sign-in page. Redirecting to the Identity Provider's hosted sign-in page is the best practice for security, so it's also a common practice you'll see across Identity Providers. Completing sign-in redirects you back to the OIDC debugger with a success message. You'll see the authorization code automatically exchanged for tokens.

![Authorization code and tokens returned in the OIDC debugger upon successful sign-in](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/oidc-debugger-success-3c7e9151b566a46426759f4ce131692ffac92fb52aaa578252bc007e19266993.jpg)

The token format for ID tokens is JSON Web Token (JWT). The access token from Okta is also a JWT. JWT is an open standard (RFC 7519) that allows systems to exchange information in JSON format securely. They are compact and safe from modifications, but our tokens contain public information. Don't worry! They are secure, just not confidential. 

You can copy the access token or ID token value from the response and read the contents using a [JWT debugger](https://jwt.io/). 

![JWT inspection in the JWT debugger](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/jwt-inspection-cf8d26b48fc064faf685a943bde66b1a540966fef2a46b2ad7bc8d026e6c070a.jpg)

So this is cool, but what do we do with the tokens in our SPA? Let's bring these concepts into our front-end application.

## Add authentication to your SPA <a name="spa-authentication"></a>

First, we need an Okta OIDC application supporting our SPA's redirect URI. We could edit the existing OIDC debugger application we created previously, or we can use a handy Okta CLI command to set everything up for us and to scaffold out a sample application in our preferred framework.

Use the following command in the terminal to get going quickly. There's a separate command for each Angular, React, and Vue.

**Angular**
```console
okta start angular
```

**React**
```console
okta start react
```

**Vue**
```console
okta start vue
```

Follow the instructions in the terminal to start the application. You should be able to sign in and sign out. Additionally, you can watch network requests for the calls it makes.

We don't have a refresh token by default. You can enable that in the Okta Admin Console. Navigate to the [Okta Developer site](https://developer.okta.com/) and sign in to your Okta organization. In the Admin Console, navigate to **Applications** > **Applications** and select the Okta application for the SPA you created. Edit the settings to enable **Refresh Token**. It automatically adds Refresh Token Rotation. Save your changes.

If you still need to, open the SPA code in your favorite IDE. Depending on your framework, navigate to the `config.js` or `app.config.ts` file. You'll see the OIDC config and property for `scopes` where you will add `offline_access`. Your OIDC config looks something like this:

```json
{
    clientId: CLIENT_ID,
    issuer: ISSUER,
    redirectUri: window.location.origin + '/login/callback',
    scopes: ['openid', 'profile', 'email', 'offline_access']
}
```

Now try rerunning the SPA. If you inspect the network request, you'll see the refresh token too. You can see the tokens by looking at the contents of your local storage too.

## Use auth tokens in SPAs <a name="spa-auth-tokens"></a>

Now that we have these tokens in our SPA, what do we do with them? Let a trusted OIDC library, such as the Okta SDKs, handle all the token requests and refresh them for us. Behind the scenes, the OIDC library is hard at work exchanging tokens.

![Refresh token call to the authorization server and returning new tokens](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/refresh-token-ea6e60a4c0c13a7a02629e0dfce99eefa2ad8254581b0a2330de030cb09d546d.jpg)

We're primarily interested in the contents of the ID token and using the access token.

### How to use the access token <a name="use-access-token"></a>
The access token gives us access to resources. That means we use access tokens in outgoing API requests that our application needs, like for Sunny to get their next rawring lesson.

![Sunny starting up a new lesson makes a call to the Rawr site's /lesson endpoint passing in the access token](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/access-token-83427aaf9873b61d85239e95b02ebb092e869cca0b5295f5f1543578918b7afd.jpg)

You'll send the access token as a Bearer token in the HTTP call's Authorization header. So in the case of Sunny, the outgoing HTTP call looks like this.

```http
GET /lessons
Authorization: Bearer access_token_value
```

You'll want to ensure you're not adding the access token to calls outside your system by maintaining an allowlist of origins that should include the token.

A great way to manage adding the Authorization header and the logic to verify the call matches the allowlist of origins is using an interceptor. Interceptors sit between your application and outgoing (or incoming) HTTP requests. Angular includes the concept of interceptors within the framework, and if you use Axios in Vue or React apps, you can configure interceptors there.

You can see access tokens in action within the SPA you previously created. If you navigate to **Messages** in the application, it will make a call to a resource server and add the access token to the Authorization header. If you want to see the messages request succeed, you'll need to run the resource server in Okta's [samples-nodejs-express-4 repo](https://github.com/okta/samples-nodejs-express-4).

{% embed https://github.com/okta/samples-nodejs-express-4 %}

### How to use the ID token <a name="use-id-token"></a>
Since the ID token contains user identity information, you can use it to start populating your user store and for supporting identifiers you need immediately, such as their name.

![Sunny looking at his profile in the Rawr app where they see their name, dinosaur family, and their avatar](https://developer.okta.com/assets-jekyll/blog/spa-auth-tokens/id-token-2144e239ee905ca526aa9ae4d418e824075d8eb274c4f8c9af3bb5f9553c956d.jpg)

In the SPA you previously created, you can see all your claims if you navigate to **Profile**.

The Okta SDK automatically decodes the ID token so that we can use the claims without decoding the payload ourselves. But before we jump right into using the claims, the Okta SDK first validates the token signature, which helps ensure the token's integrity and that it hasn't been altered. Let's take a quick peek under the covers at what happens during this validation process.

Since this token is a JWT, we can rely on standard validation for JWTs for the validity of the token itself. Next, it will validate some claims in the payload, including:
 * `iss` - the issuer, which should match the original request. In the OIDC Debugger tool, we passed this in as the "Authorize URI"
 * `aud` - the audience, which should match the client ID of your Okta application
 * `nonce` - an arbitrary one-time value that should match the original request. In the OIDC Debugger tool, it was pre-populated for us.
 * `exp` and `iat` - the expiry time and issued at times, respectively. Only non-expired tokens with an expected issue time here, please!

Once you have the access token, you can also make a request to the OIDC Discovery document's User Info endpoint, which may contain more information about the user than what is available in your ID token. You may also need to add any user information and user settings from API calls within your system into your user store since the SPAs we write have complex user information.

## Learn more about OAuth, OIDC, tokens, and authentication best practices <a name="learn-more"></a>
Hopefully, this gives insight into how authentication and authorization with OAuth 2.0 + OpenID Connect work for public clients like SPAs and how each token fits into the landscape.

If you liked this post, you might want to check out the following:
* [The Identity of OAuth Public Clients](https://developer.okta.com/blog/2022/06/01/oauth-public-client-identity)
* [Is the OAuth 2.0 Implicit Flow Dead?](https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead)
* [The Things to Keep in Mind about Auth](https://developer.okta.com/blog/2021/10/29/things-to-keep-in-mind-about-auth)
* [Quick JavaScript Authentication with OktaDev Schematics](https://developer.okta.com/blog/2023/04/04/blog/2022/10/14/quick-javascript-authentication)

Remember to follow us on [Twitter](https://twitter.com/oktadev) and subscribe to our [YouTube channel](https://www.youtube.com/c/OktaDev/) for more exciting content. We also want to hear about what tutorials you want to see. Leave us a comment below.