> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-docs-event-stream-action-templates.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Auth0 SDK for single page applications using Authorization Code Grant Flow with PKCE.

# Auth0 Single Page App SDK Code Grant Flow with PKCE.

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

The Auth0 Single Page App SDK is a new JavaScript library for implementing authentication and authorization in single page apps (SPA) with Auth0. It provides a high-level API and handles a lot of the details so you can secure SPAs using best practices while writing less code.

The Auth0 SPA SDK handles grant and protocol details, token expiration and renewal, as well as token storage and caching. Under the hood, it implements [Universal Login](/docs/authenticate/login/auth0-universal-login) and the [Authorization Code Grant Flow with PKCE](/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce).

The [library](https://github.com/auth0/auth0-spa-js) and [API documentation](https://auth0.github.io/auth0-spa-js/) are hosted on GitHub.

If you encounter any problems or errors when using the new JavaScript SDK, please [read the FAQ](https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md) to see if your issue is covered there.

## Installation

You have a few options for using the Auth0 SPA SDK in your project:

* From the CDN: `<script src="https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js"></script>`. For more information, read the [FAQ](https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md#how-to-use-from-a-cdn).
* With [npm](https://npmjs.org): `npm install @auth0/auth0-spa-js`
* With [yarn](https://yarnpkg.com): `yarn add @auth0/auth0-spa-js`

## Getting Started

### Create the client

First, you'll need to create a new instance of the `Auth0Client` client object. Create the `Auth0Client` instance before rendering or initializing your application. You can do this using either the async/await method, or with promises. You should only create one instance of the client.

Using `createAuth0Client` does a couple of things automatically:

* It creates an instance of `Auth0Client`.
* It calls `getTokenSilently` to refresh the user session.
* It suppresses all errors from `getTokenSilently`, except `login_required`.

#### Use async/await

export const codeExample1 = `import { createAuth0Client } from '@auth0/auth0-spa-js';

const auth0 = await createAuth0Client({
  domain: '{yourDomain}',
  clientId: '{yourClientId}'
});`;

<AuthCodeBlock children={codeExample1} language="jsx" />

#### Use promises

export const codeExample2 = `createAuth0Client({
  domain: '{yourDomain}',
  clientId: '{yourClientId}'
}).then(auth0 => {
  //...
});`;

<AuthCodeBlock children={codeExample2} language="jsx" />

You can also create the client directly using the `Auth0Client` constructor. This can be useful if you want to:

* Bypass the call to `getTokenSilently` on initialization.
* Do custom error handling.
* Initialize the SDK in a synchronous way.

export const codeExample3 = `import { Auth0Client } from '@auth0/auth0-spa-js';

const auth0 = new Auth0Client({
  domain: '{yourDomain}',
  clientId: '{yourClientId}'
});`;

<AuthCodeBlock children={codeExample3} language="jsx" />

### Login and get user info

Next, create a button users can click to start logging in:

`<button id="login">Click to Login</button>`

Listen for click events on the button you created. When the event occurs, use the desired login method to authenticate the user (`loginWithRedirect()` in this example). After the user is authenticated, you can retrieve the user profile with the `getUser()` method.

#### Use async/await

```jsx lines theme={null}
document.getElementById('login').addEventListener('click', async () => {
  await auth0.loginWithRedirect({
    authorizationParams: {
      redirect_uri: 'http://localhost:3000/'
    }
  });
  //logged in. you can get the user profile like this:
  const user = await auth0.getUser();
  console.log(user);
});
```

#### Use promises

```jsx lines theme={null}
document.getElementById('login').addEventListener('click', () => {
  auth0.loginWithRedirect({
    authorizationParams: {
      redirect_uri: 'http://localhost:3000/'
    }
  }).then(token => {
    //logged in. you can get the user profile like this:
    auth0.getUser().then(user => {
      console.log(user);
    });
  });
});
```

### Call an API

To call your API, start by getting the user's <Tooltip tip="Access Token: Authorization credential, in the form of an opaque string or JWT, used to access an API." cta="View Glossary" href="/docs/glossary?term=Access+Token">Access Token</Tooltip>. Then use the Access Token in your request. In this example, the `getTokenSilently` method is used to retrieve the Access Token:

`<button id="callApi">Call an API</button>`

#### Use async/await

```jsx lines theme={null}
document.getElementById('callApi').addEventListener('click', async () => {
  const accessToken = await auth0.getTokenSilently();
  const result = await fetch('https://exampleco.com/api', {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + accessToken
    }
  });
  const data = await result.json();
  console.log(data);
});
```

#### Use promises

```jsx lines theme={null}
document.getElementById('callApi').addEventListener('click', () => {
  auth0
    .getTokenSilently()
    .then(accessToken =>
      fetch('https://exampleco.com/api', {
        method: 'GET',
        headers: {
          Authorization: 'Bearer ' + accessToken
        }
      })
    )
    .then(result => result.json())
    .then(data => {
      console.log(data);
    });
});
```

### Logout

Add a button users can click to logout:

`<button id="logout">Logout</button>`

```jsx lines theme={null}
$('#logout').click(async () => {
  auth0.logout({
    logoutParams: {
      returnTo: 'http://localhost:3000/'
    }
  });
});
```

### Change storage options

The Auth0 SPA SDK stores tokens in memory by default. However, this does not provide persistence across page refreshes and browser tabs. Instead, you can opt-in to store tokens in local storage by setting the `cacheLocation` property to `localstorage` when initializing the SDK. This can help to mitigate some of the effects of browser privacy technology that prevents access to the Auth0 <Tooltip tip="Session Cookie: Entity that, when present, allows the user to be considered authenticated." cta="View Glossary" href="/docs/glossary?term=session+cookie">session cookie</Tooltip> by storing Access Tokens for longer.

<Warning>
  Storing tokens in browser local storage provides persistence across page refreshes and browser tabs. However, if an attacker can achieve running JavaScript in the SPA using a cross-site scripting (XSS) attack, they can retrieve the tokens stored in local storage. A vulnerability leading to a successful XSS attack can be either in the SPA source code or in any third-party JavaScript code (such as bootstrap, jQuery, or Google Analytics) included in the SPA.

  Read more about [token storage](/docs/secure/tokens/token-best-practices).
</Warning>

export const codeExample4 = `const auth0 = await createAuth0Client({
  domain: '{yourDomain}',
  clientId: '{yourClientId}',
  cacheLocation: 'localstorage'
});`;

<AuthCodeBlock children={codeExample4} language="jsx" />

### Use rotating Refresh Tokens

The Auth0 SPA SDK can be configured to use [rotating Refresh Tokens](/docs/secure/tokens/refresh-tokens/refresh-token-rotation) to get new access tokens silently. These can be used to bypass browser privacy technology that prevents access to the Auth0 session cookie when authenticating silently, as well as providing built-in reuse detection.

Configure the SDK to do this by setting `useRefreshTokens` to `true` on initialization:

export const codeExample5 = `const auth0 = await createAuth0Client({
  domain: '{yourDomain}',
  clientId: '{yourClientId}',
  useRefreshTokens: true
});

// Request a new access token using a refresh token
const token = await auth0.getTokenSilently();`;

<AuthCodeBlock children={codeExample5} language="jsx" />

<Tooltip tip="Refresh Token: Token used to obtain a renewed Access Token without forcing users to log in again." cta="View Glossary" href="/docs/glossary?term=Refresh+Tokens">Refresh Tokens</Tooltip> will also need to be [configured for your tenant](/docs/secure/tokens/refresh-tokens/configure-refresh-token-rotation) before they can be used in your SPA.

Once configured, the SDK will request the `offline_access` scope during the authorization step. Furthermore, `getTokenSilently` will then call the `/oauth/token` endpoint directly to exchange refresh tokens for access tokens.
The SDK will obey the storage configuration when storing refresh tokens. If the SDK has been configured using the default in-memory storage mechanism, refresh tokens will be lost when refreshing the page.

## Usage

Below are examples of usage for various methods in the SDK. Note that jQuery is used in these examples.

### Login with redirect

Redirect to the `/authorize` endpoint at Auth0, starting the [Universal Login](/docs/authenticate/login/auth0-universal-login) flow:

```jsx lines theme={null}
$('#loginRedirect').click(async () => {
  await auth0.loginWithRedirect({
    authorizationParams: {
      redirect_uri: 'http://localhost:3000/'
    }
  });
});
```

### Login with popup

Use a popup window to log in using the <Tooltip tip="Universal Login: Your application redirects to Universal Login, hosted on Auth0's Authorization Server, to verify a user's identity." cta="View Glossary" href="/docs/glossary?term=Universal+Login">Universal Login</Tooltip> page:

```jsx lines theme={null}
$('#loginPopup').click(async () => {
  await auth0.loginWithPopup();
});
```

If the user takes longer than the default timeout of 60 seconds to complete the authentication flow, the authentication will be interrupted, and you will need to catch the error in your code to either:

Suggest that the user retry and close the popup manually using `error.popup.close`:

```jsx lines theme={null}
$('#loginPopup').click(async () => {
  try {
    await auth0.loginWithPopup();
  } catch {error}
  if (error instanceof auth0.PopupTimeoutError) {
    // custom logic to inform user to retry
    error.popup.close();
  }
});
```

Or create a custom `popup` option in the `options` object:

```jsx lines theme={null}
$('#loginPopup').click(async () => {
  const popup = window.open(
    '',
    'auth0:authorize:popup',
    'left=100,top=100,width=400,height=600,resizable'
  );
  try {
    await auth0.loginWithPopup({ popup });
  } catch {error}
  if (error instanceof auth0.PopupTimeoutError) {
    // custom logic to inform user to retry
    error.popup.close();
  }
});
```

### Login with redirect callback

When the browser is redirected from Auth0 back to your SPA, `handleRedirectCallback` must be called in order to complete the login flow:

```jsx lines theme={null}
$('#loginRedirectCallback').click(async () => {
  await auth0.handleRedirectCallback();
});
```

### Get Access Token with no interaction

Get a new Access Token silently using either a hidden iframe and `prompt=none`, or by using a rotating Refresh Token. Refresh Tokens are used when `useRefreshTokens` is set to `true` when configuring the SDK.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Getting an Access Token silently without using Refresh Tokens will not work in browsers that block third-party cookies, such as Safari and Brave. To learn more about the custom domain workaround, read [Troubleshoot Renew Tokens When Using Safari](https://support.auth0.com/center/s/article/troubleshoot-auth0-token-renewal-issues-in-safari-with-itp-enabled).
</Callout>

If in-memory storage (the default) and refresh tokens are used, new tokens are retrieved using a web worker on supported browsers:

```jsx lines theme={null}
$('#getToken').click(async () => {
  const token = await auth0.getTokenSilently();
});
```

The `getTokenSilently()` method requires you to have **Allow Skipping User Consent** enabled in your [API Settings in the Dashboard](https://manage.auth0.com/#/apis). Additionally, user consent [cannot be skipped on 'localhost'](/docs/get-started/applications/third-party-applications/user-consent-and-third-party-applications).

### Get Access Token with popup

Access Tokens can also be retrieved using a popup window. Unlike `getTokenSilently`, this method of retrieving an Access Token will work in browsers where third-party cookies are blocked by default:

```jsx lines theme={null}
$('#getTokenPopup').click(async () => {
  const token = await auth0.getTokenWithPopup({
    authorizationParams: {
      audience: 'https://mydomain/api/',
      scope: 'read:rules'
    }
  });
});
```

### Get Access Token for a different audience

Options may be passed to `getTokenSilently` that get an Access Token with a different <Tooltip tip="Audience: Unique identifier of the audience for an issued token. Named aud in a token, its value contains the ID of either an application (Client ID) for an ID Token or an API (API Identifier) for an Access Token." cta="View Glossary" href="/docs/glossary?term=audience">audience</Tooltip> and scope of that which was requested at user authentication time.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  This only works when not using Refresh Tokens (`useRefreshTokens: false`), as a Refresh Token is bound to the particular audience and scope that was requested at user authentication time.
</Callout>

```jsx lines theme={null}
$('#getToken_audience').click(async () => {
  const differentAudienceOptions = {
    authorizationParams: {
      audience: 'https://mydomain/another-api/',
      scope: 'read:rules',
      redirect_uri: 'http://localhost:3000/callback.html'
    }
  };
  const token = await auth0.getTokenSilently(differentAudienceOptions);
});
```

### Get user

You can get the authenticated user's profile data by calling the `getUser` method:

```jsx lines theme={null}
$('#getUser').click(async () => {
  const user = await auth0.getUser();
});
```

### Get ID Token claims

You can get the claims of the authenticated user's <Tooltip tip="ID Token: Credential meant for the client itself, rather than for accessing a resource." cta="View Glossary" href="/docs/glossary?term=ID+Token">ID Token</Tooltip> by calling the `getIdTokenClaims` method:

```jsx lines theme={null}
$('#getIdTokenClaims').click(async () => {
  const claims = await auth0.getIdTokenClaims();
  // if you need the raw id_token, you can access it
  // using the __raw property
  const id_token = claims.__raw;
});
```

### Logout (default)

You can initiate a logout action by calling the `logout` method:

```jsx lines theme={null}
$('#logout').click(async () => {
  auth0.logout({
    logoutParams: {
      returnTo: 'http://localhost:3000/'
    }
  });
});
```

### Logout with no client ID

You can initiate a logout action with no <Tooltip tip="Client ID: Identification value given to your registered resource from Auth0." cta="View Glossary" href="/docs/glossary?term=Client+ID">Client ID</Tooltip> specified by calling the `logout` method and including `clientId: null`:

```jsx lines theme={null}
$('#logoutNoClientId').click(async () => {
  auth0.logout({
    clientId: null,
    logoutParams: {
      returnTo: 'http://localhost:3000/'
    }
  });
});
```

## Learn more

* [Validate Access Tokens](/docs/secure/tokens/access-tokens/validate-access-tokens)
