---
title: How to Sign In with Okta Using Capacitor
description: Learn how to add Okta authentication to your Capacitor app using the OAuth plugin with PKCE support on Android, iOS, and web.
date:
  created: 2026-03-11
  updated: 2026-07-14
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor OAuth: sdks/capacitor/oauth.md
faq: true
---

# How to Sign In with Okta Using Capacitor

[Okta](https://www.okta.com/){:target="_blank"} is a widely used identity platform that powers single sign-on (SSO) and user management for thousands of organizations. If you need to add Okta authentication to a Capacitor app, the [Capacitor OAuth plugin](../../sdks/capacitor/oauth.md) supports the Authorization Code flow with PKCE out of the box. In this guide, you'll learn how to register your app in Okta, implement sign-in and sign-out, manage tokens, and retrieve user profile data on Android, iOS, and web. This is also an alternative to [Ionic Auth Connect](https://ionic.io/docs/auth-connect/okta){:target="_blank"} for teams looking for a lightweight, open approach.

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## Bonus: Video Tutorial and Demo App

This tutorial shows the full OAuth plugin flow in a Capacitor app, including PKCE, redirect handling, token lifecycle management, and native/web behavior. The same architecture applies directly to Okta with provider-specific configuration values.

<div style="margin-top: 2rem;">
  <iframe
    width="100%"
    height="450px"
    src="https://www.youtube-nocookie.com/embed/Cr1dJNN6Urw?rel=0&modestbranding=1"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen
  ></iframe>
</div>

- **[OAuth Demo App](https://github.com/capawesome-team/capacitor-oauth-demo){:target="_blank"}** — A practical reference implementation you can adapt to Okta by replacing issuer and client configuration.

## Prerequisites

Before getting started, make sure you have:

- An **Okta developer account**. You can [sign up for a free Okta developer account](https://developer.okta.com/signup/){:target="_blank"} if you don't already have one.
- A **Capacitor app** with the [Capacitor OAuth plugin](../../sdks/capacitor/oauth.md) installed. For installation instructions, head over to the [Installation](../../sdks/capacitor/oauth.md/#installation) section in the plugin documentation.

## Setting Up Okta

### Creating an Application

To get started, you need to register a new application in the Okta Admin Console:

1. Sign in to your [Okta Admin Console](https://login.okta.com/){:target="_blank"}.
2. Go to **Applications** > **Applications** and click **Create App Integration**.
3. Select **OIDC - OpenID Connect** as the sign-in method.
4. Choose **Native Application** as the application type and click **Next**.
5. Give your application a **Name** (e.g. `My Capacitor App`).
6. Under **Grant type**, make sure **Authorization Code** and **Refresh Token** are selected.
7. Configure the **Sign-in redirect URIs** and **Sign-out redirect URIs** (see sections below).
8. Under **Assignments**, choose the appropriate controlled access option for your use case.
9. Click **Save**.
10. On the application's **General** tab, note the **Client ID** and your **Okta domain** (e.g. `dev-123456.okta.com`). You'll need both values later.

### Configuring Sign-in Redirect URIs

Add a redirect URI for each platform your app supports. You can add multiple URIs in the **Sign-in redirect URIs** section of your application settings.

**Android and iOS**: Use a custom scheme based on your app's package name or bundle identifier:

```
com.example.app://oauth/callback
```

**Web**: Use your local development URL:

```
http://localhost:3000/oauth/callback
```

### Configuring Sign-out Redirect URIs

Similarly, configure the **Sign-out redirect URIs** so Okta knows where to send users after they log out.

**Android and iOS**:

```
com.example.app://oauth/logout
```

**Web**:

```
http://localhost:3000/oauth/logout
```

## Implementing Authentication

In the examples below, replace `{okta-domain}` with your Okta domain (e.g. `dev-123456.okta.com`) and `{client-id}` with your application's **Client ID**.

### Signing In

Kick off the OAuth flow using the [`login(...)`](../../sdks/capacitor/oauth.md#login) method. The plugin fetches Okta's OpenID Connect discovery document automatically and takes care of the PKCE challenge:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';

const login = async () => {
  const result = await Oauth.login({
    issuerUrl: 'https://{okta-domain}/oauth2/default',
    clientId: '{client-id}',
    redirectUrl: 'com.example.app://oauth/callback',
    scopes: ['openid', 'profile', 'email', 'offline_access'],
  });
  console.log('Access token:', result.accessToken);
  console.log('ID token:', result.idToken);
  console.log('Refresh token:', result.refreshToken);
};
```

Adding `offline_access` to the scopes ensures you receive a refresh token.

### Handling the Redirect Callback (Web)

On the web, [`login(...)`](../../sdks/capacitor/oauth.md#login) redirects the user to Okta's hosted login page. Once the user authenticates, Okta redirects them back to your app. Call [`handleRedirectCallback()`](../../sdks/capacitor/oauth.md#handleredirectcallback) when the page loads to finish the token exchange:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';
import { Capacitor } from '@capacitor/core';

const handleRedirectCallback = async () => {
  if (Capacitor.getPlatform() !== 'web') {
    return;
  }
  const url = new URL(window.location.href);
  if (!url.searchParams.has('code')) {
    return;
  }
  const result = await Oauth.handleRedirectCallback();
  console.log('Access token:', result.accessToken);
};

handleRedirectCallback();
```

On Android and iOS, the redirect is handled natively, so this step only applies to the web.

### Refreshing the Access Token

Access tokens are short-lived by design. Use the [`refreshToken(...)`](../../sdks/capacitor/oauth.md#refreshtoken) method to obtain a fresh access token without prompting the user to sign in again:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';

const refreshToken = async () => {
  const result = await Oauth.refreshToken({
    issuerUrl: 'https://{okta-domain}/oauth2/default',
    clientId: '{client-id}',
    refreshToken: 'YOUR_REFRESH_TOKEN',
  });
  console.log('New access token:', result.accessToken);
};
```

### Decoding the ID Token

Extract user profile claims from the ID token with the [`decodeIdToken(...)`](../../sdks/capacitor/oauth.md#decodeidtoken) method:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';

const decodeIdToken = async () => {
  const result = await Oauth.decodeIdToken({
    token: 'YOUR_ID_TOKEN',
  });
  console.log('Name:', result.payload.name);
  console.log('Email:', result.payload.email);
};
```

The token is decoded locally on the device. If you need server-side verification, validate the JWT on your backend instead.

### Signing Out

Terminate the session by calling the [`logout(...)`](../../sdks/capacitor/oauth.md#logout) method:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';

const logout = async () => {
  await Oauth.logout({
    issuerUrl: 'https://{okta-domain}/oauth2/default',
    idToken: 'YOUR_ID_TOKEN',
    postLogoutRedirectUrl: 'com.example.app://oauth/logout',
  });
};
```

## Fetching the User Profile

You can also retrieve the authenticated user's profile directly from Okta's `/userinfo` endpoint using the access token:

```typescript
import { Oauth } from '@capawesome-team/capacitor-oauth';

const login = async () => {
  const result = await Oauth.login({
    issuerUrl: 'https://{okta-domain}/oauth2/default',
    clientId: '{client-id}',
    redirectUrl: 'com.example.app://oauth/callback',
    scopes: ['openid', 'profile', 'email', 'offline_access'],
  });

  const response = await fetch(
    'https://{okta-domain}/oauth2/default/v1/userinfo',
    {
      headers: {
        Authorization: `Bearer ${result.accessToken}`,
      },
    },
  );
  const user = await response.json();
  console.log('Name:', user.name);
  console.log('Email:', user.email);
};
```

## FAQ

### Which platforms does this work on?

The [Capacitor OAuth plugin](../../sdks/capacitor/oauth.md) runs the Authorization Code flow with PKCE on Android, iOS, and the web from a single codebase. On Android and iOS the flow completes natively; on the web you finish it by calling [`handleRedirectCallback()`](../../sdks/capacitor/oauth.md#handleredirectcallback) on page load.

### Do I need an Okta client secret?

No. You register the app as a **Native** application, a public client that authenticates with PKCE rather than a secret — the right choice for mobile apps, where a client secret can't be stored safely.

### How do I keep the user signed in after they close the app?

Request the `offline_access` scope in [`login(...)`](../../sdks/capacitor/oauth.md#login) to receive a refresh token, store it securely (for example with the [Capacitor Secure Preferences plugin](../../sdks/capacitor/secure-preferences.md)), and exchange it for a fresh access token when needed. See [Refreshing the Access Token](#refreshing-the-access-token).

### How do I get the user's profile information?

Call your Okta org's `/userinfo` endpoint with the access token, as shown in [Fetching the User Profile](#fetching-the-user-profile). Make sure you requested the `profile` and `email` scopes during login.

### Can I use the same approach for other identity providers?

Yes. Because the plugin speaks standard OpenID Connect, the same flow works with [Auth0](./how-to-sign-in-with-auth0-using-capacitor.md), [Microsoft Entra ID](./how-to-sign-in-with-azure-entra-id-using-capacitor.md), or any compliant provider. For provider-specific SDKs, see [Google Sign-In](./how-to-sign-in-with-google-using-capacitor.md) and [Apple Sign-In](./how-to-sign-in-with-apple-using-capacitor.md).

## Related Posts

- [Alternative to the Ionic Auth Connect Plugin](./alternative-to-ionic-auth-connect-plugin.md)
- [Announcing the Capacitor OAuth Plugin](./announcing-the-capacitor-oauth-plugin.md)
- [How to Sign In with Apple Using Capacitor](./how-to-sign-in-with-apple-using-capacitor.md)
- [How to Sign In with Auth0 Using Capacitor](./how-to-sign-in-with-auth0-using-capacitor.md)

## Try Capawesome Cloud

If you're building Capacitor apps, [Capawesome Cloud](/){:target="_blank"} can help you ship faster with cloud-based native builds, over-the-air updates, and more.

[Try Capawesome Cloud Free](/){ .md-button .md-button--primary }

## Conclusion

Request the `offline_access` scope on the first sign-in if users should stay signed in between app launches, and refresh the access token instead of sending them back to the Okta login page. For a web build, call [`handleRedirectCallback()`](../../sdks/capacitor/oauth.md#handleredirectcallback) on page load: the [Capacitor OAuth plugin](../../sdks/capacitor/oauth.md) finishes the flow natively on Android and iOS, but on the web that call completes the token exchange.

For more details, check out the full [API Reference](../../sdks/capacitor/oauth.md#api) for all available methods and options. You might also find the [How to Sign In with Azure Entra ID Using Capacitor](./how-to-sign-in-with-azure-entra-id-using-capacitor.md) guide helpful if you need to support multiple identity providers. If you have questions or run into issues, feel free to [create an issue](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose){:target="_blank"} on [GitHub](https://github.com/capawesome-team/capacitor-plugins){:target="_blank"} or join the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}.

To stay up to date with the latest news, subscribe to the [Capawesome newsletter](/newsletter/){:target="_blank"}.
