# Authgear Overview

Authgear is a highly adaptable identity-as-a-service (IDaaS) platform for web and mobile applications

**Authgear** is an authentication & user management solution that makes it very easy for developers to integrate and customize their consumer applications. It includes these features out of the box:

* Zero trust authentication architecture with [OpenID Connect](https://openid.net/developers/how-connect-works/) (OIDC) standard.
* Easy-to-use interfaces for user registration and login, including email, phone, username as login ID, and password, OTP, magic links, etc. for authentication.
* Supports a wide range of identity providers, such as [Google](https://developers.google.com/identity), [Apple](https://support.apple.com/en-gb/guide/deployment/depa64848f3a/web), and [Azure Active Directory](https://azure.microsoft.com/en-gb/products/active-directory/) (AD).
* Supports biometric login on mobile, Passkeys, and Multi-Factor Authentication (MFA) such as SMS/email-based verification and authenticator apps with TOTP.
* A user management portal for password resets, account locking, scheduled deletion or anonymization, and user profile management.
* Single Sign-On (SSO) provides a single unified experience for your customers to log into multiple web/mobile apps, including Web2Web, Web2App, and App2App SSO.
* Enable [SSO with SAML](/get-started/single-sign-on-with-saml) for your users to log into multiple web applications easily.
* [Session management](/authentication-and-access/sessions) with Authgear Portal, and a pre-built setting page for users to control concurrent sessions.
* Customizable UI with a **user-friendly low-code** dashboard.
* Various security features such as audit logs, brute force protection, smart account lockout, password policy, etc.
* APIs for further integration and customization. For example, build your own custom login and sign-up pages from the ground up powered by the [Authentication Flow API](/reference/apis/authentication-flow-api).

Most importantly, you can [get started](https://accounts.portal.authgear.com/signup) **with Authgear for free**.

### Learn about Authgear

Authgear contains the following high-level components:

#### Authenticate on the Web/Mobile App

* **Client App SDKs** - for developers to quickly implement authentication with Auth UI on your web and mobile applications. Check out [Start Building](/get-started/start-building) for tutorials and API References.
* **Auth UI** - the default batteries-included UI for login, signup and the settings page. You can customize the style via the **Portal**, including the CSS and HTML of each page.
* [**Authentication Flow API**](/customization/custom-ui/authentication-flow-api) - for developers to implement their own login, signup and reauthentication UI (e.g. a mobile native view); or to define a customized login, signup and reauth flow.
* [**Use Authgear as OpenID Connect Provider**](/get-started/oidc-provider) - for developers to use Authgear with other software that already supports OIDC login, you can use Authgear as an OpenID Connect Provider.

#### Backend Authentication and Integrations

* [**Backend/API Integration**](/get-started/backend-api) - explains the common approach of using Access Tokens or Cookies (JWT or random string) to authenticate API or HTTP requests.
* [**Admin API**](/reference/apis/admin-api) - allows your backend to interact directly with Authgear for user management purposes.
* [**Events and Hooks**](/customization/events-hooks) - call an external web endpoint or use hosted TypeScript to customize the behaviour of Authgear, e.g. blocking certain types of sign-up, or calling an external endpoint on each login.
* [**User Import API**](/admin-and-operations/user-management/import-users-using-user-import-api) - Import multiple users from another service to your project.
* [**Export User API**](/admin-and-operations/user-management/export-users-using-the-user-export-api) - Export user data from Authgear into a CSV or [ndjson](https://github.com/ndjson/ndjson-spec) file.
* [**Link OAuth Provider using Account Management API**](/customization/custom-ui/manually-link-oauth-provider-using-account-management-api) - Link an OAuth provider to a user's account without AuthUI.

#### Management Portal

* **Authgear Portal** - You can configure your projects, manage users, check out [audit log](/admin-and-operations/monitor/audit-log), or customize the **AuthUI**. See the [5-minute quick start guide](/get-started/5-minute-guide) for Authgear Portal.
* **Analytics Page** - View reports of all users and active users over a specific time interval on the [analytics page](/admin-and-operations/monitor/analytics).

#### Security

* [**Brute-force Protection**](/security/brute-force-protection) - Set account Lockout Policy to safeguard a user account from brute-force login attempts.
* [**Bot Protection**](/security/bot-protection) - Bot protection tools to block automated attackers.
* [**Fraud Protection**](/security/fraud-protection) - Detect and stop fraudulent traffic such as SMS pumping before it drives up your messaging costs.
* [**Password Strength**](/authentication-and-access/authentication/passwords/password-policy) - Learn how to set password strength and how the password strength is calculated.

#### Login Methods

* [**Biometric Login**](/authentication-and-access/authentication/biometric) - Add biometric login to your application.
* [**Two-Factor Authentication (2FA)**](/authentication-and-access/authentication/enable-two-factor-authentication-2fa) - Enable 2FA in your Authgear project.
* [**Email Magic Link Login**](/authentication-and-access/authentication/add-email-magic-link-login) - Allow users to log in without a password using a magic link.
* [**Passkeys Login**](/authentication-and-access/authentication/passkeys) - Set up passkey for your project.
* [**Social Login / Enterprise Login**](/authentication-and-access/social-enterprise-login-providers) - Allow users to log in to your application using their existing account with a social media site or enterprise login provider.

#### Customize User Interface (UI)

* [**Customize Built-in UI**](/customization/built-in-ui/branding) - Customize the look and feel of AuthUI to match your branding.
* [**Language and Localization**](/customization/built-in-ui/languages-and-localization) - Change the language for display texts.

#### User Management

Features for managing your users via Authgear Portal

* [**Create a new account on behalf of a user**](/authentication-and-access/authentication/passwords/how-to-handle-password-while-creating-accounts-for-users) - Create a new account for a user from Authgear Portal.
* [**Account Deletion**](/admin-and-operations/user-management/account-deletion) - Delete a user account from your project.
* [**User Roles and Groups**](/admin-and-operations/user-management/manage-users-roles-and-groups) - Detailed guide on how to use Roles and Groups.
* [**User Profiles**](/integration/user-profiles) - Guides on how to view and manage user profile information.


# 5-Minute Guide

A quick guide on getting started with Authgear

Authgear makes it easy to add user authentication and authorization to any application. In this guide, we'll show you how to get started with Authgear in 5 minutes.

### Video Guide

{% embed url="<https://www.youtube.com/watch?v=gnCXywMZ1Sk>" %}

### 1. Create Authgear account

To start using Authgear in your application, create a free Authgear account on [authgear.com](https://authgear.com).

After you sign up, you'll see the onboarding screen that will guide you through the process of creating your first Authgear project.

### 2. Authgear Projects

An Authgear project is similar to a container that holds all your users, settings, and client applications. You need an Authgear project before you can start signing users up.

You can create multiple projects under one account based on your needs. When you create multiple projects, each project is isolated from the others. For example, you can create **Project 1** for your e-commerce business and **Project 2** for a fitness club you organize. **Project 1** and **Project 2** will not share the same users or settings.

To create an additional project, log in to your Authgear account then click on the "Create Project" button.

### 3. Testing your signup/login page

Now that you have created a project in Authgear, you're ready to integrate Authgear into your applications for your customers/users to sign up and log in. See our documentation for adding Authgear to your application or website [here](/get-started/start-building) for your favourite framework and programming language.

Before any integration, you can try out the signup flow for your project from the "Getting Started" page.

<figure><img src="/files/mmwFZh7IwSECsJt6LOkG" alt=""><figcaption></figcaption></figure>

You should be greeted with the AuthUI Login/Sign-up page. Use the form to create a new user account under your project.

<figure><img src="/files/gdgwAwzfMD2YS2qjZ5Ow" alt="" width="375"><figcaption></figcaption></figure>

{% hint style="info" %}
The flow for sign-up and login will differ based on the login method you enable for your Authgear project. For example, users can sign up using their phone number and password if you enable the **Mobile** login method.
{% endhint %}

### 4. User Settings Page

After logging in from the previous step, you can **check out the Pre-Built User Settings Page**. This page is the default page where users of your project can edit their profile and manage security settings.

<figure><img src="/files/G8lMsomZE1ecZSKqdnWv" alt=""><figcaption></figcaption></figure>

Click on the **Edit Profile** to edit and view profile attributes such as first name, last name, photo, etc.

<figure><img src="/files/mSob761Ul2umHubdZiaM" alt=""><figcaption></figcaption></figure>

Users can manage the identities (email address, phone number, or social media account) linked to their account under the **My Account** section of the User Settings page.

The **Security** section of the User Settings page has the following options:

* **Password:** users can change their password from here.
* **2-step verification:** users can click on this option to view and manage 2-step authenticators for their account. The option is only available when you enable 2FA for your project in **Authgear Portal** > **Authentication** > **2FA**.
* **Signed-in device:** from this page, the user can view browsers and devices that their account is currently signed in on.
* **Advanced Settings:** contains additional settings. For example, when you enable [Account Deletion](/admin-and-operations/user-management/account-deletion) for your project, users can click on this link to access the **Delete Account** button.

You can learn more about the User Settings page [here](/customization/built-in-ui/user-settings).

### 5. Integrate Authgear with Your Application or Website

To get more from Authgear and use Authgear to add user authentication to your web or mobile application, you should integrate Authgear to your application or website.

Authgear has official SDKs for integrating Authgear with React, React Native, ionic, Native Android, iOS, and Flutter applications. See the [Start Building](/get-started/start-building) section of Authgear documentation for detailed instructions for each SDK and using other frameworks without the official SDK.

### 6. View and Manage Your Users

Authgear offers multiple options for viewing and managing all users who sign up for your project. These options include:

* Authgear Portal
* [Admin API](/reference/apis/admin-api)

The quickest way to manage your users is from the Authgear Portal. To open the user management page in the Authgear Portal, click on the **User Management** link in the navigation bar on the left side.

Click on **Users** to view a list of all the users in your project. Click on a user from the list to view their complete profile and perform administrative operations like modify profile details, suspend/unsuspend user, delete account, etc.

You can also add new users to your project from the User Management page. To do that, click on the **Create User** button on the right.

<figure><img src="/files/tG3dnlMyjZMCW7fgIy2V" alt=""><figcaption></figcaption></figure>

### 7. Continue Exploring Authgear

Continue to navigate around the Authgear Portal to see all the features and settings available for your project.

You can also check out any of the following guides to learn more about Authgear:

* [customize the look of AuthUI](/customization/built-in-ui/branding) login and sign-up page to match your branding needs using the Design tool
* guides on how to [integrate Authgear to your app](/get-started/start-building).
* how to [enable 2FA for your project](/authentication-and-access/authentication/enable-two-factor-authentication-2fa).


# Skills for AI Agent

[Authgear Skills](https://github.com/authgear/authgear-skills) are installable skills for AI coding agents (Cursor, Claude Code, Codex, OpenCode, Gemini CLI, and similar) to integrate Authgear into your projects.

To install all Authgear Skills, in your project use:

```shellscript
npx skills add authgear/skills
```

Or clone [the repo](https://github.com/authgear/authgear-skills) and point the skills path of your coding agent to the `skills` folder.

### Available skills

Once installed, these skills will be available for your project.

<table><thead><tr><th width="221.4140625">Skill</th><th>When to use</th></tr></thead><tbody><tr><td>/<strong>authgear-integration</strong></td><td>Integrate Authgear authentication into web, mobile, and backend applications</td></tr><tr><td><strong>/authgear-custom-ui</strong> (coming soon)</td><td>Build <a href="/pages/QuQP4wQAMhHjbsQ6yegR">custom UI</a> for signup/login</td></tr><tr><td><strong>/authgear-authflow</strong> (coming soon)</td><td>Set up <a href="/pages/bh9jTI1HfOss0Ga6QOXo">custom authentication flow</a></td></tr></tbody></table>

### Example prompts

You can try our these example prompts in your project:

* "Add Authgear authentication to my React app with login/logout buttons and a protected dashboard page"
* "Integrate Authgear into my React Native app with login flow and user setting screen"
* "Protect my Express.js API endpoints with JWT token validation using Authgear"
* "Create a user profile page that displays email and phone number from Authgear"


# Start Building

Choose the integration approach based on application type

## Integration Approaches

There are 3 different high-level approaches to integrating Authgear with your applications:

1. **Mobile apps or single-page web applications:**\
   The frontend clients integrate with Authgear's SDKs, which handle full login flow and session management. It's important to validate the session in your backend server.
2. **Regular Web Applications:**\
   Traditional server-side rendered web apps that run on the server can use OIDC protocol to authenticate with Authgear. The application server has full control over the session storage.
3. **Software built by others:**\
   Integrate with other OIDC/SAML compatible applications like WordPress, Salesforce for Single Sign-On.

## **Mobile apps or single-page web applications**

<figure><img src="/files/Z353b5CCXPzQBlJrQwYH" alt=""><figcaption></figcaption></figure>

### Client-side SDKs

Client-side SDKs are designed for developers to quickly implement authentication with Auth UI on your web and mobile applications. After login, it returns the user data for your apps. It can open a hosted [pre-built account settings page](/customization/built-in-ui/user-settings) for the user to manage their own account. The SDKs manage session token storage automatically and have built-in token ownership protection ([DPoP](https://oauth.net/2/dpop/)) against stolen refresh tokens.

**Check out the following guides for your specific framework:**

* Guides for Frontend JS SDK
  * [React](/get-started/single-page-app/react)
  * [Vue](/get-started/single-page-app/vue)
  * [Angular](/get-started/single-page-app/angular)
  * [Vanilla JS](/get-started/single-page-app/website)
* Guides for Mobile SDKs
  * [iOS](/get-started/native-mobile-app/ios)
  * [Android](/get-started/native-mobile-app/android)
  * [React Native](/get-started/native-mobile-app/react-native)
  * [Flutter](/get-started/native-mobile-app/flutter)
  * [Capacitor (Ionic)](/get-started/native-mobile-app/ionic)
  * [Xamarin](/get-started/native-mobile-app/xamarin)
  * [Others](/get-started/native-mobile-app/using-authgear-without-sdk-client-side)

### Validate JWT in your backend server

After the frontend integration is complete, every request sent from your application to the backend server should include the Authgear session in its header. JWKS should be used to validate the requests and decode user information from the JWT access token. See [Validate JWT in your application server](/get-started/backend-api/jwt) for details and code examples.

### Customization

You can [customize the look and feel of Authgear prebuilt UI](/customization/built-in-ui/branding) to match your branding. [Events and hooks](/customization/events-hooks) can be used to stay notified and add functionality during the authentication process.

### User Management through backend server

The Authgear Admin API enables comprehensive user management via a GraphQL endpoint for your backend server. The server can perform operations including searching for users, updating user details, deleting user accounts, and disabling user access.

For detailed implementation instructions and API capabilities, refer to the [Admin API ](/reference/apis/admin-api)guide.

### Custom UI

If you wish to use a custom UI instead of the pre-built UI for signup and login, you need to deploy another server and complete the signup/login process using Authentication Flow API. See [Custom UI ](/customization/custom-ui)for in-depth instructions.

## Regular Web Applications

<figure><img src="/files/rB1roLy9wxM4Q9mAWCAM" alt=""><figcaption></figcaption></figure>

If your application is a traditional web app running on a server, you can leverage the OpenID Connect (OIDC) protocol to authenticate users via Authgear. A wide range of plug-and-play libraries can be found that simplify the integration process. These libraries handle crucial tasks such as authentication requests, session management, and redirecting users back to your application seamlessly.

**See the following tutorials for your specific application framework:**

* [Next JS](/get-started/regular-web-app/nextjs)
* [Express JS](/get-started/regular-web-app/express)
* [PHP (Laravel)](/get-started/regular-web-app/laravel)
* [Python (Flask)](/get-started/regular-web-app/python-flask-app)
* [ASP.NET Core MVC](/get-started/regular-web-app/asp.net-core-mvc)
* [Java (Spring Boot)](/get-started/regular-web-app/java-spring-boot)

### Customization

You can [customize the look and feel of Authgear prebuilt UI](/customization/built-in-ui/branding) to match your branding. [Events and hooks](/customization/events-hooks) can be used to stay notified and add functionality during the authentication process.

### User Management

The Authgear Admin API enables comprehensive user management via a GraphQL endpoint for your server. The server can perform operations including searching for users, updating user details, deleting user accounts, and disabling user access.

For detailed implementation instructions and API capabilities, refer to the [Admin API ](/reference/apis/admin-api)guide.

### Custom UI

If you wish to use a custom UI instead of the pre-built UI for signup and login, you need to deploy another server and complete the signup/login process using Authentication Flow API. See [Custom UI ](/customization/custom-ui)for in-depth instructions.

## Software built by others

When implementing identity management for your enterprise software, Authgear provides robust single sign-on (SSO) capabilities that seamlessly connect your workforce. Enterprise applications typically support standard authentication protocols like OpenID Connect (OIDC) and Security Assertion Markup Language (SAML)

* [Integration with OIDC Protocol](/get-started/oidc-provider)
* [Integration with SAML 2.0 Protocol](/get-started/single-sign-on-with-saml)


# Single-Page App

You need to protect a JavaScript SPA application that runs entirely in a browser

{% content-ref url="/pages/OebXDnWVjMaHZWu9GwaM" %}
[Angular](/get-started/single-page-app/angular)
{% endcontent-ref %}

{% content-ref url="/pages/cpJZRTXowaem5PW8JpMd" %}
[JavaScript (Web)](/get-started/single-page-app/website)
{% endcontent-ref %}

{% content-ref url="/pages/-MbjniMzPmbpZ2MI29aV" %}
[React](/get-started/single-page-app/react)
{% endcontent-ref %}

{% content-ref url="/pages/dfc7Z8f9qyNYOcOGTALB" %}
[Vue](/get-started/single-page-app/vue)
{% endcontent-ref %}


# JavaScript (Web)

Integrate Authgear to your website with the Web SDK

In this guide, you'll learn how to integrate Authgear into your website using the Token Approach. In the token approach, the Authgear server returns an access token and a refresh token to your SPA application after successful user authentication.

Your application can send the access token in subsequent HTTP requests to access protected resources.

Follow this guide to add Authgear SDK to any web application in under 🕐 **10 minutes**.

{% hint style="info" %}
See and clone the full code for the demo app for this tutorial in [<mark style="color:orange;">**the Github Repo here**</mark>](https://github.com/authgear/authgear-example-spa-js).
{% endhint %}

This guide uses the Authgear Web SDK for integrating Authgear with a SPA Web app. Supported browsers include:

* Last 2 Firefox major versions
* Last 2 Chrome major versions
* Last 2 Edge major versions
* Last 3 Safari major versions

## Setup Application in Authgear

Signup for an Authgear Portal account in <https://portal.authgear.com/>. Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an application in the project.

### **Step 1: Create an application in the Portal**

Go to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Click **⊕Add Application** in the top toolbar.

Input the name of your application and select the application type **Single Page Application**. Click the **Save** button to proceed.

<figure><img src="/files/pM6lIHJrUrW4WaGfTozK" alt=""><figcaption><p>create spa application authgear portal</p></figcaption></figure>

On the next screen you will see a list of tutorials for different frameworks, click on **Next** to skip to your client application configuration page.

### **Step 2: Configure the application**

First, decide the paths in your website that users will be redirected to after they have authenticated with Authgear.

To run the demo app in this tutorial offline, scroll to the URIs section of your client application page add the following URI:

Under **Authorized Redirect URIs** add `http://localhost:3000/`

{% hint style="info" %}
Note that the trailing "/" in the above URLs must be included.
{% endhint %}

![Edit an application](/files/zu5AjbBMwqQqGaiwoSuF)

Click on the **Save** button to keep your changes.

## Add Authgear to any web page using JavaScript

In this section, we'll create a simple web page and use the Authgear Web JavaScript SDK to add user authentication to the page.

### Step 1: Create a Basic Single Page Web Application

For this guide, we'll create a basic web application with only one page. Follow these steps to set up the application. First, create a new directory on your computer for the project. You can do this in a terminal using the following commands:

```bash
mkdir my-webapp
cd my-webapp
```

### Step 2: Create Web Server

We'll create an Express.js server in the web project directory so that we can access the HTML page in a web browser using `http://localhost:3000`. Run the following commands from the root of your web project directory to install Express.

First, generate a package.json file in your project directory:

```bash
npm init -y
```

Install the Express npm package:

```bash
npm install express
```

Install Nodemon (used for adding hot road to JavaScript development):

```sh
npm install -D nodemon
```

Now create a `server.js` file in the root folder of your project. Add the following code to server.js:

```javascript
const express = require("express");
const { join } = require("path");
const path = require("path");
const app = express();

// Serve static assets from the /public folder
app.use(express.static(join(__dirname, "public")));

// Serve the index page for all other requests
app.get(/.*/, (req, res) => {
  res.sendFile(path.join(__dirname, "index.html"));
});


// Listen on port 3000
app.listen(3000, () => console.log("Application running on port 3000"));
```

Next, open the `package.json` in your project directory and add the following lines in the script section:

```json
"start": "node server.js",
"dev": "nodemon server.js"
```

{% hint style="info" %}
Note that the above you may use your preferred tool/environment to serve the HTML file and skip the step of creating an Express server.
{% endhint %}

### Step 3: Create Web Page

Create a new file called `index.html` in the root of your project directory. Add the following code to the file:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Authgear SPA SDK Sample</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css" />
  </head>

  <body>
    <h2>SPA Authentication Sample</h2>
    <p>Welcome to our page!</p>
    <button id="btn-login" disabled="true" onclick="login()">Log in</button>
    <button id="btn-logout" disabled="true" onclick="logout()">Log out</button>
  </body>
</html>
```

The content `index.html` is a simple web page that contains some text, a Log in button, and a Log out button. In later steps, we will implement the `onclick` events for both steps such that each calls the correct function from the Authgear Web JavaScript SDK.

### Step 4: Install Authgear Web JavaScript SDK

#### CDN

The Web JS SDK is available on a CDN that you can include in any webpage using the following script tag:

```html
<script src="https://unpkg.com/@authgear/web@2.2.0/dist/authgear-web.iife.js"></script>
```

The Web JS SDK is also available as [an npm package](https://www.npmjs.com/package/@authgear/web). That can be installed using any of the following commands:

#### NPM

```bash
npm install --save --save-exact @authgear/web
```

#### Yarn

```bash
yarn add @authgear/web --exact
```

We recommend that you use the npm package to add Authgear to your web application when you're using build tools like Vite and Webpack, and when building with frameworks like React, Vue, Angular, etc.

The easiest way to add a JavaScript library such as the Authgear SDK to a generic Single Page Application (a basic `.html` file page), is to use a CDN. Hence, we'll use the CDN method to add the Authgear Web JavaScript SDK to our demo application for this tutorial.

To install the Authgear SDK, add the Authgear SDK CDN \<script> tag to `index.html` on a new line just before the `</body>` . Your index.html should look like this at this point:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Authgear SPA SDK Sample</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css" />
  </head>

  <body>
    <h2>SPA Authentication Sample</h2>
    <p>Welcome to our page!</p>
    <button id="btn-login" disabled="true" onclick="login()">Log in</button>
    <button id="btn-logout" disabled="true" onclick="logout()">Log out</button>
    <script src="https://unpkg.com/@authgear/web@2.2.0/dist/authgear-web.iife.js"></script>
  </body>
</html>
```

With that, we have added the Authgear SDK to a basic HTML page and we are ready to start making calls to its functions.

#### Checkpoint

At this point, the structure of your project folder should look like this:

```
my-webapp
├── index.html
├── server.js
└── package.json
```

To test your progress so far save your files then run the `npm run dev` command. Next open `localhost:3000` on your preferred web browser and you should see a page that looks like this:

<figure><img src="/files/1usff2tdHaWhLQXthmbM" alt=""><figcaption><p>spa demo web app page</p></figcaption></figure>

### Step 5: Initialize Authgear SDK

Create a new `public/js` directory in the root of your project directory.

Create a new file called `app.js` in the `public/js` directory. Add the following content to `app.js`:

```javascript
let authgearClient = null;

const configureClient = async () => {
    authgearClient = window.authgear.default;

    await authgearClient.configure({
        endpoint: "<YOUR_AUTHGEAR_PROJECT_DOMAIN>",
        clientID: "<YOUR_AUTHGEAR_APP_CLIENT_ID>",
        sessionType: "refresh_token",
    }).then(
        () => {
            console.log("Authgear client successfully configured!");
        },
        (err) => {
            console.log("Failed to configure Authgear");
        }
    );
};

window.onload = async () => {
    await configureClient();
}
```

In app.js, the `configureClient()` function gets an instance of the Authgear Web SDK (`authgearClient`) and then calls the `configure()` function of the SDK to initialize Authgear.

You can find value for `clientID` and `endpoint` from your Authgear client application configuration page.

Finally, link `app.js` in `index.html` using `<script src="js/app.js"></script>` just above the line with the Authgear CDN `<script>` tag as shown below:

```html
<body>
  <h2>SPA Authentication Sample</h2>
  <p>Welcome to our page!</p>
  <button id="btn-login" disabled="true" onclick="login()">Log in</button>
  <button id="btn-logout" disabled="true" onclick="logout()">Log out</button>
  <script src="js/app.js"></script>
  <script src="https://unpkg.com/@authgear/web@2.2.0/dist/authgear-web.iife.js"></script>
</body>
```

### Step 6: Start the authentication flow

When the user clicks login/signup on your website, make a **start authorization** call to redirect them to the login/signup page (AuthUI).

In this step, we'll implement the `login()` function that is called when the **Log in** button is pressed.

Update `app.js` by adding the following code after the declaration of the `configureClient()` function:

```javascript
const login = async () => {
    await authgearClient
        .startAuthentication({
            redirectURI: "http://localhost:3000/",
            prompt: "login",
        })
        .then(
            () => {
                console.log("Logged in!");
            },
            (err) => {
                console.log("Log in failed", err);
            }
        );
};
```

{% hint style="info" %}
Make sure you have added `http://localhost:3000/` as an Authorized Redirect URI in the portal for your Authgear client application. Note that the last "/" in the URL is required.
{% endhint %}

### Step 7: Handling auth result in the redirectURI

After the user authenticates on the login page, the user will be redirected to the `redirectURI` with a `code` parameter in the URL query. In the `redirectURI` of your application, make a **finish authorization** call to handle the authentication result. This will attempt to exchange the `code` for the access token and user info.

Once authorization succeeds, the application should be able to display user info and access protected resources.

To handle the redirect after authentication, we'll call the Authgear SDK's `finishAuthentication()` function when there's a code parameter in the URL of the current page. To do that, update the `window.onload` callback in app.js to the following:

```javascript
window.onload = async () => {
    await configureClient();
    updateUI();

    const query = window.location.search;
    if (query.includes("code=")) {
        await authgearClient.finishAuthentication();
        updateUI();
        window.history.replaceState({}, document.title, "/");
    }
}
```

The `updateUI()` function will update the state of the webpage when the user's logged-in state changes. Add the following code to the end of app.js to implement `updateUI`:

```javascript
const updateUI = async () => {
    const isAuthenticated = authgearClient.sessionState === "AUTHENTICATED";

    document.getElementById("btn-logout").disabled = !isAuthenticated;
    document.getElementById("btn-login").disabled = isAuthenticated;
};
```

The complete content of app.js at the end of this step should look like this:

```javascript
let authgearClient = null;

const configureClient = async () => {
    authgearClient = window.authgear.default;

    await authgearClient.configure({
        endpoint: "<YOUR_AUTHGEAR_PROJECT_DOMAIN>",
        clientID: "<YOUR_AUTHGEAR_CLIENT_APP_ID>",
        sessionType: "refresh_token",
    }).then(
        () => {
            console.log("Authgear client successfully configured!");
        },
        (err) => {
            console.log("Failed to configure Authgear");
        }
    );
};

const login = async () => {
    await authgearClient
        .startAuthentication({
            redirectURI: "http://localhost:3000/",
            prompt: "login",
        })
        .then(
            () => {
                console.log("Logged in!");
            },
            (err) => {
                console.log("Log in failed", err);
            }
        );
};

window.onload = async () => {
    await configureClient();
    updateUI();

    const query = window.location.search;
    if (query.includes("code=")) {
        await authgearClient.finishAuthentication();
        updateUI();
        window.history.replaceState({}, document.title, "/");
    }
}

const updateUI = async () => {
    const isAuthenticated = authgearClient.sessionState === "AUTHENTICATED";

    document.getElementById("btn-logout").disabled = !isAuthenticated;
    document.getElementById("btn-login").disabled = isAuthenticated;
};
```

#### Checkpoint

At this point, your file structure should look like this:

```
my-webapp
├── index.html
├── server.js
├── public/js
│   └── app.js
└── package.json
```

Save all changes in your code, and rerun your app on a web browser. This time, clicking on the **Log in** button should redirect you to your Authgear project's user login page (AuthUI).

<figure><img src="/files/Cs8I3NeFH4JSuG82EhKc" alt=""><figcaption></figcaption></figure>

### Step 8: Open User Settings Page

To add a User Settings button to your app, add the following tag to your `index.html` just below the Logout button:

```html
<button id="btn-settings" disabled="true" onclick="openUserSettings()">User Settings</button>
```

Next, add the following code to `app.js`: just below the `logout()` function:

```javascript
const openUserSettings = () => {
    authgearClient.open("/settings");
}
```

Finally, add the following line to the end of your `updateUI()` function so that the User Settings button is displayed when the user is authenticated:

```javascript
document.getElementById("btn-settings").disabled = !isAuthenticated;
```

Save your work and run your app again. When you click on the User Settings button, your web app should open the pre-built User Settings page.

### Step 9: Additional Actions

#### Get the Logged In State

When you start launching the application. You may want to know if the user has logged in. (e.g. Redirect users to log in if they haven't logged in). The `sessionState` reflects the user logged-in state in the SDK local state. That means even the`sessionState` is `AUTHENTICATED`, the session may be invalid if it is revoked remotely. After initializing the Authgear SDK, call `fetchUserInfo` to update the `sessionState` as soon as it is proper to do so.

```javascript
// After authgearClient.configure, it only reflect SDK local state.
// value can be NO_SESSION or AUTHENTICATED
let sessionState = authgearClient.sessionState;

if (sessionState === "AUTHENTICATED") {
    authgearClient
        .fetchUserInfo()
        .then((userInfo) => {
            // sessionState is now up to date
        })
        .catch((e) => {
            // sessionState is now up to date
            // it will change to NO_SESSION if the session is invalid
        });
}
```

The value of `sessionState` can be `UNKNOWN`, `NO_SESSION` or `AUTHENTICATED`. Initially the `sessionState` is `UNKNOWN`. After a call to `authgearClient.configure`, the session state would become `AUTHENTICATED` if a previous session was found, or `NO_SESSION` if such session was not found.

#### Fetching User Info

In some cases, you may need to obtain current user info through the SDK. (e.g. Display email address in the UI). Use the `fetchUserInfo` function to obtain the user info.

```javascript
authgearClient.fetchUserInfo();
```

See more user info [examples](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo).

#### Log the user out

Use the `logout` function to log out the user. The user will be redirected to your Authgear endpoint to log out their session. You should provide the `redirectURI` for the user to return to your app.

```javascript

authgearClient
  .logout({
    // user will navigate to the redirectURI after logged out
    // make sure it is in the "Post Logout Redirect URIs" in the application portal
    redirectURI: "https://yourdomain.com",
  })
  .then(
    () => {
      // logged out successfully
    },
    (err) => {
      // failed to logout
    }
  );
```

#### Calling an API

Once you have logged-in user, you can start making authenticated requests to backend APIs as described below.

#### Token-based authentication

There are two ways to include the access token in the HTTP requests to your application server.

#### Option 1: Using fetch function provided by Authgear SDK

Authgear SDK provides the `fetch` function for you to call your application server. This `fetch` function will include the Authorization header in your application request, and handle refresh access token automatically. The `authgearClient.fetch` implements [fetch](https://fetch.spec.whatwg.org/).

```javascript
authgearClient
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

#### Option 2: Add the access token to the HTTP request header

You can get the access token through `authgearClient.accessToken`. Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call to refresh the access token only if it is expired. Include the access token in the Authorization header of the application request.

```javascript
authgearClient
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgearClient.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```

## Next steps

To protect your application server from unauthorized access. You will need to [integrate Authgear to your backend](/get-started/backend-api).

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## JavaScript SDK Reference

For detailed documentation on the JavaScript Web SDK, visit [@authgear/web Reference](https://authgear.github.io/authgear-sdk-js/docs/web/)


# React

Follow this quickstart tutorial to add authentication to your React application

Authgear helps you add user logins to your React apps. It provides a pre-built login page and user settings page that can accelerate your development process.

Follow this :clock1: **15-minute** tutorial to create a simple app using React with the Authgear SDK.

{% hint style="info" %}
**Check out and clone** [<mark style="color:orange;">**the Sample Project on GitHub**</mark>](https://github.com/authgear/authgear-example-react)**.**
{% endhint %}

## Setup Application in Authgear

To use Authgear's features, you'll need an account and a Project. Sign up for a free account at <https://portal.authgear.com/> and create a new Project to get started.

After that, we will need to create an Application in the Project Portal.

### Create an application in the Portal

To create a client application, go to **Applications** on the left menu bar in the Authgear Portal.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt="" width="375"><figcaption><p>portal navigate to applications</p></figcaption></figure>

Next, click **⊕Add Application** in the top toolbar.

Enter the name of your application, e.g. "MyAwesomeApp", then select **Single Page Application** as the Application Type. Click the **Save** button to create the application.

### Configure Authorize Redirect URI

The **Authorized Redirect URI** is a URL in you application where the user will be redirected to after login with Authgear. In this path, make a **finish authentication** call to complete the login process.

Go to the URI section of the Authgear client application you just created and add a new **Authorized Redirect URI**. For this tutorial, add `http://localhost:4000/auth-redirect` to Authorize Redirect URIs.

Click **Save** to keep all client app configuration changes before proceeding to the next steps.

<figure><img src="/files/yLWQJmerBVVdkzEQNPzV" alt="" width="375"><figcaption><p>Configure Authorized Redirect URIs</p></figcaption></figure>

## Add Authgear to React App

In this section, we'll create a simple React application and connect it to Authgear such that, users of the app will log in, view their user settings, and log out of their account.

### Step 1: Create a simple React project

Here are some recommended steps to scaffold a React project. You can skip this part if you are adding Authgear to an existing project. See [#step-2-install-authgear-sdk-to-the-project](#step-2-install-authgear-sdk-to-the-project "mention") in the next section.

#### Create a new React project using Vite

Run the following command from your preferred folder to create a new React project with Vite:

```bash
npm create vite@latest my-app -- --template react-ts 
```

Next, run the following commands to open the new project directory and install the dependencies:

```sh
cd my-app
npm install
```

#### Change port

In the `package.json` file, update the value of `dev` field in the `script.dev` section to:

```
"dev": "vite --port 4000",
```

This will enable the `npm run dev` command run the app in development mode on port 4000.

The file structure in your project should look like this now:

```bash
my-app
├── node_modules
│   └── (...)
├── package-lock.json
├── package.json
├── index.html
└── src
    ├── App.tsx
    └── main.tsx
```

Run `npm run dev` now to run the project and you will see the default "Vite + React" page when you open `http://localhost:4000` on a web browser.

### Step 2: Install Authgear SDK to the project

Run the following command within your React project directory to install the Authgear Web SDK

```bash
npm install --save --save-exact @authgear/web
```

In `src/main.tsx` , import `authgear` and call the `configure` function to initialize an Authgear instance on application loads.

```tsx
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import authgear from "@authgear/web"

async function init() {
  try {
    // configure Authgear container instance
    await authgear.configure({
      endpoint: import.meta.env.VITE_AUTHGEAR_ENDPOINT,
      clientID: import.meta.env.VITE_AUTHGEAR_CLIENT_ID,
      sessionType: "refresh_token",
    });
  } finally {
    createRoot(document.getElementById("root")!).render(<App />);
  }
}

init().catch((e) => {
  // Error handling
  console.error(e)
});
```

The Authgear container instance takes `endpoint` and `clientID` as parameters. They can be obtained from the configuration page for the application created in [#setup-application-in-authgear](#setup-application-in-authgear "mention"). Create a `.env` file in the root directory of your project and add your Authgear client application configuration using the following fields:

```properties
VITE_AUTHGEAR_CLIENT_ID=<CLIENT_ID>
VITE_AUTHGEAR_ENDPOINT=<AUTHGEAR_ENDPOINT>
VITE_AUTHGEAR_REDIRECT_URL=http://localhost:4000/auth-redirect
```

It is recommended to render the app after `configure()` resolves. So by the time the app is rendered, Authgear is ready to use.

{% hint style="info" %}
Run **`npm run dev`** now and you should see the default page again and no error message in the console if Authgear SDK is configured successfully
{% endhint %}

### Step 3: Implement the Context Provider

Since we want to reference the logged-in state everywhere in the app, let's put the state in a **context provider** with `UserProvider.tsx` in the `/src/context` folder.

The `UserProvider.tsx` file will have an `isLoggedIn` boolean and a `setIsLoggedIn` function. The `isLoggedIn` boolean state can be auto-updated using the `onSessionStateChange` callback. This callback can be stored in `delegate` which is in the local SDK container.

```tsx
// src/context/UserProvider.tsx
import React, { createContext, useEffect, useState, useMemo } from "react";
import authgear from "@authgear/web";

interface UserContextValue {
  isLoggedIn: boolean;
}

export const UserContext = createContext<UserContextValue>({
  isLoggedIn: false,
});

interface UserContextProviderProps {
  children: React.ReactNode;
}

const UserContextProvider: React.FC<UserContextProviderProps> = ({
  children,
}) => {
  // By default the user is not logged in
  const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);

  useEffect(() => {
    // When the sessionState changed, logged in state will also be changed
    authgear.delegate = {
      onSessionStateChange: (container) => {
        // sessionState is now up to date
        // Value of sessionState can be "NO_SESSION" or "AUTHENTICATED"
        const sessionState = container.sessionState;
        if (sessionState === "AUTHENTICATED") {
          setIsLoggedIn(true);
        } else {
          setIsLoggedIn(false);
        }
      },
    };
  }, [setIsLoggedIn]);

  const contextValue = useMemo<UserContextValue>(() => {
    return {
      isLoggedIn,
    };
  }, [isLoggedIn]);

  return (
    <UserContext.Provider value={contextValue}>{children}</UserContext.Provider>
  );
};

export default UserContextProvider;
```

### Step 4: Implement the Auth Redirect

Next, we will add an "AuthRedirect" page for handling the authentication result after the user has been authenticated by Authgear.

First, install `react-router-dom` using the following command:

```sh
npm install --save-exact react-router-dom
```

Create the `AuthRedirect.tsx` component file in the `src/` folder.

Call the Authgear `finishAuthentication()` function in the Auth Redirect component to send a token back to Authgear server in exchange for an access token and a refresh token. Don't worry about the technical jargons, `finishAuthentication()` will do all the hard work for you and save the authentication data.

When the authentication is finished, the `isLoggedIn` state from the UserContextProvider will be automatically set to `true`. Finally, navigate back to root (`/`) which is our Home page.

The final `AuthRedirect.tsx` will look like this

```tsx
// src/AuthRedirect.tsx
import React, { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import authgear from "@authgear/web";

const AuthRedirect: React.FC = () => {
  const usedToken = useRef(false);

  const navigate = useNavigate();

  useEffect(() => {
    async function updateToken() {
      try {
        await authgear.finishAuthentication();
      } finally {
        navigate("/");
        usedToken.current = true;
      }
    }

    if (!usedToken.current) {
      updateToken().catch((e) => console.error(e));
    }
  }, [navigate]);

  return <></>;
};

export default AuthRedirect;

```

{% hint style="info" %}
Since in React 18 and later, useEffect will be fired twice in development mode, we need to implement a [cleanup function](https://beta.reactjs.org/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) to stop it from firing twice. We will use an `useRef` Hook to stop the user token from being sent twice to the Authgear Endpoint.

Without a cleanup function, an`useEffect`Hook will be fired twice and hence `finishAuthentication()` will send the token back to Authgear Endpoint for two times, which the second one will result in "Invalid Token" error since the token can only be used once.
{% endhint %}

### Step 5: Add Routes and Context Provider to the App

Now, we will add a "Home" page. Create a `Home.tsx` component file the `src/` folder.

Then import **Home** and **AuthRedirect** as routes. And Import **UserContextProvider** and wrap the routes with it.

Your final `App.tsx` should look like this:

<pre class="language-tsx"><code class="lang-tsx">// src/App.tsx
import React from "react";
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './Home';
import AuthRedirect from './AuthRedirect';
import UserContextProvider from './context/UserProvider';

const App: React.FC = () => {
  return (
    &#x3C;UserContextProvider>
      &#x3C;Router>
<strong>        &#x3C;Routes>
</strong>          &#x3C;Route path="/auth-redirect" element={&#x3C;AuthRedirect />} />
          &#x3C;Route path="/" element={&#x3C;Home />} />
        &#x3C;/Routes>
      &#x3C;/Router>
    &#x3C;/UserContextProvider>
  );
}

export default App;
</code></pre>

The file structure should now look like

```
src
├── App.tsx
├── AuthRedirect.tsx
├── Home.tsx
├── context
│   └── UserProvider.tsx
└── main.tsx
```

### Step 6: Add a Login button

First, we will import the Authgear dependency and the React Hook that we will use to `Home.tsx`. Then add the login button which will call `startAuthentication(ConfigureOptions)` through the `startLogin` on click callback. This will redirect the user to the login page.

```tsx
// src/Home.tsx
import React, { useEffect, useState, useCallback, useContext } from 'react';
import authgear, { PromptOption } from '@authgear/web';

const Home: React.FC = () => {
  const startLogin = useCallback(() => {
    authgear
      .startAuthentication({
        redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authentication, user should be redirected to Authgear
        },
        err => {
          // failed to start authentication
        }
      );
  }, []);
  return (
    <div>
      <h1>Home Page</h1>
      <div>
        <button onClick={startLogin}>Login</button>
      </div>
    </div>
  );
}

export default Home;

```

You can now run **`npm run dev`** and you will be redirected to the Authgear Login page when you click the Login button.

![User will be redirected to the Authgear login page by clicking the login button](/files/Cs8I3NeFH4JSuG82EhKc)

### Step 7: Show the user information

The Authgear SDK helps you get the information of the logged-in users easily.

In the last step, the user is successfully logged in, so let's try to print the user ID (sub) of the user on the Home page.

In `Home.tsx`, we will add a simple loading splash and a greeting message printing the Sub ID. We will add two conditional elements such that they are only shown when user is logged in. We can also change the login button to show only if the user is not logged in.

Make use of `isLoggedIn` from the `UserContext` to control the components on the page. Fetch the user info by `fetchUserInfo()` and access its `sub` property.

```tsx
// src/Home.tsx  
import React, { useEffect, useState, useCallback, useContext } from "react";
import authgear, { PromptOption } from "@authgear/web";
import { UserContext } from "./context/UserProvider";

const Home: React.FC = () => {
  const [greetingMessage, setGreetingMessage] = useState<string>("");
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const { isLoggedIn } = useContext(UserContext);

  useEffect(() => {
    async function updateGreetingMessage() {
      setIsLoading(true);
      try {
        if (isLoggedIn) {
          const userInfo = await authgear.fetchUserInfo();
          setGreetingMessage("The current User sub: " + userInfo.sub);
        }
      } finally {
        setIsLoading(false);
      }
    }

    updateGreetingMessage().catch((e) => {
      console.error(e);
    });
  }, [isLoggedIn]);

  const startLogin = useCallback(() => {
    authgear
      .startAuthentication({
        redirectURI: "http://localhost:4000/auth-redirect",
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authentication, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authentication
        }
      );
  }, []);

  return (
    <div>
      <h1>Home Page</h1>
      {isLoading && "Loading"}
      {greetingMessage ? <span>{greetingMessage}</span> : null}
      {!isLoggedIn && (
        <div>
          <button type="button" onClick={startLogin}>
            Login
          </button>
        </div>
      )}
    </div>
  );
};

export default Home;
```

Run the app again, the User ID (sub) of the user should be printed on the Home page.

### Step 8: Add a Logout button

Now, let's add a Logout button that is displayed when the user is logged in.

In `Home.tsx`, we will use conditional elements to show a Logout button only for a user that is currently logged in.

Find the following line in Home.tsx:

```tsx
{!isLoggedIn && (
        <div>
          <button type="button" onClick={startLogin}>
            Login
          </button>
        </div>
)}
```

Add the following code on a new line just after the above line:

```tsx
{isLoggedIn && (
  <div>
    <button onClick={logout}>Logout</button>
  </div>
)}
```

Then, add the `logout` callback:

```tsx
const logout = useCallback(() => {
  authgear
    .logout({
      redirectURI: "http://localhost:4000/",
    })
    .then(
      () => {
        setGreetingMessage('');
      },
      (err) => {
        console.error(err);
      }
  );
}, []);
```

Run the app again, we can now log out by clicking the Logout button.

### Step 9: Open User Settings

Authgear provides a built-in UI for the users to set their attributes and change security settings.

Use the `openURL` function to open the settings page at `<your_app_endpoint>/settings`

In `Home.tsx` Add a conditional link to the existing elements.

```tsx
{isLoggedIn && (
  <a target="_blank" rel="noreferrer" onClick={userSetting} href="#">
    User Setting
  </a>
)}
```

And add the `userSetting` callback:

```tsx
import authgear, { Page } from "@authgear/web";
const userSetting = useCallback((e: React.MouseEvent<HTMLAnchorElement>) => {
    e.preventDefault();
    e.stopPropagation();
    authgear.open(Page.Settings);
}, []);
```

This is the resulting `Home.tsx`:

```tsx
// src/Home.tsx
import React, { useEffect, useState, useCallback, useContext } from "react";
import { UserContext } from "./context/UserProvider";
import authgear, { Page, PromptOption } from "@authgear/web";

const Home: React.FC = () => {
  const [greetingMessage, setGreetingMessage] = useState<string>("");
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const { isLoggedIn } = useContext(UserContext);

  useEffect(() => {
    async function updateGreetingMessage() {
      setIsLoading(true);
      try {
        if (isLoggedIn) {
          const userInfo = await authgear.fetchUserInfo();
          setGreetingMessage("The current User sub: " + userInfo.sub);
        }
      } finally {
        setIsLoading(false);
      }
    }

    updateGreetingMessage().catch((e) => {
      console.error(e);
    });
  }, [isLoggedIn]);

  const startLogin = useCallback(() => {
    authgear
      .startAuthentication({
        redirectURI: "http://localhost:4000/auth-redirect",
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authorization, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authorization
          console.error(err);
        }
      );
  }, []);

  const logout = useCallback(() => {
    authgear
      .logout({
        redirectURI: "http://localhost:4000/",
      })
      .then(
        () => {
          setGreetingMessage("");
        },
        (err) => {
          console.error(err);
        }
      );
  }, []);

  const userSetting = useCallback((e: React.MouseEvent<HTMLAnchorElement>) => {
    e.preventDefault();
    e.stopPropagation();
    authgear.open(Page.Settings);
  }, []);

  return (
    <div>
      {/* eslint-disable-next-line react/forbid-elements */}
      <h1>Home Page</h1>
      {isLoading && "Loading"}
      {greetingMessage ? <span>{greetingMessage}</span> : null}
      {!isLoggedIn && (
        <div>
          <button type="button" onClick={startLogin}>
            Login
          </button>
        </div>
      )}
      {isLoggedIn && (
        <div>
          <button type="button" onClick={logout}>
            Logout
          </button>
          <br />
          <a target="_blank" rel="noreferrer" onClick={userSetting} href="#">
            User Setting
          </a>
        </div>
      )}
    </div>
  );
};

export default Home;
```

![Show the User ID, a link to User Settings and a logout button after login](/files/aXaPt110PmOPwURwSVtL)

## Next steps, Calling an API

To access restricted resources on your backend application server, the HTTP requests should include the access token in their Authorization headers. The Web SDK provides a `fetch` function which automatically handles this, or you can get the token with `authgear.accessToken`.

#### Option 1: Using fetch function provided by Authgear SDK

Authgear SDK provides the `fetch` function for you to call your application server. This `fetch` function will include the Authorization header in your application request, and handle the process of refreshing an access token automatically. The `authgear.fetch` implements [fetch](https://fetch.spec.whatwg.org/).

```javascript
authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

#### Option 2: Add the access token to the HTTP request header

You can get the access token through `authgear.accessToken`. Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call to refresh the access token only if it is expired. Include the access token into the Authorization header of the application requests.

```javascript
authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```


# Angular

Follow this quickstart tutorial to add authentication to your Angular application

Authgear helps you add user logins to your Angular apps. It provides prebuilt login page and user settings page that accelerate the development.

Follow this :clock1: **15 minutes** tutorial to create a simple app using Angular with Authgear SDK.

{% hint style="info" %}
**Check out and clone** [<mark style="color:orange;">**the Sample Project on GitHub**</mark>](https://github.com/authgear/authgear-example-angular)**.**
{% endhint %}

{% hint style="info" %}
This tutorial targets **Angular 17 and above**, which use [standalone components](https://angular.dev/guide/components) and bootstrap the app without `NgModule`. The sample project is built with Angular 22 (standalone, zoneless change detection). If you are on an older Angular version that still uses `NgModule`, adapt the steps accordingly.
{% endhint %}

**Table of Content**

* [Setup Application in Authgear](#setup-application-in-authgear)
* [Create a simple Angular project](#step-1-create-a-simple-angular-project)
* [Install Authgear SDK to the project](#step-2-install-authgear-sdk-to-the-project)
* [Implement User Service](#step-3-implement-the-user-service)
* [Implement the Auth Redirect page](#step-4-implement-the-auth-redirect)
* [Add a Login button](#step-6-add-a-login-button)
* [Show the user information](#step-7-show-the-user-information)
* [Add a Logout button](#step-8-add-an-logout-button)
* [Open User Settings](#step-9-open-user-settings)
* [Calling an API](#next-steps-calling-an-api)

## Setup Application in Authgear

Signup for an account in <https://portal.authgear.com/> and create a Project.

After that, we will need to create an Application in the Project Portal.

### Create an application in the Portal

1. Go to **Applications** on the left menu bar.
2. Click **⊕Add Application** in the top tool bar.
3. Input the name of your application, e.g. "MyAwesomeApp".
4. Select **Single Page Application** as the application type
5. Click "Save" to create the application

### Configure Authorize Redirect URI

The Redirect URI is a URL in you application where the user will be redirected to after login with Authgear. In this path, make a **finish authentication** call to complete the login process.

For this tutorial, add `http://localhost:4000/auth-redirect` to Authorize Redirect URIs.

### Configure Post Logout Redirect URI

The Post Logout Redirect URI is the URL users will be redirected after they have logged out. The URL must be whitelisted.

For this tutorial, add `http://localhost:4000/` to Post Logout Redirect URIs.

**Save** the configuration before next steps.

![Configure Authorized Redirect URIs and Post Logout Redirect URIs.](/files/7iBL9afm3ZAuNTLSmCc8)

## Step 1: Create a simple Angular project

Here are some recommended steps to scaffold an Angular project. You can skip this part if you are adding Authgear to an existing project. See [#step-2-install-authgear-sdk-to-the-project](#step-2-install-authgear-sdk-to-the-project "mention") in the next section.

#### Install the Angular CLI

To install the Angular CLI, open a terminal window and run the following command:

```bash
npm install -g @angular/cli
```

{% hint style="info" %}
For Windows clients, please find your reference in <https://angular.dev/tools/cli/setup-local> for more information on installing the Angular CLI.
{% endhint %}

#### Create initial workspace

Run the following cli command to create a new workspace and initial app called `my-app` with routing enabled.

```bash
# Create a workspace called my-app
ng new my-app --routing --defaults
# Move into the project directory
cd my-app
```

On Angular 17+, this scaffolds a standalone application. The important generated files are:

* `src/main.ts` — bootstraps the app with `bootstrapApplication(AppComponent, appConfig)`
* `src/app/app.config.ts` — the application providers (this is where we will configure Authgear and the router)
* `src/app/app.routes.ts` — the route definitions

{% hint style="info" %}
Newer versions of the Angular CLI generate component and service files **without** the `.component` / `.service` suffix (for example `app.ts` and class `App` instead of `app.component.ts` and class `AppComponent`). This tutorial and the sample project use the suffixed names. The code is identical either way — just match the file names and class names your CLI generated.
{% endhint %}

#### Edit script for launching the app

In the `package.json` file, edit the `start` script in the `script` section

```bash
# before
"start": "ng serve"

# after
"start": "ng serve --port 4000"
```

The `start` script run the app in development mode on port 4000 instead of the default one.

#### Edit the `app.component.html` file

By default, the Angular CLI generated an initial application for us, but for simplicity, we recommend to modify some of these files to scratch.

In the `src/app/app.component.html` file, remove all the lines and add the following line:

```html
<div>Hello world</div>
```

#### Run your initial app

Run `npm start` now to run the project and you will see "Hello world" on `http://localhost:4000`.

## Step 2: Install Authgear SDK to the project

Run the following command within your Angular project directory to install the Authgear Web SDK

```bash
npm install --save-exact @authgear/web
```

Authgear must be configured **before** any other SDK call (for example `finishAuthentication()` or `fetchUserInfo()`), and configuration is asynchronous. The cleanest way to guarantee this in Angular is to run `authgear.configure()` during app initialization, so the app only renders after Authgear is ready.

In `src/app/app.config.ts`, register an app initializer that calls `configure`:

```typescript
// src/app/app.config.ts
import {
  ApplicationConfig,
  provideAppInitializer,
  provideBrowserGlobalErrorListeners,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import authgear from '@authgear/web';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
    // Configure Authgear before the app renders. configure() must complete
    // before any other SDK call, so blocking bootstrap here avoids a race in
    // which the redirect page or home page runs before the SDK is ready.
    provideAppInitializer(async () => {
      try {
        await authgear.configure({
          endpoint: '<your_app_endpoint>',
          clientID: '<your_client_id>',
          sessionType: 'refresh_token',
        });
      } catch (e) {
        // Don't block bootstrap if Authgear is unreachable; the app will
        // render in the logged-out state and login can be retried.
        console.error(e);
      }
    }),
  ],
};
```

The Authgear container instance takes `endpoint` and `clientID` as parameters. They can be obtained from the application page created in [#setup-application-in-authgear](#setup-application-in-authgear "mention").

Because the initializer is awaited, by the time any route renders Authgear is ready to use.

{% hint style="info" %}
Run **`npm start`** now and you should see a page with "Hello World" and no error message in the console if Authgear SDK is configured successfully
{% endhint %}

## Step 3: Implement the User Service

Since we want to reference the logged in state in anywhere of the app, let's put the state in a **service** with `user.service.ts` in the `/src/app/services/` folder.

In `user.service.ts`, it will have an `isLoggedIn` state. The state is auto updated using the `onSessionStateChange` callback, which is stored in the `delegate` of the local SDK container. We expose the state as an Angular [signal](https://angular.dev/guide/signals) so the UI updates reactively (this also works with zoneless change detection, the default on newer Angular versions).

```typescript
// src/app/services/user.service.ts
import { Injectable, signal } from '@angular/core';
import authgear from '@authgear/web';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  // By default the user is not logged in
  readonly isLoggedIn = signal(false);

  constructor() {
    // When the sessionState changed, logged in state will also be changed
    authgear.delegate = {
      onSessionStateChange: (container) => {
        // sessionState is now up to date
        // value of sessionState can be "NO_SESSION" or "AUTHENTICATED"
        const sessionState = container.sessionState;
        this.isLoggedIn.set(sessionState === 'AUTHENTICATED');
      },
    };
  }
}
```

The `onSessionStateChange` delegate fires while `configure()` runs, so the `UserService` must be instantiated **before** `configure()` — otherwise it would miss the initial state of an already-authenticated user. Update the app initializer in `src/app/app.config.ts` to inject the service first:

```typescript
// src/app/app.config.ts
import { inject, provideAppInitializer } from '@angular/core';
import authgear from '@authgear/web';
import { UserService } from './services/user.service';

// ...

    provideAppInitializer(async () => {
      // Instantiate UserService first so its onSessionStateChange delegate is
      // registered before configure() runs and can observe the initial state.
      inject(UserService);
      try {
        await authgear.configure({
          endpoint: '<your_app_endpoint>',
          clientID: '<your_client_id>',
          sessionType: 'refresh_token',
        });
      } catch (e) {
        console.error(e);
      }
    }),
```

## Step 4: Implement the Auth Redirect

Next, we will add an "auth-redirect" page for handling the authentication result after the user have been authenticated by Authgear.

Create the `auth-redirect` component using the following command:

```bash
ng generate component auth-redirect
```

We will inject the router to navigate after the redirect is handled.

Call the Authgear `finishAuthentication()` function in the Auth Redirect component to send a token back to Authgear server in exchange for access token and refresh token. Don't worry about the technical jargons, `finishAuthentication()` will do all the hard work for you and and save the authentication data.

When the authentication is finished, the `isLoggedIn` state from the UserService will automatic set to `true`. Finally, navigate back to root (`/`) which is our Home page.

The final `auth-redirect.component.ts` will look like this

```typescript
// src/app/auth-redirect/auth-redirect.component.ts
import { Component, OnInit, inject } from '@angular/core';
import { Router } from '@angular/router';
import authgear from '@authgear/web';

@Component({
  selector: 'app-auth-redirect',
  templateUrl: './auth-redirect.component.html',
  styleUrl: './auth-redirect.component.css',
})
export class AuthRedirectComponent implements OnInit {
  private readonly router = inject(Router);

  ngOnInit(): void {
    authgear
      .finishAuthentication()
      .catch((e) => console.error(e))
      .then(() => {
        this.router.navigate(['']);
      });
  }
}
```

## Step 5: Add Routes to the App

Next, we will add a "Home" page . Create a `home` component using the following command:

```bash
ng generate component home
```

Then import **HomeComponent** and **AuthRedirectComponent** as routes. We can add those routes in the `app.routes.ts` file that was generated with the workspace:

```typescript
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AuthRedirectComponent } from './auth-redirect/auth-redirect.component';

export const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'auth-redirect', component: AuthRedirectComponent },
];
```

The router is already provided in `app.config.ts` via `provideRouter(routes)` (added in Step 2). Make sure the root component renders the routed component by importing `RouterOutlet` and using it in the template.

```typescript
// src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {}
```

Replace the lines in `src/app/app.component.html` with the following:

```html
<router-outlet></router-outlet>
```

The file structure should now look like

```
src
├── (...)
├── main.ts
└── app
    ├── app.config.ts
    ├── app.routes.ts
    ├── app.component.ts
    ├── app.component.html
    ├── (...)
    ├── auth-redirect
    │   ├── auth-redirect.component.ts
    │   ├── auth-redirect.component.html
    │   └── (...)
    ├── home
    │   ├── home.component.ts
    │   ├── home.component.html
    │   └── (...)
    └── services
        └── user.service.ts
```

## Step 6: Add a Login button

First we will import the Authgear dependency and inject the UserService in `home.component.ts`. Then add the `startLogin` method which will call `startAuthentication(options)`. This will redirect the user to the login page.

```typescript
// src/app/home/home.component.ts
import { Component, inject } from '@angular/core';
import { UserService } from '../services/user.service';
import authgear, { PromptOption } from '@authgear/web';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrl: './home.component.css',
})
export class HomeComponent {
  readonly user = inject(UserService);

  startLogin(): void {
    authgear
      .startAuthentication({
        redirectURI: 'http://localhost:4000/auth-redirect',
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authorization, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authorization
          console.error(err);
        }
      );
  }
}
```

{% hint style="info" %}
`prompt` takes a `PromptOption` enum value (for example `PromptOption.Login`) in `@authgear/web` v5 and above. Importing and using the enum keeps the call type-safe.
{% endhint %}

Then you can add a button which will trigger the `startLogin` method in `home.component.html`:

```html
<h1>Home Page</h1>
<button type="button" (click)="startLogin()">Login</button>
```

You can now run **`npm start`** and you will be redirected to the Authgear Login page when you click the Login button.

![User will be redirected to the Authgear login page by clicking the login button](/files/NEfVntNC3LKqmRFZGSga)

## Step 7: Show the user information

The Authgear SDK helps you get the information of the logged in users easily.

In the last step, the user is successfully logged in so let's try to print the user ID (sub) of the user in the Home page.

In `home` component, we will add a simple Loading splash and a greeting message printing the Sub ID. We will add two conditional elements such that they are only shown when user is logged in. We can also change the login button to show only if the user is not logged in.

Make use of `isLoggedIn` from the `UserService` to control the components on the page. Fetch the user info by `fetchUserInfo()` and access its `sub` property. We store the loading and greeting state as signals so the template stays reactive.

```typescript
// src/app/home/home.component.ts
import { Component, OnInit, inject, signal } from '@angular/core';
import { UserService } from '../services/user.service';
import authgear, { PromptOption } from '@authgear/web';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrl: './home.component.css',
})
export class HomeComponent implements OnInit {
  readonly user = inject(UserService);

  readonly isLoading = signal(false);
  readonly greetingMessage = signal('');

  async updateGreetingMessage() {
    this.isLoading.set(true);
    try {
      if (this.user.isLoggedIn()) {
        const userInfo = await authgear.fetchUserInfo();
        this.greetingMessage.set('The current User sub: ' + userInfo.sub);
      }
    } finally {
      this.isLoading.set(false);
    }
  }

  ngOnInit(): void {
    this.updateGreetingMessage().catch((e) => {
      console.error(e);
    });
  }

  startLogin(): void {
    authgear
      .startAuthentication({
        redirectURI: 'http://localhost:4000/auth-redirect',
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authorization, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authorization
          console.error(err);
        }
      );
  }
}
```

In the `home.component.html`, use the new `@if` control flow and read each signal by calling it:

```html
<h1>Home Page</h1>
@if (isLoading()) {
  <span>Loading</span>
}
@if (greetingMessage()) {
  <span>{{ greetingMessage() }}</span>
}
@if (!user.isLoggedIn()) {
  <div>
    <button type="button" (click)="startLogin()">Login</button>
  </div>
}
```

Run the app again, the User ID (sub) of the user should be printed on the Home page.

## Step 8: Add a Logout button

Finally, let's add an Logout button when user is logged in.

In `home.component.html`, we will add a conditional element in the markup:

```html
@if (user.isLoggedIn()) {
  <div>
    <button type="button" (click)="logout()">Logout</button>
  </div>
}
```

And add the `logout` method:

```typescript
logout(): void {
  authgear
    .logout({
      redirectURI: 'http://localhost:4000/',
    })
    .then(
      () => {
        this.greetingMessage.set('');
      },
      (err) => {
        console.error(err);
      }
    );
}
```

Run the app again, we can now logout by clicking the logout button.

## Step 9: Open User Settings

Authgear provide a built-in UI for the users to set their attributes and change security settings.

Use the `open` function to open the setting page at `<your_app_endpoint>/settings`

In `home.component.html` append a conditional link to the logout button section.

```html
@if (user.isLoggedIn()) {
  <div>
    <button type="button" (click)="logout()">Logout</button>
    <br />
    <a target="_blank" rel="noreferrer" (click)="userSetting($event)" href="#">
      User Setting
    </a>
  </div>
}
```

And add the `userSetting` method (note the added `Page` import):

```typescript
import authgear, { Page, PromptOption } from '@authgear/web';

async userSetting(event: MouseEvent) {
  event.preventDefault();
  event.stopPropagation();
  await authgear.open(Page.Settings);
}
```

This the resulting `home.component.ts`:

```typescript
// src/app/home/home.component.ts
import { Component, OnInit, inject, signal } from '@angular/core';
import { UserService } from '../services/user.service';
import authgear, { Page, PromptOption } from '@authgear/web';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrl: './home.component.css',
})
export class HomeComponent implements OnInit {
  readonly user = inject(UserService);

  readonly isLoading = signal(false);
  readonly greetingMessage = signal('');

  async updateGreetingMessage() {
    this.isLoading.set(true);
    try {
      if (this.user.isLoggedIn()) {
        const userInfo = await authgear.fetchUserInfo();
        this.greetingMessage.set('The current User sub: ' + userInfo.sub);
      }
    } finally {
      this.isLoading.set(false);
    }
  }

  ngOnInit(): void {
    this.updateGreetingMessage().catch((e) => {
      console.error(e);
    });
  }

  startLogin(): void {
    authgear
      .startAuthentication({
        redirectURI: 'http://localhost:4000/auth-redirect',
        prompt: PromptOption.Login,
      })
      .then(
        () => {
          // started authorization, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authorization
          console.error(err);
        }
      );
  }

  logout(): void {
    authgear
      .logout({
        redirectURI: 'http://localhost:4000/',
      })
      .then(
        () => {
          this.greetingMessage.set('');
        },
        (err) => {
          console.error(err);
        }
      );
  }

  async userSetting(event: MouseEvent) {
    event.preventDefault();
    event.stopPropagation();
    await authgear.open(Page.Settings);
  }
}
```

This is the resulting home.component.html:

```html
<h1>Home Page</h1>
@if (isLoading()) {
  <span>Loading</span>
}
@if (greetingMessage()) {
  <span>{{ greetingMessage() }}</span>
}
@if (!user.isLoggedIn()) {
  <div>
    <button type="button" (click)="startLogin()">Login</button>
  </div>
}
@if (user.isLoggedIn()) {
  <div>
    <button type="button" (click)="logout()">Logout</button>
    <br />
    <a target="_blank" rel="noreferrer" (click)="userSetting($event)" href="#">
      User Setting
    </a>
  </div>
}
```

![Show the User ID, a link to User Settings and a logout button after login](/files/aXaPt110PmOPwURwSVtL)

## Next steps, Calling an API

To access restricted resources on your backend application server, the HTTP requests should include the access token in their Authorization headers. The Web SDK provides a `fetch` function which automatically handle this, or you can get the token with `authgear.accessToken`.

#### Option 1: Using fetch function provided by Authgear SDK

Authgear SDK provides the `fetch` function for you to call your application server. This `fetch` function will include the Authorization header in your application request, and handle refresh access token automatically. The `authgear.fetch` implements [fetch](https://fetch.spec.whatwg.org/).

```javascript
authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

#### Option 2: Add the access token to the HTTP request header

You can get the access token through `authgear.accessToken`. Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call only if the access token has expired. Include the access token into the Authorization header of the application request.

```javascript
authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```


# Vue

Follow this quickstart tutorial to add authentication to your Vue application

Authgear helps you add user logins to your Vue apps. It provides a prebuilt login page and user settings page that accelerate the development.

Follow this :clock1: **15-minute** tutorial to create a simple app using Vue with Authgear SDK.

{% hint style="info" %}
**Check out and clone** [<mark style="color:orange;">**the Sample Project on GitHub**</mark>](https://github.com/authgear/authgear-example-vue)**.**
{% endhint %}

## Setup Application in Authgear

You need an Authgear account and a project to use Authgear. Sign up for a free account at <https://portal.authgear.com/> and create a Project.

After that, we will need to create an Application in the Project Portal.

### Create an application in the Portal

In this step, we'll create an Authgear client application in the Authgear Portal. We'll use the configuration for this application in later steps to connect our Vue application to Authgear.

To create a client application navigate to **Applications** on the left menu bar in Authgear Portal.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption><p>portal navigate to applications</p></figcaption></figure>

Next, click **⊕Add Application** in the top toolbar to open the **New Application** page.

Input the name of your application, e.g. "MyAwesomeApp" then select **Single Page Application** as the Application Type.

Click **Save** to create the application.

<figure><img src="/files/pM6lIHJrUrW4WaGfTozK" alt=""><figcaption><p>create new client application</p></figcaption></figure>

On the next screen, you'll see links to tutorials for different frameworks. Click **Next** to skip to the application configuration page

### Configure Authorize Redirect URI

The Redirect URI is a URL in your application where the user will be redirected to after login with Authgear. In this path, make a **finish authentication** call to complete the login process.

Scroll to the URI section of the configuration page for the client application you created in the last step. For this tutorial, add `http://localhost:4000/auth-redirect` to Authorize Redirect URIs.

Click the **Save** button to save your changes before you proceed.

<figure><img src="/files/yLWQJmerBVVdkzEQNPzV" alt="" width="375"><figcaption><p>Configure Authorized Redirect URIs</p></figcaption></figure>

## Add Authgear to Vue App

In this section, we'll add Authgear to a Vue application and use the Authgear JavaScript SDK to implement the following features:

* User Login/Sign-up
* Read logged-in user's info
* Open User Settings page
* End user session using a Logout button

### Step 1: Create a simple Vue project

Here are some recommended steps to scaffold a Vue project. You can skip this part if you are adding Authgear to an existing project. See [#step-3-install-authgear-sdk-to-the-project](#step-3-install-authgear-sdk-to-the-project "mention") in the next section.

#### Install basic project dependencies

Create the project folder and install the dependencies. We will use [Vite](https://vite.dev/guide/cli.html) as the build tool and the `vue-router` package. Also, we will use TypeScript in this tutorial.

Run the following commands to create a new project:

Create project and move into the project directory:

```sh
npm create vite@latest my-app -- --template vue-ts
cd my-app
```

Install dependencies:

```bash
npm install
```

Install Vue router:

```
npm install --save-exact vue-router
```

#### Add port configuration for development mode

As we are using port 4000 for this tutorial, we need to add the port information to the config. In the `vite.config.ts` file, modify the file with the following lines:

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    port: 4000,
  },
});
```

After doing so, when you run `npm run dev` , the server will be running on port 4000.

#### Create the `Home.vue` file

Create a new file called `Home.vue` in the `src/components` folder with simply showing `Hello World` on the screen. Add the follow code to `Home.vue`:

```tsx
// src/components/Home.vue
<script setup lang="ts"></script>

<template><div>Hello World</div></template>
```

#### Edit the `App.vue` file

The `App.vue` file is generated by `Vite` already but some sections of it might not be needed for this tutorial. Edit the content of `App.vue` to the following:

```tsx
// src/components/App.vue
<script setup lang="ts">
import Home from "./components/Home.vue";
</script>

<template>
  <Home />
</template>
```

#### Delete unnecessary files

Some of the files might not be used and thus can be deleted. You can perform the following script to delete these files:

```bash
rm -rf src/assets src/components/HelloWorld.vue
```

#### File structure

The file structure in your project is now:

```bash
my-app
├── node_modules
│   └── (...)
├── package-lock.json
├── package.json
├── vite.config.ts
├── (...)
└── src
    ├── components
    │   └── Home.vue
    ├── App.vue
    ├── main.ts
    └── (...)
```

Run `npm run dev` now to run the project and you will see the default page with the title `Vite + Vue` and a "Hello World" message on `http://localhost:4000`.

### Step 2: Create routes for the project

Create a `AuthRedirect.vue` file in the `src/components` folder with the same content as `src/components/Home.vue` at this moment.

Create a file called `router.ts` in the `src/` folder. We will import `Home` and `AuthRedirect` component as the route and we will implement these components later. The content of this file will look like this:

```typescript
// src/router.ts
import { createRouter, createWebHistory } from "vue-router";

export const history = createWebHistory();
export const router = createRouter({
  history,
  routes: [
    {
      path: "/",
      // We will implement this component later
      component: () => import("./components/Home.vue"),
    },
    {
      path: "/auth-redirect",
      // We will implement this component later
      component: () => import("./components/AuthRedirect.vue"),
    },
  ],
});
```

### Step 3: Install Authgear SDK to the project

Run the following command within your Vue project directory to install the Authgear Web SDK

```bash
npm install --save-exact @authgear/web
```

In `src/main.ts` , import `authgear` and call the `configure` function to initialize an Authgear instance on application loads. We will also import `router` and use it to build routes for us. Your `main.ts` file should look like this:

```typescript
// src/main.ts
import { createApp } from "vue";
import './style.css'
import App from "./App.vue";
import { router } from "./router";
import authgear from "@authgear/web";

const app = createApp(App);

async function init() {
  try {
    // configure Authgear container instance
    await authgear.configure({
      endpoint: import.meta.env.VITE_AUTHGEAR_ENDPOINT,
      clientID: import.meta.env.VITE_AUTHGEAR_CLIENT_ID,
      sessionType: "refresh_token",
    });
  } finally {
    app.use(router);
    app.mount("#app");
  }
}

init().catch((e) => {
  // Error handling
  console.error(e)
});
```

The Authgear container instance takes `endpoint` and `clientID` as parameters. They can be obtained from the application page created in [#setup-application-in-authgear](#setup-application-in-authgear "mention").

Create a `.env` file in the root directory of your project and add your Authgear client application configuration using the following fields:

```properties
VITE_AUTHGEAR_CLIENT_ID=<CLIENT_ID>
VITE_AUTHGEAR_ENDPOINT=<AUTHGEAR_ENDPOINT>
VITE_AUTHGEAR_REDIRECT_URL=http://localhost:4000/auth-redirect
```

It is recommended to render the app after `configure()` resolves. So by the time the app is rendered, Authgear is ready to use.

{% hint style="info" %}
Run **`npm run dev`** now and you should see the same page and no error message in the console if Authgear SDK is configured successfully
{% endhint %}

### Step 4: Implement the Context Provider

Since we want to reference the logged-in state everywhere in the app, let's put the state in a **context provider** with `UserProvider.vue` in the `/src/contexts` folder.

In `UserProvider.vue`, will have a `isLoggedIn` boolean value. The `isLoggedIn` boolean state can be auto-updated using the `onSessionStateChange` callback. This callback can be stored in `delegate` which is in the local SDK container.

```tsx
// src/contexts/UserProvider.vue
<script lang="ts">
import {
  defineComponent,
  InjectionKey,
  provide,
  readonly,
  ref,
  Ref,
  toRefs,
} from "vue";
import authgear from "@authgear/web";

export interface UserContextValue {
  isLoggedIn: Ref<boolean>;
}

export const UserStateSymbol: InjectionKey<UserContextValue> =
  Symbol("UserState");

export default defineComponent({
  setup() {
    const isLoggedIn = ref(false);

    const state: UserContextValue = {
      isLoggedIn,
    };

    authgear.delegate = {
      onSessionStateChange: (container) => {
        const sessionState = container.sessionState;
        if (sessionState === "AUTHENTICATED") {
          isLoggedIn.value = true;
        } else {
          isLoggedIn.value = false;
        }
      },
    };

    provide<UserContextValue>(UserStateSymbol, toRefs(readonly(state)));

    return { state };
  },
});
</script>

<template>
  <slot />
</template>

```

### Step 5: Implement the Auth Redirect

Next, we will add an "AuthRedirect" page for handling the authentication result after the user has been authenticated by Authgear.

Create the `AuthRedirect.vue` component file in the `src/components/` folder.

Call the Authgear `finishAuthentication()` function in the Auth Redirect component to send a token back to Authgear server in exchange for access token and refresh token. Don't worry about the technical jargons, `finishAuthentication()` will do all the hard work for you and and save the authentication data.

When the authentication is finished, the `isLoggedIn` state from the UserContextProvider will automatically be set to `true`. Finally, navigate back to root (`/`) which is our Home page.

The final `AuthRedirect.vue` will look like this

```tsx
// src/components/AuthRedirect.vue
<script setup lang="ts">
import { onMounted } from "vue";
import authgear from "@authgear/web";
import { router } from "../router";

onMounted(() => {
  async function updateToken() {
    try {
      await authgear.finishAuthentication();
    } finally {
      router.replace({ path: "/" });
    }
  }
  updateToken().catch((e) => console.error(e));
});
</script>

<template></template>

```

### Step 6: Apply Routes and Context Provider to the App

As we have already configured the routes in the previous section, we can simply add `<router-view />` tag to the `App.vue`. We can then Import **UserProvider** and wrap the `router-view` with it.

Your final `App.vue` should look like this:

```tsx
// src/App.vue
<script setup lang="ts">
import UserProvider from "./contexts/UserProvider.vue";
</script>

<template>
  <UserProvider>
    <router-view />
  </UserProvider>
</template>
```

The file structure should now look like

```
src
├── App.vue
├── main.ts
├── router.ts
├── vite-env.d.ts
├── contexts
│   └── UserProvider.vue
└── components
    ├── AuthRedirect.vue
    └── Home.vue
```

### Step 7: Add a Login button

First, we will import the Authgear dependency. Then add the login button which will call `startAuthentication(ConfigureOptions)` through `startLogin` callback on click. This will redirect the user to the login page.

```tsx
// src/components/Home.vue
<script setup lang="ts">
import authgear, { PromptOption } from "@authgear/web";

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};
</script>

<template>
  <h1>Home Page</h1>
  <button @click="startLogin">Login</button>
</template>
```

You can now run **`npm run dev`** and you will be redirected to the Authgear Login page when you click the Login button.

![User will be redirected to the Authgear login page by clicking the login button](/files/Cs8I3NeFH4JSuG82EhKc)

### Step 8: Show the user information

The Authgear SDK helps you get the information of the logged-in users easily.

In the last step, the user is successfully logged in so let's try to print the user ID (sub) of the user in the Home page.

In `Home.vue`, we will add a simple Loading splash and a greeting message printing the Sub ID. We will add two conditional elements such that they are only shown when user is logged in. We can also change the login button to show only if the user is not logged in.

Make use of `isLoggedIn` from the `UserProvider` to control the components on the page. Fetch the user info by `fetchUserInfo()` and access its `sub` property.

The Login button can be also rendered conditionally which only visible if the user is not logged in.

```tsx
// src/components/Home.vue  
<script setup lang="ts">
import authgear, { PromptOption } from "@authgear/web";
import { inject, onMounted, ref } from "vue";
import { UserStateSymbol } from "../contexts/UserProvider.vue";

const { isLoggedIn } = inject(UserStateSymbol)!;
const isLoading = ref(false);
const greetingMessage = ref("");

onMounted(() => {
  async function updateGreetingMessage() {
    isLoading.value = true;
    try {
      if (isLoggedIn.value) {
        const userInfo = await authgear.fetchUserInfo();
        greetingMessage.value = "The current User sub: " + userInfo.sub;
      }
    } finally {
      isLoading.value = false;
    }
  }

  updateGreetingMessage().catch((e) => {
    console.error(e);
  });
});

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};
</script>

<template>
  <h1>Home Page</h1>
  <span v-if="isLoading">Loading...</span>
  <span v-if="greetingMessage">{{ greetingMessage }}</span>
  <div v-if="!isLoggedIn">
    <button @click="startLogin">Login</button>
  </div>
</template>

```

Run the app again, the User ID (sub) of the user should be printed on the Home page.

### Step 9: Add a Logout button

Finally, let's add a Logout button when a user is logged in.

In `Home.vue`, we will add the following conditional elements in the template:

```tsx
<div v-if="isLoggedIn">
  <button @click="logout">Logout</button>
</div>
```

And add the `logout` callback:

```tsx
const logout = () => {
  authgear
    .logout({
      redirectURI: "http://localhost:4000/",
    })
    .then(
      () => {
        greetingMessage.value = "";
      },
      (err) => {
        console.error(err);
      }
    );
};
```

Run the app again, we can now log out by clicking the **Logout** button.

### Step 10: Open User Settings

Authgear provides a built-in UI for the users to set their attributes and change security settings.

Use the `open` function to open the settings page at `<your_app_endpoint>/settings`

In `Home.vue` append a conditional link to the logout button section like this:

```tsx
<div v-if="isLoggedIn">
  <button @click="logout()">Logout</button>
  <br />
  <a
    target="_blank"
    rel="noreferrer"
    @click.stop.prevent="userSetting"
    href="#"
  >
    User Setting
  </a>
</div>
```

And add the `userSetting` callback:

```tsx
import authgear, { PromptOption, Page } from "@authgear/web";

const userSetting = async () => {
  await authgear.open(Page.Settings);
};
```

This is the resulting `Home.vue`:

```tsx
// src/components/Home.vue
<script setup lang="ts">
import authgear, { PromptOption, Page } from "@authgear/web";
import { inject, onMounted, ref } from "vue";
import { UserStateSymbol } from "../contexts/UserProvider.vue";

const { isLoggedIn } = inject(UserStateSymbol)!;
const isLoading = ref(false);
const greetingMessage = ref("");

onMounted(() => {
  async function updateGreetingMessage() {
    isLoading.value = true;
    try {
      if (isLoggedIn.value) {
        const userInfo = await authgear.fetchUserInfo();
        greetingMessage.value = "The current User sub: " + userInfo.sub;
      }
    } finally {
      isLoading.value = false;
    }
  }

  updateGreetingMessage().catch((e) => {
    console.error(e);
  });
});

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};

const logout = () => {
  authgear
    .logout({
      redirectURI: "http://localhost:4000/",
    })
    .then(
      () => {
        greetingMessage.value = "";
      },
      (err) => {
        console.error(err);
      }
    );
};

const userSetting = async () => {
  await authgear.open(Page.Settings);
};
</script>

<template>
  <h1>Home Page</h1>
  <span v-if="isLoading">Loading...</span>
  <span v-if="greetingMessage">{{ greetingMessage }}</span>
  <div v-if="!isLoggedIn">
    <button @click="startLogin">Login</button>
  </div>
  <div v-if="isLoggedIn">
    <button @click="logout">Logout</button>
    <br />
    <a
      target="_blank"
      rel="noreferrer"
      @click.stop.prevent="userSetting"
      href="#"
    >
      User Setting
    </a>
  </div>
</template>

```

![Show the User ID, a link to User Settings and a logout button after login](/files/k99wS67RsU3iJkOoRGeo)

## Next steps, Calling an API

To access restricted resources on your backend application server, the HTTP requests should include the access token in their Authorization headers. The Web SDK provides a `fetch` function which automatically handles this, or you can get the token with `authgear.accessToken`.

#### Option 1: Using fetch function provided by Authgear SDK

Authgear SDK provides the `fetch` function for you to call your application server. This `fetch` function will include the Authorization header in your application request, and handle the refresh access token automatically. The `authgear.fetch` implements [fetch](https://fetch.spec.whatwg.org/).

```javascript
authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

#### Option 2: Add the access token to the HTTP request header

You can get the access token through `authgear.accessToken`. Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call only if the access token has expired. Include the access token into the Authorization header of the application request.

```javascript
authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```


# Native/Mobile App

If you are developing mobile or desktop applications, choose from one of these SDKs for your platform to get started.

{% content-ref url="/pages/JAyUCdyydxw7WN5n45Uz" %}
[Android SDK](/get-started/native-mobile-app/android)
{% endcontent-ref %}

{% content-ref url="/pages/YtcrWkc6l9FXHnGcFjaU" %}
[Flutter SDK](/get-started/native-mobile-app/flutter)
{% endcontent-ref %}

{% content-ref url="/pages/LzMAOMrajRTN1xwCp51w" %}
[iOS SDK](/get-started/native-mobile-app/ios)
{% endcontent-ref %}

{% content-ref url="/pages/Dn1akGNmYT8XruQHU4bR" %}
[React Native SDK](/get-started/native-mobile-app/react-native)
{% endcontent-ref %}

{% content-ref url="/pages/g4tCcrjleuzgU3xxOK1S" %}
[Ionic SDK](/get-started/native-mobile-app/ionic)
{% endcontent-ref %}

{% content-ref url="/pages/IKTtMHiNmizfVOt8cDPE" %}
[Xamarin SDK](/get-started/native-mobile-app/xamarin)
{% endcontent-ref %}


# iOS SDK

Integrate your iOS application with Authgear iOS SDK

This guide provides instructions on integrating Authgear with an iOS app.

The Authgear iOS SDK supports **iOS 11.0 and higher**. This tutorial builds the demo app with Apple's Observation framework (`@Observable`) and Swift Concurrency (`async`/`await`), which require **iOS 17.0 or higher**, **Xcode 16 or later**, and the **Swift 6** language mode. If you need to support older iOS versions, you can apply the same structure using `ObservableObject` and the SDK's completion handlers instead.

Follow this guide to add Authgear to your iOS app in 🕐 10 minutes.

{% hint style="info" %}
You can find the full code for the demo app for this tutorial in [this Github repo](https://github.com/authgear/authgear-example-ios/)
{% endhint %}

## Setup Application in Authgear

Sign up for an Authgear Portal account at [https://portal.authgear.com/](https://portal.authgear.com). Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an Authgear client application in the project.

### **Step 1: Create an application in the Portal**

Go to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Click **⊕Add Application** in the top toolbar.

Input the name of your application and select **Native App** as the application type. Click "Save".

![Create an application](/files/7wywVJAvi44Ch5FG30h3)

You will see a list of guides that can help you for setting up, then click "Next".

### **Step 2: Configure the application**

Here you'll need to define a custom URI scheme that Authgear will use to redirect users back to your app after authentication. For our example app, the custom URL scheme is `com.example.authgeardemo`, and the full **Redirect URI** built on top of it is `com.example.authgeardemo://host/path`. For further instructions on setting up a custom URI scheme in iOS, see the official documentation [here](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app).

Head back to Authgear Portal, and add `com.example.authgeardemo://host/path` as Redirect URI.

Click "Save" button and note the **Client ID**. and **Endpoint** for your new client application as you'll use them later in your iOS application. You can also obtain the Client ID again from the Applications list later.

![Fill in the Authorized Redirect URI](/files/PQMB3LwKqqICXuaPU6YX)

## Add Authgear to your iOS Application

In this step, we'll add user authentication to a simple iOS app using the Authgear iOS SDK and the client application we created in the previous steps.

### Pre-requisites

To follow the steps in this guide seamlessly, you should have the following:

* [Xcode](https://developer.apple.com/xcode/) 16 or later
* A project targeting iOS 17.0 or later, using the Swift 6 language mode
* Some knowledge of SwiftUI and Swift Concurrency (`async`/`await`)

### Step 1: Create new iOS project

For the purpose of this guide, we'll create a new project in Xcode. Skip this step if you're adding Authgear to your existing app.

To create a new project, open Xcode and navigate to **File** > **New** > **Project**. Create your new project with the following details:

* **Project Name:** `my_demo_app`
* choose `SwiftUI` as **Interface** Leave other fields unchanged and proceed to create the project.

<figure><img src="/files/vkjWxBinlaw2Jb6dJhtv" alt="" width="375"><figcaption><p>Xcode new project</p></figcaption></figure>

### Step 2: Install Authgear SDK

The Authgear iOS SDK makes it easy to interact with Authgear services from your iOS project.

To add Authgear SDK to your project, in Xcode navigate to **File** > **Add Package Dependencies** and enter `https://github.com/authgear/authgear-sdk-ios.git` in the Package URL text field. Select the **Up to Next Major Version** dependency rule starting from `2.0.0`.

Click **Add Package** to proceed.

<figure><img src="/files/jYZZCMRbec8jMReRyclc" alt="" width="563"><figcaption><p>Xcode package manager</p></figcaption></figure>

On the next screen, select your application under **Add to Target** then click on **Add Package**.

<figure><img src="/files/KuRcVUybigl14yNsVqwP" alt="" width="563"><figcaption><p>xcode add package</p></figcaption></figure>

Alternatively, if your project uses cocoapods, install the SDK using:

```
pod 'Authgear', :git => 'https://github.com/authgear/authgear-sdk-ios.git'
```

### Step 3: Create the authentication model

We'll keep all authentication logic in one place — an `@Observable` model that owns a single `Authgear` instance and exposes a small, UI-friendly state. The view (next step) just renders that state and calls the model's methods.

Create a new Swift file named `AuthenticationModel.swift` and add the following:

```swift
//  AuthenticationModel.swift

import Authgear
import Observation

// Sendable snapshots produced *inside* the SDK's completion handlers so that
// no non-Sendable Authgear type crosses the `await` boundary.
private enum AuthOutcome: Sendable {
    case signedIn(userID: String)
    case cancelled
    case failed(message: String)
}

private enum VoidOutcome: Sendable {
    case ok
    case failed(message: String)
}

@MainActor
@Observable
final class AuthenticationModel {
    enum State: Equatable {
        case loading
        case signedOut
        case signedIn(userID: String)
    }

    private(set) var state: State = .loading

    // Non-nil drives an error alert in the view.
    var errorMessage: String?

    private let authgear: Authgear
    private var didConfigure = false

    init() {
        authgear = Authgear(
            clientId: "<CLIENT_ID>",
            endpoint: "<AUTHGEAR_ENDPOINT>"
        )
    }

    // Configure the SDK once and restore any existing session.
    func configure() async {
        guard !didConfigure else { return }
        didConfigure = true

        let outcome: VoidOutcome = await withCheckedContinuation { continuation in
            authgear.configure { result in
                switch result {
                case .success:
                    continuation.resume(returning: .ok)
                case let .failure(error):
                    continuation.resume(returning: .failed(message: error.localizedDescription))
                }
            }
        }

        switch outcome {
        case .ok:
            // Refresh the session if the user has an existing one.
            if authgear.sessionState == .authenticated {
                await refreshCurrentUser()
            } else {
                state = .signedOut
            }
        case let .failed(message):
            errorMessage = message
            state = .signedOut
        }
    }

    // Start the interactive login flow.
    func logIn() async {
        state = .loading

        let outcome: AuthOutcome = await withCheckedContinuation { continuation in
            authgear.authenticate(redirectURI: "com.example.authgeardemo://host/path") { result in
                switch result {
                case let .success(userInfo):
                    continuation.resume(returning: .signedIn(userID: userInfo.sub))
                case let .failure(error):
                    if let authgearError = error as? AuthgearError,
                       case .cancel = authgearError {
                        continuation.resume(returning: .cancelled)
                    } else {
                        continuation.resume(returning: .failed(message: error.localizedDescription))
                    }
                }
            }
        }

        apply(outcome)
    }

    // Log out and clear the local session.
    func logOut() async {
        state = .loading

        let outcome: VoidOutcome = await withCheckedContinuation { continuation in
            authgear.logout { result in
                switch result {
                case .success:
                    continuation.resume(returning: .ok)
                case let .failure(error):
                    continuation.resume(returning: .failed(message: error.localizedDescription))
                }
            }
        }

        switch outcome {
        case .ok:
            state = .signedOut
        case let .failed(message):
            errorMessage = message
            // The SDK's session state is authoritative after a failed logout.
            if authgear.sessionState == .authenticated {
                await refreshCurrentUser()
            } else {
                state = .signedOut
            }
        }
    }

    // Open the pre-built user settings page.
    func openUserSettings() {
        authgear.open(page: .settings)
    }

    // Refresh the signed-in user's info; downgrades to signed-out if the
    // refresh token is no longer valid.
    private func refreshCurrentUser() async {
        let outcome: AuthOutcome = await withCheckedContinuation { continuation in
            authgear.fetchUserInfo { result in
                switch result {
                case let .success(userInfo):
                    continuation.resume(returning: .signedIn(userID: userInfo.sub))
                case let .failure(error):
                    continuation.resume(returning: .failed(message: error.localizedDescription))
                }
            }
        }

        apply(outcome)
    }

    private func apply(_ outcome: AuthOutcome) {
        switch outcome {
        case let .signedIn(userID):
            state = .signedIn(userID: userID)
        case .cancelled:
            state = .signedOut
        case let .failed(message):
            errorMessage = message
            state = .signedOut
        }
    }
}
```

Replace **"\<CLIENT\_ID>"** and **"\<AUTHGEAR\_ENDPOINT>"** with the client ID and endpoint from the configuration page of the client project you [created earlier](#step-2-configure-the-application).

{% hint style="info" %}
**Why the `AuthOutcome` / `VoidOutcome` enums?** The Authgear SDK ships completion-handler APIs, and its result types (`UserInfo`, `SessionState`, `AuthgearError`) are not `Sendable`. To stay clean under the Swift 6 language mode, each call is wrapped with `withCheckedContinuation`, and the completion closure captures **only** the continuation and resumes with a small `Sendable` value extracted inside the closure. That way no non-`Sendable` SDK type crosses the `await` boundary, so the model compiles under strict concurrency checking without extra annotations.

In the demo app repo, the client ID, endpoint, and redirect URI are factored into a small `Constants.swift` enum instead of being hard-coded in the initializer — a good pattern for real projects.
{% endhint %}

Here's what each method does:

* `configure()` — initializes the SDK once (guarded by `didConfigure`) and, if a previous session exists, refreshes it so returning users land straight on the signed-in screen.
* `logIn()` — runs the interactive authentication flow. User cancellation is handled distinctly from real errors, so cancelling the login sheet doesn't raise an error alert.
* `logOut()` — ends the current session. On failure it treats the SDK's `sessionState` as authoritative rather than assuming the user is signed out.
* `openUserSettings()` — opens Authgear's pre-built User Settings page, where users can view and modify their profile attributes and security settings.
* `refreshCurrentUser()` — calls `fetchUserInfo` to update `sessionState` and retrieve the user's `sub` (a unique user ID). This also detects a session that has been revoked remotely: `sessionState` becomes `.noSession` and the model drops back to signed-out.

### Step 4: Build the ContentView

Now replace the contents of `ContentView.swift` with a state-driven view that observes the model. It triggers `configure()` from `.task` when the view first appears, wraps the async actions in `Task {}`, and surfaces any `errorMessage` with an `.alert`:

```swift
//  ContentView.swift

import SwiftUI

struct ContentView: View {
    @State private var model = AuthenticationModel()

    var body: some View {
        VStack(spacing: 16) {
            switch model.state {
            case .loading:
                ProgressView()
            case .signedOut:
                signedOutView
            case let .signedIn(userID):
                signedInView(userID: userID)
            }
        }
        .padding()
        .task {
            await model.configure()
        }
        .alert(
            "Something went wrong",
            isPresented: Binding(
                get: { model.errorMessage != nil },
                set: { isPresented in
                    if !isPresented { model.errorMessage = nil }
                }
            ),
            presenting: model.errorMessage
        ) { _ in
            Button("OK", role: .cancel) {}
        } message: { message in
            Text(message)
        }
    }

    private var signedOutView: some View {
        VStack(spacing: 16) {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("My Demo App")
            Button("Login") {
                Task { await model.logIn() }
            }
        }
    }

    private func signedInView(userID: String) -> some View {
        VStack(spacing: 16) {
            Text("Welcome user \(userID)")
            Button("User Settings") {
                model.openUserSettings()
            }
            Button("Logout") {
                Task { await model.logOut() }
            }
        }
    }
}

#Preview {
    ContentView()
}
```

The view has no direct SDK calls — it only reads `model.state` and invokes the model's methods. When signed out it shows the **Login** button; while an operation is in flight it shows a `ProgressView`; once signed in it greets the user and offers **User Settings** and **Logout**.

#### Checkpoint

Run your app now. It should launch and settle on the **Login** screen. Tapping **Login** will attempt to open the authentication page — but it won't return to your app yet. We'll register the redirect URI scheme next.

<figure><img src="/files/mRbLYSaZM4c83GcpmQF3" alt="" width="188"><figcaption></figcaption></figure>

### Step 5: Register the URI Scheme for the Redirect URI

For Authgear to redirect back into your app after authentication, register the custom URL **scheme** — the part of the redirect URI before `://`. For our redirect URI `com.example.authgeardemo://host/path`, the scheme is `com.example.authgeardemo`.

Open your project's `Info.plist` or project settings UI in Xcode and add the following:

{% tabs %}
{% tab title="Xcode" %}
Navigate to **Targets** > **{Your project}** > **Info** and expand the **URL Types** section.

Add a new URL scheme with the following details:

**Identifier**: `CFBundleURLTypes`

**URL Schemes**: `com.example.authgeardemo`

**Role**: Editor

<figure><img src="/files/YMJyKXqYunSMnOS86HpO" alt=""><figcaption><p>xcode project properties</p></figcaption></figure>
{% endtab %}

{% tab title="Info.plist" %}

<pre><code>&#x3C;?xml version="1.0" encoding="UTF-8"?>
&#x3C;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
&#x3C;plist version="1.0">
<strong>    &#x3C;dict>
</strong>        &#x3C;!-- Other entries -->
        &#x3C;key>CFBundleURLTypes&#x3C;/key>
        &#x3C;array>
            &#x3C;dict>
                &#x3C;key>CFBundleTypeRole&#x3C;/key>
                &#x3C;string>Editor&#x3C;/string>
                &#x3C;key>CFBundleURLName&#x3C;/key>
                &#x3C;string>CFBundleURLTypes&#x3C;/string>
                &#x3C;key>CFBundleURLSchemes&#x3C;/key>
                &#x3C;array>
                    &#x3C;string>com.example.authgeardemo&#x3C;/string>
                &#x3C;/array>
            &#x3C;/dict>
        &#x3C;/array>
    &#x3C;/dict>
&#x3C;/plist>
</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Register the bare scheme `com.example.authgeardemo` in `CFBundleURLSchemes` — **not** the full redirect URI `com.example.authgeardemo://host/path`. `CFBundleURLSchemes` expects only the scheme (the text before `://`). The full redirect URI is what you register in the Authgear Portal and pass to `authenticate(redirectURI:)`.
{% endhint %}

### Step 6: Run and test

Run your app again and try logging in. Because the redirect scheme is now registered, Authgear will redirect back to your app after authentication and the view will switch to the signed-in screen.

Try the full flow:

* **Login** — completes authentication and shows "Welcome user \<sub>".
* **User Settings** — opens Authgear's pre-built settings page.
* **Logout** — ends the session and returns to the Login screen.

If configuration fails (for example, a wrong endpoint) or authentication errors out, the `.alert` you added in Step 4 surfaces the message instead of failing silently.

## Understanding session state

You may want to know whether the user has logged in (for example, to show a Login button only when they haven't).

The `sessionState` reflects the user's logged-in state in the SDK's local state. Even if `sessionState` is `.authenticated`, the session may be invalid if it was revoked remotely. Hence, after initializing the SDK, call `fetchUserInfo` to update `sessionState` as soon as it is proper to do so — which is exactly what `configure()` → `refreshCurrentUser()` does in the model above.

The value of `sessionState` can be `.unknown`, `.noSession` or `.authenticated`. Initially it is `.unknown`. After a call to `authgear.configure`, it becomes `.authenticated` if a previous session was found, or `.noSession` if no such session existed.

## Using the Access Token in HTTP Requests

Call `refreshAccessTokenIfNeeded` every time before using the access token; it checks and makes a network call only if the access token has expired. Then include the access token in the `Authorization` header of your request. As with the other SDK calls, you can bridge the completion handler into `async`/`await` with a continuation:

```swift
func callProtectedAPI() async throws {
    try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
        authgear.refreshAccessTokenIfNeeded { result in
            continuation.resume(with: result)
        }
    }

    // The access token is ready to use. It can be empty if the user is not
    // logged in or the session is invalid.
    guard let accessToken = authgear.accessToken else {
        // The user is not logged in, or the token is expired.
        return
    }

    // Example only — use your own networking library.
    var urlRequest = URLRequest(url: URL(string: "YOUR_SERVER_URL")!)
    urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "authorization")
    // ... continue making your request
}
```

## Next steps

To protect your application server from unauthorized access. You will need to **integrate your backend with Authgear**.

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## iOS SDK Reference

For detailed documentation on the iOS SDK, visit [iOS SDK Reference](https://authgear.github.io/authgear-sdk-ios/).


# Android SDK

How to use authgear android SDK

This guide provides instructions on integrating Authgear with an Android app. Supported platforms include:

* Android 5.0 (API 21) or higher

Follow this guide to add Authgear to your Android app in 🕐 10 minutes.

{% hint style="info" %}
You can find the full code for the demo app for this tutorial in [this Github repo](https://github.com/authgear/authgear-example-android)
{% endhint %}

## Setup Application in Authgear

Sign up for an Authgear Portal account at [https://portal.authgear.com](https://portal.authgear.com/). Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an application in the project.

### **Step 1: Create an application in the Portal**

Go to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Click **⊕Add Application** in the top toolbar.

Input the name of your application and select **Native App** as the application type. Click "Save".

You will see a list of guides that can help you for setting up, then click "Next".

![Create an application](/files/7wywVJAvi44Ch5FG30h3)

### **Step 2: Configure the application**

Define a custom URI scheme that Authgear will use to redirect users back to your app after they have authenticated. The scheme should be based on the package name for your Android app. For the demo app, we'll be creating in this guide the scheme is: `com.example.authgeardemo://host/path`. To learn more about setting up a custom URI scheme in Android, see the official documentation [here](https://developer.android.com/training/app-links/deep-linking).

Head back to Authgear Portal, and add the URL scheme you have defined as a Redirect URI. For our demo app, add the following URI:

```
com.example.authgeardemo://host/path
```

Click "Save" in the top toolbar and note the **Client ID** as you'll use it later in your Android app. You can also obtain it again from the Applications list later.

![Fill in the Redirect URI](/files/PQMB3LwKqqICXuaPU6YX)

## Add Authgear to an Android Application

In this step, we'll add user authentication to an Android application using the Authgear client application we set up in the previous steps.

The Authgear SDK works with both **Jetpack Compose** and the classic **View/XML** UI toolkit. [Step 4](#step-4-implement-authentication) below provides the implementation for each — pick the tab that matches your app.

### Pre-requisites

To follow along, you need to have the following:

* Android Studio installed on your computer
* Basic knowledge of Kotlin or Java

### Step 1: Create an Android App project

For the purpose of this guide, we'll be creating a new simple Android app project. Feel free to skip this step if you are adding Authgear to your existing app.

Open Android Studio and create a new project with the following details:

* On the Activity selection screen, choose **Empty Activity** (Jetpack Compose) or **Empty Views Activity** (XML) — this guide covers both. The current Android Studio default, **Empty Activity**, uses Jetpack Compose.
* **Name**: My Demo App
* **Build configuration language**: Groovy DSL

{% hint style="info" %}
The reason for recommending you use Groovy DSL as **Build configuration language** for this guide is to make it easier to copy and paste the Gradle configurations we've provided without having to make many rewrites.
{% endhint %}

### Step 2: Add Authgear SDK to your project

The Authgear Android SDK makes it easier to interact with Authgear endpoints and services from your Android app.

The SDK is published on Maven Central. Make sure the `mavenCentral()` repository is available to your project. It is included by default in new Android Studio projects; if it is missing, add it to your project's `settings.gradle` file:

```groovy
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
    }
}
```

Next, add the Authgear SDK to the `dependencies` section of your app-level (`/app/build.gradle`) `build.gradle`:

```groovy
dependencies {
    // Other implementations
    implementation 'com.authgear:android-sdk:3.0.0'
}
```

`3.0.0` is the latest version at the time of writing. Check for newer releases on [Maven Central](https://central.sonatype.com/artifact/com.authgear/android-sdk) or the [release tags](https://github.com/authgear/authgear-sdk-android/tags).

#### Enable Java 8+ API desugaring support

To enable Java 8+ API desugaring support for your project, make the following changes to the app-level `build.gradle` file.

1. Add `coreLibraryDesugaringEnabled true` to the `android` > `compileOptions` section:

```gradle
compileOptions {
    coreLibraryDesugaringEnabled true
}
```

2. Then add the coreLibraryDesugaring to the dependencies section:

```gradle
dependencies {
    // Other implementations
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
}
```

Learn more about Java 8+ API desugaring support [here](https://developer.android.com/studio/write/java8-support#library-desugaring).

{% hint style="warning" %}
Your app's **window theme** must descend from a `Theme.AppCompat` theme. The SDK's authentication screens (`OAuthActivity`, `OAuthRedirectActivity`) are `AppCompatActivity`, and Android throws `You need to use a Theme.AppCompat theme (or descendant) with this activity` on login if the app theme is a plain framework theme.

An **Empty Views Activity** project already uses a Material Components (AppCompat-descendant) theme. If you created a **Jetpack Compose** project, open `res/values/themes.xml` and make sure your app theme uses an AppCompat or Material 3 parent, for example:

```xml
<resources>
    <style name="Theme.MyDemoApp" parent="Theme.AppCompat.DayNight.NoActionBar" />
</resources>
```

Your Compose UI still themes itself; this is only the window theme.
{% endhint %}

Sync Gradle to continue.

### Step 3: Set up the Redirect URI

Add the following activity entry to the `AndroidManifest.xml` of your app. The intent system would dispatch the redirect URI to `OAuthRedirectActivity` and the SDK would handle the rest.

<pre class="language-xml"><code class="lang-xml">&#x3C;!-- Your application configuration. Omitted here for brevity -->
&#x3C;application>
<strong>    &#x3C;!-- Other activities or entries -->
</strong>
    &#x3C;!-- Add the following activity -->
    &#x3C;!-- android:exported="true" is required -->
    &#x3C;!-- See https://developer.android.com/about/versions/12/behavior-changes-12#exported -->
    &#x3C;activity android:name="com.oursky.authgear.OAuthRedirectActivity"
        android:exported="true"
        android:launchMode="singleTask">
        &#x3C;intent-filter>
            &#x3C;action android:name="android.intent.action.VIEW" />
            &#x3C;category android:name="android.intent.category.DEFAULT" />
            &#x3C;category android:name="android.intent.category.BROWSABLE" />
            &#x3C;!-- Configure data to be the exact redirect URI your app uses. -->
            &#x3C;!-- Here, we are using com.example.authgeardemo://host/path as configured in the portal -->
            &#x3C;!-- NOTE: The redirectURI supplied in AuthenticateOptions *has* to match as well -->
            &#x3C;data android:scheme="com.example.authgeardemo"
                android:host="host"
                android:pathPrefix="/path"/>
        &#x3C;/intent-filter>
    &#x3C;/activity>
&#x3C;/application>
</code></pre>

#### Targeting API level 30 or above (Android 11 or above)

If your Android app is targeting API level 30 or above (Android 11 or above), you need to add a `queries` section to `AndroidManifest.xml`.

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Other elements such <application> -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>
```

### Step 4: Implement authentication

Now initialize Authgear and build the screen with a **Login** button, plus **User Settings** and **Logout** for logged-in users. Choose the tab that matches your UI toolkit — both produce the same flow.

Replace `<CLIENT_ID>` and `<AUTHGEAR_ENDPOINT>` in the code below with the values from the configuration page of your Authgear client application.

{% tabs %}
{% tab title="Jetpack Compose" %}
The SDK exposes `suspend` functions, so you call them from Compose with `rememberCoroutineScope()` and `LaunchedEffect`.

Create the Authgear instance in your `MainActivity` and host your Compose UI:

```kotlin
class MainActivity : ComponentActivity() {

    private lateinit var authgear: Authgear

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        authgear = Authgear(application, "<CLIENT_ID>", "<AUTHGEAR_ENDPOINT>")
        setContent {
            MaterialTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    MainScreen(authgear)
                }
            }
        }
    }
}
```

The `MainScreen` composable configures Authgear when it first appears (restoring an existing session), then shows a **Login** button or the logged-in view based on `sessionState`:

```kotlin
private const val REDIRECT_URI = "com.example.authgeardemo://host/path"
private const val TAG = "AuthgearDemo"

@Composable
fun MainScreen(authgear: Authgear) {
    val scope = rememberCoroutineScope()
    var configuring by remember { mutableStateOf(true) }
    var email by remember { mutableStateOf<String?>(null) }
    var busy by remember { mutableStateOf(false) }

    // Configure once when the screen first appears; restore an existing session.
    LaunchedEffect(Unit) {
        try {
            authgear.configure()
            if (authgear.sessionState == SessionState.AUTHENTICATED) {
                email = authgear.fetchUserInfo().email
            }
        } catch (e: Throwable) {
            Log.e(TAG, "configure failed", e)
        }
        configuring = false
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        when {
            configuring -> CircularProgressIndicator()

            email != null -> {
                Text("Welcome, $email")
                OutlinedButton(
                    enabled = !busy,
                    onClick = { authgear.open(Page.SETTINGS) },
                    modifier = Modifier.padding(top = 16.dp),
                ) { Text("User Settings") }
                Button(
                    enabled = !busy,
                    onClick = {
                        scope.launch {
                            busy = true
                            try {
                                authgear.logout()
                                email = null
                            } catch (e: Throwable) {
                                Log.e(TAG, "logout failed", e)
                            }
                            busy = false
                        }
                    },
                ) { Text("Logout") }
            }

            else -> {
                Button(
                    enabled = !busy,
                    onClick = {
                        scope.launch {
                            busy = true
                            try {
                                email = authgear.authenticate(AuthenticateOptions(REDIRECT_URI)).email
                            } catch (e: Throwable) {
                                Log.e(TAG, "login failed", e)
                            }
                            busy = false
                        }
                    },
                ) { Text("Login") }
            }
        }
    }
}
```

{% hint style="info" %}
`configure`, `authenticate`, `logout`, and `fetchUserInfo` are `suspend` extension functions on `Authgear` — import them (e.g. `import com.oursky.authgear.configure`) if they show as unresolved. Import any other unresolved class as well.
{% endhint %}
{% endtab %}

{% tab title="Views (XML)" %}
**Enable View Binding**

Add the following to your app-level (`/app/build.gradle`) `build.gradle` under the `android` block:

```groovy
buildFeatures {
    viewBinding = true
}
```

**Build the layout**

Open `res/layout/activity_main.xml`, delete the default "Hello World!" TextView, and add a title, a Login button, and the logged-in views (a progress bar, welcome text, User Settings and Logout buttons) grouped so they can be shown/hidden together:

```xml
<TextView
    android:id="@+id/app_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="My Demo App!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/login_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Login"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/app_title" />

<ProgressBar
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="48dp"
    android:indeterminate="true"
    android:visibility="invisible"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    android:id="@+id/welcome_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="welcome user"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/login_btn" />

<Button
    android:id="@+id/user_settings_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="User Settings"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/welcome_text" />

<Button
    android:id="@+id/logout_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Logout"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/user_settings_btn" />

<androidx.constraintlayout.widget.Group
    android:id="@+id/logged_in_views"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    app:constraint_referenced_ids="welcome_text,user_settings_btn,logout_btn" />
```

The complete `activity_main.xml` is available [here](https://github.com/authgear/authgear-example-android/blob/main/app/src/main/res/layout/activity_main.xml).

**Initialize Authgear and wire the buttons**

In `MainActivity.kt`, initialize Authgear with view binding, call `configure()`, and connect the buttons:

```kotlin
class MainActivity : AppCompatActivity() {

    private lateinit var authgear: Authgear
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        authgear = Authgear(application, "<CLIENT_ID>", "<AUTHGEAR_ENDPOINT>")
        authgear.configure(object : OnConfigureListener {
            override fun onConfigured() {
                updateUi(authgear)
            }

            override fun onConfigurationFailed(throwable: Throwable) {
                Log.d("TAG", throwable.toString())
                // Something went wrong, check the client ID or endpoint.
            }
        })

        binding.loginBtn.setOnClickListener { startLogin() }
        binding.logoutBtn.setOnClickListener { logout() }
        binding.userSettingsBtn.setOnClickListener { openUserSettings() }
    }
}
```

**Implement the actions**

Add these methods to `MainActivity`. `startLogin()` starts the authentication flow, `updateUi()` reflects the session state (and fetches the user's email), `logout()` ends the session, and `openUserSettings()` opens the pre-built settings page:

```kotlin
fun startLogin() {
    binding.progressBar.visibility = View.VISIBLE
    val options = AuthenticateOptions("com.example.authgeardemo://host/path")
    authgear.authenticate(options, object : OnAuthenticateListener {
        override fun onAuthenticated(userInfo: UserInfo) {
            updateUi(authgear)
        }

        override fun onAuthenticationFailed(throwable: Throwable) {
            binding.progressBar.visibility = View.INVISIBLE
            Log.d("TAG", throwable.toString())
        }
    })
}

fun updateUi(authgear: Authgear) {
    val state = authgear.sessionState
    if (state == SessionState.AUTHENTICATED) {
        binding.loginBtn.visibility = View.GONE
        binding.loggedInViews.visibility = View.VISIBLE
        // Get userInfo and display in welcome text
        authgear.fetchUserInfo(object : OnFetchUserInfoListener {
            override fun onFetchedUserInfo(userInfo: UserInfo) {
                binding.welcomeText.text = userInfo.email
            }

            override fun onFetchingUserInfoFailed(throwable: Throwable) {
                Log.d("TAG", "Failed to fetch UserInfo")
            }
        })
    } else {
        binding.loggedInViews.visibility = View.GONE
        binding.loginBtn.visibility = View.VISIBLE
    }
    binding.progressBar.visibility = View.INVISIBLE
}

fun logout() {
    binding.progressBar.visibility = View.VISIBLE
    authgear.logout(true, object : OnLogoutListener {
        override fun onLogout() {
            updateUi(authgear)
        }

        override fun onLogoutFailed(throwable: Throwable) {
            Log.d("TAG", throwable.toString())
        }
    })
}

fun openUserSettings() {
    authgear.open(Page.SETTINGS)
}
```

{% hint style="info" %}
Import any class that shows as unresolved.
{% endhint %}
{% endtab %}
{% endtabs %}

#### Checkpoint

Run your app on a device or emulator and tap **Login**. Because you set up the Redirect URI in [Step 3](#step-3-set-up-the-redirect-uri), the Authgear login page opens, and on success you're returned to the app showing the user's email with the **User Settings** and **Logout** buttons.

<figure><img src="/files/Q1DIHcGWDtL2a2VDSuUS" alt="" width="188"><figcaption><p>Demo app screenshot</p></figcaption></figure>

### Additional Actions

#### Get the Logged In State

You can use the user's logged-in state to determine whether a user is logged in and display content like their user info and a logout button, as we did in [Step 4](#step-4-implement-authentication). The `SessionState` reflects the user logged-in state in the SDK local state. That means even if the `SessionState` is `AUTHENTICATED`, the session may be invalid if it is revoked remotely. After initializing the Authgear SDK, call `fetchUserInfo` to update the `SessionState` as soon as it is proper to do so.

```kotlin
// After authgear.configure, it only reflect SDK local state.
// value can be NO_SESSION or AUTHENTICATED
val state = authgear.sessionState
```

The value of `SessionState` can be `UNKNOWN`, `NO_SESSION` or `AUTHENTICATED`. Initially, the `sessionState` is `UNKNOWN`. After a call to `authgear.configure`, the session state would become `AUTHENTICATED` if a previous session was found, or `NO_SESSION` if such session was not found.

#### Fetching User Info

In some cases, you may need to obtain current user info through the SDK. (e.g. Display email address in the UI as we did in [Step 4](#step-4-implement-authentication)). Use the `fetchUserInfo` function to obtain the user info, see [example](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo).

#### Using the Access Token in HTTP Requests

Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call only if the access token has expired. Include the access token in the Authorization header of your application request. If you are using OKHttp in your project, you can also use the interceptor extension provided by the SDK, see [detail](/get-started/native-mobile-app/android/okhttp-interceptor-extension).

{% hint style="info" %}
The access token is a [JSON Web Token (JWT)](https://en.wikipedia.org/wiki/JSON_Web_Token).
{% endhint %}

```kotlin
try {
    authgear.refreshAccessTokenIfNeededSync()
} catch (e: OAuthException) {
    // Something went wrong
}

val accessToken = authgear.accessToken 
if (accessToken == null) {
    // The user is not logged in, or the token is expired.
    // It is up to the caller to decide how to handle this situation.
    // Typically, the request could be aborted
    // immediately as the response would be 401 anyways.
    return
}

val headers = mutableMapOf<String, String>()
headers["authorization"] = "Bearer $accessToken"

// Submit the request with the headers..
```

## **Next steps** <a href="#secure-your-application-server-with-authgear" id="secure-your-application-server-with-authgear"></a>

To protect your application server from unauthorized access. You will need to [integrate Authgear to your backend](/get-started/backend-api).

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## Android SDK Reference

For detailed documentation on the Android SDK, visit [Android SDK Reference](https://authgear.github.io/authgear-sdk-android/)


# Android OKHttp Interceptor Extension (Optional)

The Authgear Android SDK provides an optional `Okhttp` interceptor which handles everything from refreshing the access token to putting the access token in the header.

## Get the Extension

The extension is included in the SDK. Please refer to the above section for getting the SDK.

## Usage

Configure `OkHttpClient` to use `AuthgearInterceptor` as follows:

```java
Authgear authgear = // Obtain the authgear instance.
OKHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(AuthgearInterceptor(authgear))
            .build()
```

The client would then include the access token in every request and refresh the access token when necessary before the requests.


# Flutter SDK

How to integrate with a Flutter app

This guide provides instructions on integrating Authgear with a Flutter app. Supported platforms include:

* Flutter 3.0.0 or higher
* Android minimum SDK 30 (Android 11 or later)

Follow this guide to add Authgear to your Flutter app in 🕐 10 minutes.

{% hint style="info" %}
You can find the full code for the demo app for this tutorial in the Github repo [here](https://github.com/authgear/authgear_example_flutter).
{% endhint %}

## Setup Application in Authgear

Signup for an Authgear Portal account in <https://portal.authgear.com/>. Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an application in the project.

### **Step 1: Create an application in the Portal**

Go to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt="" width="563"><figcaption></figcaption></figure>

You will see the **"New Application"** page or Click **⊕Add Application** in the top tool bar.

Input the name of your application and select **Native App** as the application type. Click "Save".

You will see a list of guides that can help you for setting up, then click "Next".

<figure><img src="/files/7wywVJAvi44Ch5FG30h3" alt="" width="563"><figcaption><p>Create Application</p></figcaption></figure>

### **Step 2: Configure the application**

In your IDE, define a custom URI scheme that Authgear will use to redirect users back to your app after they have authenticated , For your demo application for this guide, the URI scheme will be: `com.example.authgeardemo.flutter://host/path`. To learn more about setting up URI Scheme in flutter, visit <https://docs.flutter.dev/ui/navigation/deep-linking>.

Head back to Authgear Portal, fill in the Redirect URI that you have defined in the previous step.

Click "Save" and note the **Client ID**. and **Endpoint**. You can also obtain them from the Applications list later.

![Fill in the Redirect URI](/files/0YZ2Eqt7Ul3e5NM9haum)

## Add User Authentication to Flutter App using Authgear SDK

In this part of the guide, we'll add user Authentication to a simple demo app using the Authgear Flutter SDK.

The demo app will have a login button that users can click to initiate the authentication flow. It will also include a group of widgets for greeting logged-in users, opening the user settings page, and logout.

### Step 1: Create Flutter App

If you're new to Flutter, follow the official [Flutter documentation](https://docs.flutter.dev/get-started/install) to see how you can install Flutter on your computer.

Run the following command to create a new Flutter project:

```bash
flutter create myapp
cd myapp
```

### Step 2: Install Authgear SDK

Run the following command from the root directory of your Flutter project to install the Authgear SDK:

```bash
flutter pub add flutter_authgear
```

### Step 3: Initialize Authgear

First, import Authgear at the top of `lib/main.dart`:

```dart
import 'package:flutter_authgear/flutter_authgear.dart';
```

Next, create a field variable `_authgear` of type `Authgear` and an `_init()` method in your page's State class:

```dart
late Authgear _authgear;

Future<void> _init() async {
  _authgear = Authgear(endpoint: "<AUTHGEAR_ENDPOINT>", clientID: "<CLIENT_ID>");
  await _authgear.configure();
}
```

{% hint style="info" %}
**Note:** The **State Class** is the class associated with a stateful page (widget). For example, the following show part of the State class for our demo app after adding the `_authgear` field and an `_init()` method:

```dart
class _MyHomePageState extends State<MyHomePage> {
  late Authgear _authgear;
  
  Future<void> _init() async {
    _authgear = Authgear(endpoint: "<AUTHGEAR_ENDPOINT>", clientID: "<CLIENT_ID>");
    await _authgear.configure();
  }
  
  @override
  Widget build(BuildContext context) {
  //other codes including wigdets for UI
  ...
  }
}
```

{% endhint %}

Replace "\<AUTHGEAR\_ENDPOINT>" and "\<ClIENT\_ID>" with the client ID and endpoint for the client application you created earlier.

Finally, override the `initState()` method for your State class to call your new `_init()` method. This will initiate a new instance of the Authgear SDK that you'll use to perform operations like starting an authentication flow.

```dart
@override
void initState() {
  super.initState();
  _init();
}
```

### Step 4: Add Login Button

In this step, we'll add a login button and other UI widgets to our demo application.

To do that, first, add a `_userInfo` field variable to the State class:

```dart
UserInfo? _userInfo;
```

Our demo app will use the value of the `_userInfo` variable to determine when to show a login button or the group of widgets for logged-in users.

Next, replace the widget in `body` attribute of `Scaffold` with the following:

```dart
Center(
    child: (_userInfo != null) ?
        loggedInUserScreen(_userInfo!.sub)
        :
        Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
      Text("My Demo App"),
      TextButton(
      onPressed: _onPressedAuthenticate,
      child: Text("Login"),
    )])
    ,
)
```

Now implement the `loggedInUserScreen()` custom widget like this within the State class:

```dart
Widget loggedInUserScreen(String userId) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Text("Welcome user $userId"),
      TextButton(
          onPressed: _onPressedSettings,
          child: Text("User Settings"),
        ),
      TextButton(
          onPressed: _onPressedLogout,
          child: Text("Logout"),
        )
    ],
  );
}
```

In the next step, we'll implement the `_onPressedAuthenticate()` method.

### Step 5: Start Authentication Flow

Here we will implement the `_onPressedAuthenticate()` method that will be called when a user clicks on the Login button.

Add the following method in the State class:

```dart
Future<void> _onPressedAuthenticate() async {
  try {
    final userInfo = await _authgear.authenticate(redirectURI: "com.example.authgeardemo.flutter://host/path");
    setState(() {
      _userInfo = userInfo;
    });
  } catch (e) {
    debugPrint(e.toString());
  }
}
```

The above code calls the `authenticate()` method of the Authgear SDK. This will start a new authentication flow. Replace the value for the redirectURI argument with the redirect URI you registered in your client application.

Create empty `_onPressedSettings()` and `_onPressedLogout()` methods in your State class for now so that you can build application:

```dart
Future<void> _onPressedSettings() async {

}

Future<void> _onPressedLogout() async {

}
```

At this point, the full code for main.dart should look like this:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_authgear/flutter_authgear.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Authgear _authgear;
  UserInfo? _userInfo;

  @override
  void initState() {
    super.initState();
    _init();
  }

  Future<void> _init() async {
    _authgear = Authgear(endpoint: "<AUTHGEAR_ENDPOINT>", clientID: "<CLIENT_ID>");
    await _authgear.configure();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "MyApp",
      home: Scaffold(
        appBar: AppBar(title: const Text("MyApp")),
        body: Center(
          child: (_userInfo != null) ?
              loggedInUserScreen(_userInfo!.sub)
              :
              Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                Text("My Demo App"),
                TextButton(
                onPressed: _onPressedAuthenticate,
                child: Text("Login"),
              )])
          ,
        )
      ),
    );
  }

  Future<void> _onPressedAuthenticate() async {
    try {
      final userInfo = await _authgear.authenticate(redirectURI: "com.example.authgeardemo.flutter://host/path");
      setState(() {
        _userInfo = userInfo;
      });
    } catch (e) {
      debugPrint(e.toString());
    }
  }

  Future<void> _onPressedSettings() async {

  }

  Future<void> _onPressedLogout() async {
    
  }

  Widget loggedInUserScreen(String userId) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text("Welcome user $userId"),
        TextButton(
            onPressed: _onPressedSettings,
            child: Text("User Settings"),
          ),
        TextButton(
            onPressed: _onPressedLogout,
            child: Text("Logout"),
          )
      ],
    );
  }
}
```

**Checkpoint**

At this point, you can save your work and run the following command to test your app on a mobile device or emulator:

```bash
flutter run
```

When your app runs, you should see the Login button, clicking on it should start a new Authentication. However, you may not be able to complete authentication because we're yet to configure our app to handle redirect from Authgear.

<figure><img src="/files/KCRXc84LmpJzs6z2UqLa" alt="" width="188"><figcaption><p>demo app preview</p></figcaption></figure>

## Step 6: Setup redirect URI for your app

To finish the integration, setup the app to handle the redirectURI specified in the application. This part requires platform specific integration.

Here you declare the URL schemes supported by your app, so the device can redirect the user to the app after authentication using the redirect URI.

#### Android

Add the following `<activity>` entry to the `/android/app/src/main/AndroidManifest.xml` of your app. The intent system would dispatch the redirect URI to `OAuthRedirectActivity` and the SDK would handle the rest.

```markup
<!-- Your application configuration. Omitted here for brevity -->
<application>
  <!-- Other activities or entries -->

  <!-- Add the following activity -->
  <!-- android:exported="true" is required -->
  <!-- See https://developer.android.com/about/versions/12/behavior-changes-12#exported -->
  <activity android:name="com.authgear.flutter.OAuthRedirectActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!-- Configure data to be the exact redirect URI your app uses. -->
                <!-- Here, we are using com.example.authgeardemo.flutter://host/path as configured in the portal -->
                <!-- NOTE: The redirectURI supplied in AuthenticateOptions *has* to match as well -->
                <data android:scheme="com.example.authgeardemo.flutter"
                    android:host="host"
                    android:pathPrefix="/path"/>
            </intent-filter>
  </activity>
</application>
```

You also need to add a `queries` section to `AndroidManifest.xml`.

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Other elements such <application> -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>
```

Remove the following line from `AndroidManifest.xml` because this setting conflicts with the SDK:

```xml
android:taskAffinity=""
```

Next, open `/android/app/build.gradle` and set `minSdk` to `30`:

```gradle
android {
    ...
    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 30 // specify minSdk to 30
        targetSdk = flutter.targetSdkVersion
        versionCode = flutter.versionCode
        versionName = flutter.versionName
    }
}
```

{% hint style="info" %}
To learn more about why minSdk is set to 30, see <https://developer.android.com/privacy-and-security/risks/strandhogg#mitigations>
{% endhint %}

#### iOS

#### Declare URL Handling in Info.plist

In the `Info.plist` in your project's `ios` folder, add the matching redirect URI by adding the key `CFBundleURLTypes` and the values inside `<dict>` as shown as the following example.

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
      <!-- Other entries -->
      <key>CFBundleURLTypes</key>
      <array>
              <dict>
                      <key>CFBundleTypeRole</key>
                      <string>Editor</string>
                      <key>CFBundleURLSchemes</key>
                      <array>
                              <string>com.example.authgeardemo.flutter://host/path</string>
                              <!-- Put the redirect URI your app uses here. -->
                      </array>
              </dict>
      </array>
</dict>
</plist>
```

Now if you run your app again, you should be able to login, be redirected back to your app and view the group of widgets that's for logged-in users.

### Step 7: Logout

To log out the user from the current app session, you need to invoke the`logout` method of the SDK.

Update the empty `_onPressedLogout` you added earlier so that it calls the `logout` method.

```dart
Future<void> _onPressedLogout() async {
  await _authgear.logout();
  setState(() {
      _userInfo = null;
  });
}
```

### Step 8: Open User Settings Page

Authgear provides a pre-built user settings page from which your users can view and edit their profile details, and change security settings like password, and 2FA.

The SDK includes a method that you can use to easily open this user settings page.

Update the empty `_onPressedSettings` method you added earlier so it initiates the process of opening the user settings page.

```dart
Future<void> _onPressedSettings() async {
  await _authgear.open(page: SettingsPage.settings);
}
```

### Step 9: Show User Info

At this point, our application already shows the current user's info (their unique ID, `sub`). However, the Authgear SDK includes a `getUserInfo()` method that you can call explicitly to get an object that contains the current user's information like their email, phone number, name, etc.

Calling this getUserInfo() method can also refresh the current user's access token to make sure that their session state is really `authenticated`, that is, an logged-in user is not using an expired access token.

Update your `_init()` method to check the user's logged-in state and fetch their user info when the state is `SessionState.authenticated`.

```dart
Future<void> _init() async {
  _authgear = Authgear(endpoint: "<AUTHGEAR_ENDPOINT>", clientID: "<CLIENT_ID>");
  await _authgear.configure();
  if (_authgear.sessionState == SessionState.authenticated) {
    final userInfo = await _authgear.getUserInfo();
    setState(() {
      _userInfo = userInfo;
    });
  }
}
```

### Additional Actions

#### Get the Logged In State

When you start launching the application. You may want to know if the user has logged in. (e.g. Show users the login page if they haven't logged in). The `sessionState` reflects the user logged in state in the SDK local state. That means even the `sessionState` is `SessionState.authenticated`, the session may be invalid if it is revoked remotely. After initializing the Authgear SDK, call `getUserInfo` to update the `sessionState` as soon as it is proper to do so.

```dart
// After authgear.configure, it only reflect SDK local state.
// value can be SessionState.noSession or SessionState.authenticated
SessionState state = _authgear.sessionState;

UserInfo? userInfo;
try {
  userInfo = await _authgear.getUserInfo();
  // read the userInfo if needed
} catch (e) {
  // failed to fetch user info
  // the refresh token maybe expired or revoked
}
// sessionState is now up to date
// it will change to SessionState.noSession if the session is invalid
state = _authgear.sessionState;
```

The value of `sessionState` can be `SessionState.unknown`, `SessionState.noSession` or `SessionState.authenticated`. Initially, the `sessionState` is `SessionState.unknown`. After a call to `authgear.configure`, the session state would become `SessionState.authenticated` if a previous session was found, or `SessionState.noSession` if such session was not found.

#### Using the Access Token in HTTP Requests

To include the access token to the HTTP requests to your application server, use `wrapHttpClient`.

The wrapped client will include the Authorization header in every HTTP request, and refresh access token automatically.

```dart
final originalClient = ...
final client = _authgear.wrapHttpClient(originalClient);
```

## Next steps

To protect your application server from unauthorized access. You will need to **integrate your backend with Authgear**.

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## Flutter SDK Reference

For detailed documentation on the Flutter SDK, visit [Flutter SDK Reference](https://authgear.github.io/authgear-sdk-flutter/)


# React Native SDK

How to integrate with a React Native app

This guide provides instructions on integrating Authgear with a React Native app. Supported platforms include:

* React Native 0.60.0 or higher

Follow this guide to add Authgear to your React Native app in 🕐 10 minutes.

{% hint style="info" %}
You can find the full code for the demo app for this tutorial in the Github repo [here](https://github.com/authgear/authgear-example-react-native).
{% endhint %}

{% hint style="info" %}
React Native have opt-in support for the [New Architecture](https://reactnative.dev/docs/new-architecture-intro) since 0.68. Given that the New Architecture is still considered as unstable, we do not support it at the moment.
{% endhint %}

## Video Guide for React Native

{% embed url="<https://www.youtube.com/watch?v=jCzqVrTzk_o>" %}

## Setup Application in Authgear

Signup for an Authgear Portal account in <https://portal.authgear.com/>. Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an application in the project.

### **Step 1: Create an application in Authgear Portal**

Go to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption><p>authgear navigate to applications</p></figcaption></figure>

You'll see the "**New Application**" page, or Click **⊕Add Application** in the top tool bar.

Input the name of your application and select **Native App** as the application type. Click "**Save**".

On the next screen, you will see a list of guides that can help you with setting up, click "**Next**" to continue.

![Create an application](/files/7wywVJAvi44Ch5FG30h3)

### **Step 2: Configure the application**

In your IDE, define a custom URI scheme that will be used to redirect users back to your app after they have authenticated with Authgear. For example, in our example app, we will define the following URI scheme:

```
com.authgear.example.rn://host/path
```

For further instruction on setting up custom URI scheme in React Native, see <https://reactnative.dev/docs/linking>

Now head back to Authgear Portal, and add the URI that you have defined (`com.authgear.example.rn://host/path` for this example) as an **Authorized Redirect URI**.

Click "**Save**" and note the **Client ID** and **Endpoint** for your Authgear client application. You can also obtain it again from the **Applications** list later.

![set redirect URI](/files/L8nIgcGmDyEqy4lfTfVS)

## Add User Authentication to React Native App using Authgear SDK

In this section, we'll walk through the steps to create a new React Native app and use the Authgear SDK to add user authentication to the app.

### Step 1: Create a React Native app

Run the following command to create a new React Native project:

```bash
npx @react-native-community/cli init myapp
cd myapp
```

For a more detailed guide on how to create a project and set up a development environment for React Native, follow the [official documentation of React Native](https://reactnative.dev/docs/getting-started).

### Step 2: Install the SDK

Run the following commands from the root directory of your React Native project to install the Authgear SDK:

```bash
npm install --exact @authgear/react-native
(cd ios && pod install)
```

### Step 3: Initialize Authgear

In this step, we'll implement the code to initialize an instance of the Authgear SDK which we will be using to interact with the Authgear client application we created earlier.

First, open the `App.tsx` file in your project then add the following import statements at the top:

```typescript
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import authgear, { Page, ReactNativeContainer, SessionState, SessionStateChangeReason } from "@authgear/react-native";
```

Add the following code at the top inside the `App()` function in `App.tsx` to configure a new Authgear instance and set up a `delegate` that will help our app to know the current state of a user's session (whether they're logged in or not):

```typescript
const [sessionState, setSessionState] = useState<SessionState | null>(() => {
  return authgear.sessionState;
});

const loggedIn = sessionState === "AUTHENTICATED";
const delegate = useMemo(() => {
  const d = {
    onSessionStateChange: (
      container: ReactNativeContainer,
      _reason: SessionStateChangeReason
    ) => {
      setSessionState(container.sessionState);
    },
    sendWechatAuthRequest: () => {},
  };
  return d;
}, [setSessionState]);

useEffect(() => {
  authgear.delegate = delegate;

  return () => {
      authgear.delegate = undefined;
  };
}, [delegate]);

const postConfigure = useCallback(async () => {
  const sessionState = authgear.sessionState;

  // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
  if (sessionState === "AUTHENTICATED") {
    await authgear.fetchUserInfo();
  }
}, []);

useEffect(() => {
  const configure = async () => {
    try {
      await authgear
      .configure({
        clientID: "<CLIENT_ID>",
        endpoint: "<AUTHGEAR_ENDPOINT>",
      });
      await postConfigure();
    } catch (error) {
      console.log("Error:" + error);
    }
  };

  configure();
}, [postConfigure]);
```

Replace `<CLIENT_ID>` and `<AUTHGEAR_ENDPOINT>` with the Client ID and Endpoint for your Authgear client application.

The above code includes a `postConfigure()` method that helps to get the true session state for a user that was previously logged in.

### Step 4: Add Login Button

Replace the content for the `return` statement in the `App()` function inside the `App.tsx` file with the following:

```tsx
return (
  <SafeAreaView>
    <StatusBar />
      <View style={{justifyContent: 'center', alignItems: 'center'}}>
      {!loggedIn ?
        <View>
          <Text style={{paddingTop: 50, paddingBottom: 16, fontSize: 40}}>Welcome</Text>
          <Button onPress={authenticate} title="Login" />
        </View> :
        <View>
          <Text style={{paddingTop: 50, paddingBottom: 16}}>Welcome User</Text>
        </View>
      }
      </View>
  </SafeAreaView>
);
```

Import the necessary components at the top `App.tsx`:

```typescript
import {
  Alert,
  Button,
  SafeAreaView,
  StatusBar,
  Text,
  View,
} from 'react-native';
```

### Step 5: Start Authentication Flow

In this step, you will implement an `authenticate()` method that calls the `authenticate()` method of the Authgear SDK. The Login button we added in the previous step calls this `authenticate()` method to start an authentication flow.

Add the following code to the `App()` function just after the `useEffect()` for the `configure()` method in step 3:

```typescript
const authenticate = useCallback(async () => {
  try {
    authgear
        .authenticate({
          redirectURI: "com.authgear.example.rn://host/path",
        });
  } catch (error) {
    console.log("Authentication Error:" + error);
  }
}, []);
```

#### Checkpoint

At this point the complete code in your `App.tsx` should look like this:

```tsx
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import authgear, {
  Page,
  ReactNativeContainer,
  SessionState,
  SessionStateChangeReason,
} from '@authgear/react-native';

import {Alert, Button, SafeAreaView, StatusBar, Text, View} from 'react-native';

function App(): React.JSX.Element {
  const [sessionState, setSessionState] = useState<SessionState | null>(() => {
    return authgear.sessionState;
  });

  const loggedIn = sessionState === 'AUTHENTICATED';
  const delegate = useMemo(() => {
    const d = {
      onSessionStateChange: (
        container: ReactNativeContainer,
        _reason: SessionStateChangeReason,
      ) => {
        setSessionState(container.sessionState);
      },
      sendWechatAuthRequest: () => {},
    };
    return d;
  }, [setSessionState]);

  useEffect(() => {
    authgear.delegate = delegate;

    return () => {
      authgear.delegate = undefined;
    };
  }, [delegate]);

  const postConfigure = useCallback(async () => {
    const sessionState = authgear.sessionState;

    // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
    if (sessionState === 'AUTHENTICATED') {
      await authgear.fetchUserInfo();
    }
  }, []);

  useEffect(() => {
    const configure = async () => {
      try {
        await authgear.configure({
          clientID: '<CLIENT_ID>',
          endpoint: '<AUTHGEAR_ENDPOINT>',
        });
        await postConfigure();
      } catch (error) {
        console.log('Error:' + error);
      }
    };

    configure();
  }, [postConfigure]);

  const authenticate = useCallback(async () => {
    try {
      authgear.authenticate({
        redirectURI: 'com.authgear.example.rn://host/path',
      });
    } catch (error) {
      console.log('Authentication Error:' + error);
    }
  }, []);

  return (
    <SafeAreaView>
      <StatusBar />
      <View style={{justifyContent: 'center', alignItems: 'center'}}>
        {!loggedIn ? (
          <View>
            <Text style={{paddingTop: 50, paddingBottom: 16, fontSize: 40}}>
              Welcome
            </Text>
            <Button onPress={authenticate} title="Login" />
          </View>
        ) : (
          <View>
            <Text style={{paddingTop: 50, paddingBottom: 16}}>
              Welcome User
            </Text>
          </View>
        )}
      </View>
    </SafeAreaView>
  );
}

export default App;
```

Now save your work and try running your app on Android or iOS using any of the following commands:

**Android**

```
npm run android
```

**iOS**

```
npm run ios
```

When your app opens, if you click on the Login button, you should be redirected to the Authentication UI. However, you can't complete authentication because we are yet to handle the redirect URI.

### Step 6: Setup Redirect URI

To finish the integration, set up the app to handle the redirect URI specified in your Authgear client application. This part requires platform-specific integration.

#### Android

Add the following `activity` entry to the `android/app/src/main/AndroidManifest.xml` of your React Native project. The intent system would dispatch the redirect URI to `OAuthRedirectActivity` and the sdk would handle the rest.

```xml
<!-- Your application configuration. Omitted here for brevity -->
<application>
  <!-- Other activities or entries -->

  <!-- Add the following activity -->
  <!-- android:exported="true" is required -->
  <!-- See https://developer.android.com/about/versions/12/behavior-changes-12#exported -->
  <activity android:name="com.authgear.reactnative.OAuthRedirectActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!-- Configure data to be the exact redirect URI your app uses. -->
                <!-- Here, we are using com.authgear.example://host/path as configured in the portal -->
                <!-- NOTE: The redirectURI supplied in AuthenticateOptions *has* to match as well -->
                <data android:scheme="com.authgear.example.rn"
                    android:host="host"
                    android:pathPrefix="/path"/>
            </intent-filter>
  </activity>
</application>
```

You also need to add a `queries` section to `AndroidManifest.xml`.

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Other elements such <application> -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>
```

#### iOS

#### Declare URL Handling in Info.plist

In `ios/<your_project>/Info.plist`, add the matching redirect URI.

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
      <!-- Other entries -->
      <key>CFBundleURLTypes</key>
      <array>
              <dict>
                      <key>CFBundleTypeRole</key>
                      <string>Editor</string>
                      <key>CFBundleURLSchemes</key>
                      <array>
                              <string>com.authgear.example.rn</string>
                      </array>
              </dict>
      </array>
</dict>
</plist>
```

<details>

<summary>Optional: Handle deep links for WeChat Login</summary>

Skip this part if you don't need support for "Login with WeChat".

Alternatively, use any popular deep-linking library then implement code to forward the deep link to our SDK in the JavaScript side for your React Native app.

To handle WeChat deep links, in `AppDelegate.m`, add the following code snippet:

```objectivec
// Other imports...
#import <authgear-react-native/AGAuthgearReactNative.h>

// Other methods...

// For handling deeplink
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:
                (NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
    return [AGAuthgearReactNative application:app openURL:url options:options];
}

// For handling deeplink
// deprecated, for supporting older devices (iOS < 9.0)
- (BOOL)application:(UIApplication *)application
              openURL:(NSURL *)url
    sourceApplication:(NSString *)sourceApplication
          annotation:(id)annotation {
    return [AGAuthgearReactNative application:application
                                      openURL:url
                            sourceApplication:sourceApplication
                                  annotation:annotation];
}

// for handling universal link
- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
      restorationHandler:
          (void (^)(NSArray<id<UIUserActivityRestoring>> *_Nullable))
              restorationHandler {
    return [AGAuthgearReactNative application:application
                        continueUserActivity:userActivity
                          restorationHandler:restorationHandler];
}
```

</details>

### Step 7: Logout

Now let's add a Logout feature to our example app so users can logout and end their session.

We'll add a Logout button and implement a `logout()` method that calls the corresponding `logout()` method of the Authgear SDK:

Add a `logout()` method to your `App()` function after the authenticate() method you added earlier:

```typescript
const logout = useCallback( () => {
    try {
      authgear
      .logout();
    } catch (error) {
      console.log("Error:" + error);
    }
}, []);
```

Next, add the Logout button and call the logout() method from onPress. Add the button to the return statement for `App()` just below `<Text style={{paddingTop: 50, paddingBottom: 16}}> Welcome User</Text>` :

```tsx
<Button onPress={logout} title="Logout" />
```

Save your code and run the app. Click the Logout button and the user should be logged out.

### Step 8: Show User Info

The Authgear SDK includes a `fetchUserInfo()` method that returns details such as user ID, email, phone number, etc about the current user.

In this step, we'll add a **Show User Info** button to our app. This button will call the fetchUserInfo() to demonstrate how the method works.

Add a `showUserInfo()` method to your `App()` function just below the `logout()` method:

```typescript
const showUserInfo = useCallback(async () => {
  try {
    const userInfo = await authgear.fetchUserInfo();
    Alert.alert('User Info', JSON.stringify(userInfo, null, 2));
  } catch (error) {
    console.log('Error:' + error);
  }
}, []);
```

The `showUserInfo()` method calls the `fetchUserInfo()` method of the Authgear SDK and displays the data returned in an `Alert`.

Next, add the **Show User Info** button to the return statement of `App()` just below the Logout button from the previous step:

```tsx
<View style={{height: 8}} />
<Button onPress={showUserInfo} title="Show User Info" />
```

### Step 9: Open User Settings Page

Authgear offers a pre-built User Settings page for users of your application to view and modify their profile and security details.

In this step, we'll use the `open()` method in the Authgear SDK to open this User Settings page when a logged-in user clicks on a button.

Add an `openSettings()` method to your `App()` function just below the showUserInfo() from the previous step:

```typescript
const openSettings = useCallback(() => {
  try {
    authgear.open(Page.Settings);
  } catch (error) {
    console.log('Error:' + error);
  }
}, []);
```

Next, add a **User Settings** button to the return statement of `App()` just below the Show User Info button:

```tsx
<View style={{height: 8}} />
<Button onPress={openSettings} title="User Settings" />
```

Now run your app and you should be able to access the User Settings page when you click on the **User Settings** button.

### Additional Actions

#### Get the Logged-In State

When you start launching the application. You may want to know if the user has logged in. (e.g. Show users the login page if they haven't logged in).

The `sessionState` reflects the user logged-in state in the SDK local state. That means even if the `sessionState` is `AUTHENTICATED`, the session may be invalid if it is revoked remotely. Hence, after initializing the Authgear SDK, call `fetchUserInfo` to update the `sessionState` as soon as it is proper to do so. We demonstrated this in our example app using the `postConfigure()` method.

```javascript
// After authgear.configure, it only reflect SDK local state.
// value can be NO_SESSION or AUTHENTICATED
let sessionState = authgear.sessionState;

if (sessionState === "AUTHENTICATED") {
    authgear
        .fetchUserInfo()
        .then((userInfo) => {
            // sessionState is now up to date
        })
        .catch((e) => {
            // sessionState is now up to date
            // it will change to NO_SESSION if the session is invalid
        });
}
```

The value of `sessionState` can be `UNKNOWN`, `NO_SESSION` or `AUTHENTICATED`. Initially, the `sessionState` is `UNKNOWN`. After a call to `authgear.configure()`, the session state would become `AUTHENTICATED` if a previous session was found, or `NO_SESSION` if such session was not found.

#### Using the Access Token in HTTP Requests

To include the access token in the HTTP requests to your application server, there are two ways to achieve this.

#### Option 1: Using fetch function provided by Authgear SDK

Authgear SDK provides the `fetch` function for you to call your application server. The `fetch` function will include the Authorization header in your application request, and handle refresh access token automatically. `authgear.fetch` implement [fetch](https://fetch.spec.whatwg.org).

```javascript
authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

#### Option 2: Add the access token to your HTTP

You can access the access token through `authgear.accessToken`. Call `refreshAccessTokenIfNeeded()` every time before using the access token. The `refreshAccessTokenIfNeeded()` function will check and make the network call to refresh the access token only if it has expired.

Include the access token in the Authorization header of your application request.

```javascript
authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```

## Next steps

To protect your application server from unauthorized access. You will need to [integrate Authgear to your backend](/get-started/backend-api).

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## JavaScript SDK Reference

For detailed documentation on the JavaScript React Native SDK, visit [@authgear/react-native Reference](https://authgear.github.io/authgear-sdk-js/docs/react-native/)


# Ionic SDK

Guide on how to use Authgear in an Ionic project

In this post, you'll learn how to use Authgear with your Ionic project using the Authgear Ionic SDK.

{% hint style="info" %}
You can find the full code for the demo app for this tutorial in [this Github repo](https://github.com/authgear/authgear-example-ionic)
{% endhint %}

The following are the minimum versions of Android and iOS your native app can target based on the requirement of Capacitor v7:

* iOS 14
* Android 6 (API level 23)

### Objectives (What we'll build)

At the end of this tutorial, we'll build an Ionic app that can do the following:

* Allow users to log in to their account on your Authgear project
* Allow new users to sign up
* Allow signed-in users to view their user info and logout.

The final UI for the app we'll build should look like this:

<figure><img src="/files/2g6oyanyxR6P8d5jdD5N" alt=""><figcaption><p>authgear ionic example app landing page</p></figcaption></figure>

### Prerequisites

To follow this guide seamlessly, make sure to have the following:

* Node.js installed on your local machine
* Android Studio (for building the Android client of your application)
* Xcode (for building the iOS client of your application)
* An Authgear account. You can sign up for one for free [here](https://authgear.com/).
* Any code editor (VS Code, Sublime, etc)

Follow this guide to add Authgear to your Ionic app in 🕐 10 minutes.

## Setup Application in Authgear Portal

In this part, you'll learn how to configure an Authgear client application that you will use in your Ionic project. You'll do this by performing the following steps in the Authgear Portal.

### Step 1: Set up an Authgear Application

First, log in to Authgear Portal at <https://portal.authgear.com/> and select an existing project or create a new one.

In your project, navigate to the **Applications** section then click on **Add Application** to create a new Authgear application. Enter a name for your application and select **Native App** as the Application Type. Next, click **Save** to continue to the configuration page for your new application. Skip the screen that shows you a list of tutorials for different frameworks.

<figure><img src="/files/G7gPkdQKWo5FjSCIWLto" alt=""><figcaption><p>Create new authgear client app</p></figcaption></figure>

### Step 2: Add Authorized Redirect URIs

In this step, you'll set up authorized redirect URIs for your application. An authorized redirect URI should be a URI pointing to a page on your Ionic app where you want to redirect users at the end of the authorization flow.

To add a URI, scroll to the **URIs** section of your application configuration page and enter the URI in the text field. You can click the **Add URI** button to add additional URIs.

For our example app, add the following URIs:

* `com.authgear.example.capacitor://host/path`
* `capacitor://localhost`
* `http://localhost:8100/oauth-redirect`
* `https://localhost`

<figure><img src="/files/uMrAcrpw6DIcplK2ht2s" alt=""><figcaption><p>authgear-app-redirect-uris</p></figcaption></figure>

Once you're done, click on the **Save** button.

## Add Authgear to an Ionic App

Now that you have your Authgear application configured, we can proceed with creating the Ionic application that will have all the features stated in our objective earlier.

For this tutorial, we'll be implementing an Ionic app using React.

### Step 1: Create Ionic Project

Before you can create an Ionic project, install the Ionic CLI on your computer by running the following command in Terminal or Command Prompt:

```sh
npm install -g @ionic/cli native-run cordova-res
```

Now create a new Ionic project by running the following command:

```sh
ionic start authgear-ionic-example --type=react --capacitor
```

After running the above command, follow the wizard to create a new **blank project**.

Next, open your new project in a code editor and update for `appId` in **capacitor.config.ts** to the following value:

```typescript
appId: 'com.authgear.example.capacitor',
```

This new value for `appId` is the same value we used in the authorized redirect URI earlier.

{% hint style="info" %}
**Note:** It is important that you update the value for `appId` before you create the Android and iOS projects for your Ionic application. Doing this will enable Capacitor to create your Android and iOS project with the value for appId as the package name and app ID.
{% endhint %}

Run the following command from the root directory of your new Ionic project to preview your blank project on a browser:

```sh
ionic serve
```

Finally, create the Android and iOS projects for your app by running the following commands from your Ionic project's root folder:

First, install the Android and iOS platforms:

```sh
npm i @capacitor/android @capacitor/ios
```

Then, create the projects:

```sh
npx cap add android
npx cap add ios
```

### Step 2: Install Authgear SDK

In this step, you'll install the Authgear SDK for Ionic (Capacitor) and the Javascript SDK for the web. The web SDK will help you test your application on a web browser.

To install the SDKs, run the following commands in your Terminal or Command Prompt:

**Authgear Ionic SDK**

```bash
npm i @authgear/capacitor
```

**Authgear Web SDK**

```sh
npm i @authgear/web
```

### Step 3: Configure Authgear SDK

In this step, you'll learn how to configure your Ionic project using the details from your Authgear application configuration.

To get started, open **src/pages/Home.tsx** in your code editor then import the Authgear SDK by adding the following code to the top of the file:

```typescript
import authgearWeb, {
  SessionState,
  WebContainer,
  SessionStateChangeReason,
} from "@authgear/web";
import authgearCapacitor, {
  CapacitorContainer,
  Page,
} from "@authgear/capacitor";
```

The above code imports all the components of the Authgear SDK we need for our example app.

Because Ionic apps can run on the web and native mobile platforms, we had to import both Authgear web and Authgear Capacitor SDKs.

Next, add the following constants to **Home.tsx** just below the last import statements:

```typescript
const CLIENT_ID = "<ClIENT_ID>";
const ENDPOINT = "<AUTHGEAR_ENDPOINT>";
const REDIRECT_URI_WEB_AUTHENTICATE = "http://localhost:8100/oauth-redirect";
const REDIRECT_URI_CAPACITOR = "com.authgear.example.capacitor://host/path";
const REDIRECT_URI_WEB_REAUTH = "http://localhost:8100/reauth-redirect";
```

Update the values for the constants (`CLIENT_ID`, `ENDPOINT`) to the correct values from your client application's configuration page in the Authgear Portal.

Now, just below the constants, add this small utility function that will help to check whether your Ionic app is running natively or on a web browser:

```typescript
function isPlatformWeb(): boolean {
  return Capacitor.getPlatform() === "web";
}
```

Import Capacitor by adding the following to the import section at the top of **Home.tsx**:

```typescript
import { Capacitor } from "@capacitor/core";
```

You will use the above function to determine which instance of the Authgear SDK to call based on the current platform a user is on.

Now implement a new `AuthenticationScreen` component in **Home.tsx** by pasting the following code:

```typescript
function AuthenticationScreen() {

  const [sessionState, setSessionState] = useState<SessionState | null>(() => {
    if (isPlatformWeb()) {
      return authgearWeb.sessionState;
    }
    return authgearCapacitor.sessionState;
  });

  const loggedIn = sessionState === "AUTHENTICATED";
  
  const delegate = useMemo(() => {
    const d = {
      onSessionStateChange: (
        container: WebContainer | CapacitorContainer,
        _reason: SessionStateChangeReason
      ) => {
        setSessionState(container.sessionState);
      },
    };
    return d;
  }, [setSessionState]);
  
    useEffect(() => {
    if (isPlatformWeb()) {
      authgearWeb.delegate = delegate;
    } else {
      authgearCapacitor.delegate = delegate;
    }
  
    return () => {
      if (isPlatformWeb()) {
        authgearWeb.delegate = undefined;
      } else {
        authgearCapacitor.delegate = undefined;
      }
    };
  }, [delegate]);

  return (
    <>
      <div className="container">
          
      </div>
    </>
  );
}
```

Import `useState`, `useMemo`, `useEffect` and `useCallback` at the top of **Home.tsx**:

```typescript
import React, { useState, useEffect, useCallback, useMemo } from "react";
```

The above code also implements a `sessionState` constant and a delegate to let your app know when a user's session state changes.

Next, add the following `configure()` method to the `AuthenticationScreen()` component just before the return statement:

```typescript
const configure = useCallback(async () => {
  try {
    if (isPlatformWeb()) {
      await authgearWeb.configure({
        clientID: CLIENT_ID,
        endpoint: ENDPOINT,
        sessionType: "refresh_token",
        isSSOEnabled: false,
      });
    } else {
      await authgearCapacitor.configure({
        clientID: CLIENT_ID,
        endpoint: ENDPOINT,
      });
    }
    await postConfigure();
  } catch (e) {
    console.error("Authgear Configuration error:", e);
  } 
}, [CLIENT_ID, ENDPOINT]);
```

The above code configures a new instance of the Authgear SDK using the **Client ID** and **Endpoint** for the Authgear client application we created in the first part of this guide.

Next, implement the `postConfigure()` method that was called in the configure() method:

```typescript
const postConfigure = useCallback(async () => {
  const sessionState = isPlatformWeb()
    ? authgearWeb.sessionState
    : authgearCapacitor.sessionState;

  // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
  if (sessionState === "AUTHENTICATED") {
    if (isPlatformWeb()) {
      await authgearWeb.fetchUserInfo();
    } else {
      await authgearCapacitor.fetchUserInfo();
    }
  }
}, []);
```

The `postConfigure()` method checks if the user is already authenticated and calls the fetchUserInfo() method of the Authgear SDK. Calling the fetchUserInfo() method will refresh the user's access token if it is expired.

Now, call the **configure()** method in `useEffect` by adding the following code after the `postConfigure()` method:

```typescript
useEffect(() => {
  configure();
}, []);
```

The above `useEffect` will initialize Authgear on page load.

### Step 4: Add Login Button

In this step, we'll add the Login button and the UI components we want to show authenticated users.

Add the following code to the `<div>` inside the return statement of the `AuthenticationScreen` component:

```tsx
<h1>
  Welcome
</h1>
{!loggedIn ? (
    <IonButton
    className="button"
    // disabled={!initialized || loading || loggedIn}
    onClick={
        (event) => {
            onClickAuthenticate(event, "login")
        }
    }
    >
    Login
</IonButton>
):
(
    <div>
        <p>Welcome user</p>
    </div>
)}
```

Import `IonButton` by adding it to the import for other Ionic components in our app:

```typescript
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton } from '@ionic/react';
```

Finally, add the `<AuthenticationScreen/>` component to the Home component:

```typescript
const Home: React.FC = () => {
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Blank</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Blank</IonTitle>
          </IonToolbar>
        </IonHeader>
        <AuthenticationScreen />
      </IonContent>
    </IonPage>
  );
};
```

### Step 5: Start Authentication

Here, we'll implement an `authenticate()` method inside the `AuthenticationScreen` component we created in the previous step.

To do this, first, add the following code just before the return statement of the `AuthenticationScreen()` component:

```typescript
const authenticate = useCallback(async (page: string) => {
  try {
    if (isPlatformWeb()) {
      authgearWeb.startAuthentication({
        redirectURI: REDIRECT_URI_WEB_AUTHENTICATE,
        page: page,
      });
    } else {
      const result = await authgearCapacitor.authenticate({
        redirectURI: REDIRECT_URI_CAPACITOR,
        page: page,
      });
    }
  } catch (e) {
    console.error("Authentication error:", e);
  } 
}, []);
```

Calling this `authenticate()` method will initialize an authentication flow. The `page` parameter can be used to specify whether to start the authentication flow on the `login` page or `signup` page.

Finally, implement a `onClickAuthenticate()` method that will call `authenticate` when the Login button is pressed:

```typescript
const onClickAuthenticate = useCallback(
  (e: MouseEvent<HTMLIonButtonElement>, page: string) => {
    e.preventDefault();
    e.stopPropagation();

    authenticate(page);
  },
  [authenticate]
);
```

Import `MouseEvent`:

```typescript
import type { MouseEvent } from "react";
```

#### Checkpoint

At this point, the complete code for **Home.tsx** should look like this:

```typescript
import {
  IonContent,
  IonHeader,
  IonPage,
  IonTitle,
  IonToolbar,
  IonButton,
} from "@ionic/react";

import React, { useState, useEffect, useCallback, useMemo } from "react";
import { Capacitor } from "@capacitor/core";
import type { MouseEvent } from "react";

import authgearWeb, {
  SessionState,
  WebContainer,
  SessionStateChangeReason,
} from "@authgear/web";
import authgearCapacitor, {
  CapacitorContainer,
  Page,
} from "@authgear/capacitor";
import "./Home.css";

const CLIENT_ID = <ClIENT_ID>";
const ENDPOINT = "<AUTHGEAR_ENDPOINT>";
const REDIRECT_URI_WEB_AUTHENTICATE = "http://localhost:8100/oauth-redirect";
const REDIRECT_URI_CAPACITOR = "com.authgear.example.capacitor://host/path";

function isPlatformWeb(): boolean {
  return Capacitor.getPlatform() === "web";
}

function AuthenticationScreen() {
  const [sessionState, setSessionState] = useState<SessionState | null>(() => {
    if (isPlatformWeb()) {
      return authgearWeb.sessionState;
    }
    return authgearCapacitor.sessionState;
  });

  const loggedIn = sessionState === "AUTHENTICATED";
  const delegate = useMemo(() => {
    const d = {
      onSessionStateChange: (
        container: WebContainer | CapacitorContainer,
        _reason: SessionStateChangeReason
      ) => {
        setSessionState(container.sessionState);
      },
    };
    return d;
  }, [setSessionState]);
  
  useEffect(() => {
    if (isPlatformWeb()) {
      authgearWeb.delegate = delegate;
    } else {
      authgearCapacitor.delegate = delegate;
    }
    
    return () => {
      if (isPlatformWeb()) {
        authgearWeb.delegate = undefined;
      } else {
        authgearCapacitor.delegate = undefined;
      }
    };
  }, [delegate]);

  const configure = useCallback(async () => {
    try {
      if (isPlatformWeb()) {
        await authgearWeb.configure({
          clientID: CLIENT_ID,
          endpoint: ENDPOINT,
          sessionType: "refresh_token",
          isSSOEnabled: false,
        });
      } else {
        await authgearCapacitor.configure({
          clientID: CLIENT_ID,
          endpoint: ENDPOINT,
        });
      }
      await postConfigure();
    } catch (e) {
      console.error("Authgear Configuration error:", e);
    } 
  }, [CLIENT_ID, ENDPOINT]);

  const postConfigure = useCallback(async () => {
    const sessionState = isPlatformWeb()
      ? authgearWeb.sessionState
      : authgearCapacitor.sessionState;

    // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
    if (sessionState === "AUTHENTICATED") {
      if (isPlatformWeb()) {
        await authgearWeb.fetchUserInfo();
      } else {
        await authgearCapacitor.fetchUserInfo();
      }
    }
  }, []);

  useEffect(() => {
    configure();
  }, []);

  const authenticate = useCallback(async (page: string) => {
    try {
      if (isPlatformWeb()) {
        authgearWeb.startAuthentication({
          redirectURI: REDIRECT_URI_WEB_AUTHENTICATE,
          page: page,
        });
      } else {
        const result = await authgearCapacitor.authenticate({
          redirectURI: REDIRECT_URI_CAPACITOR,
          page: page,
        });
      }
    } catch (e) {
      console.error("Authentication error:", e);
    } 
  }, []);


  const onClickAuthenticate = useCallback(
    (e: MouseEvent<HTMLIonButtonElement>, page: string) => {
      e.preventDefault();
      e.stopPropagation();

      authenticate(page);
    },
    [authenticate]
  );

  return (
    <>
      <div className="container">
        <h1>Welcome</h1>
        {!loggedIn ? (
          <IonButton
            className="button"
            onClick={(event) => {
              onClickAuthenticate(event, "login");
            }}
          >
            Login
          </IonButton>
        ) : (
          <div>
            <p>Welcome user</p>
          </div>
        )}
      </div>
    </>
  );
}
const Home: React.FC = () => {
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Blank</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Blank</IonTitle>
          </IonToolbar>
        </IonHeader>
        <AuthenticationScreen />
      </IonContent>
    </IonPage>
  );
};

export default Home;

```

Save your work and run the command to serve your project on the web or [build it for iOS or Android](#step-10-deploy-app-to-mobile).

You should be able to see the Authentication UI after you click on the Login button. However, you can't complete the authentication flow because we're yet to handle the redirect.

### Step 6: Handle Redirect in App

At the end of an authentication flow, your users will be redirected to the URL you specified in `redirectURI`. In this step, we'll set up the routes and code to process redirects to the URIs.

To handle redirect on web, create a new file **OAuthRedirect.tsx** in **src/pages/** and add the following code to it:

```typescript
import { useCallback, useEffect } from "react";
import authgearWeb from "@authgear/web";
import { useIonRouter } from "@ionic/react";

export default function OAuthRedirect() {
  const router = useIonRouter();

  const finishAuthentication = useCallback(async () => {
    const CLIENT_ID = "<ClIENT_ID>";
    const ENDPOINT = "<AUTHGEAR_ENDPOINT>";

    try {
      await authgearWeb.configure({
        clientID: CLIENT_ID,
        endpoint: ENDPOINT,
        sessionType: "refresh_token",
      });
      await authgearWeb.finishAuthentication();
      router.push("/", "root", "replace");
    } catch (e) {
      console.error(e);
    }
  }, [router]);

  useEffect(() => {
    finishAuthentication();
  }, [finishAuthentication]);

  return (
    <div>
      Finishing authentication. Open the inspector to see if there is any error.
    </div>
  );
}
```

Change the values for `CLIENT_ID` and `ENDPOINT` in the above code to the correct value from your Authgear application configuration page.

Now open **src/App.tsx** and create a new route for `OAuthRedirect` using the following code:

```typescriptreact
<Route exact path="/oauth-redirect">
    <OAuthRedirect />
</Route>
```

Remember to import `OAuthRedirect` in **App.tsx:**

```typescript
import OAuthRedirect from './pages/OAuthRedirect';
```

To handle redirect in the Android project, add the following code to **android/app/src/main/AndroidManifest.xml**:

```xml
<!-- Authgear SDK -->
<activity
    android:name="com.authgear.capacitor.OAuthRedirectActivity"
    android:launchMode="singleTask"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Configure data to be the exact redirect URI your app uses. -->
        <!-- NOTE: The redirectURI supplied in AuthenticateOptions has to match as well -->
        <data
            android:host="host"
            android:pathPrefix="/path"
            android:scheme="com.authgear.example.capacitor" />
    </intent-filter>
</activity>
```

At this point, if you build your project and run it, you should be able to login successfully.

### Step 7: Implement Logout

To implement Logout, we'll add a Logout button, an onClick handler, and a method that will call the logout() method of the Authgear SDK.

Add a Logout button to `AuthenticationScreen` component just below `<p>Welcome user</p>`:

```tsx
<IonButton className="button" onClick={onClickLogout}>
    Logout
</IonButton>
```

Next, add a `logout()` method to `AuthenticationScreen` component:

```typescript
const logout = useCallback(async () => {
  try {
    if (isPlatformWeb()) {
      await authgearWeb.logout({
        redirectURI: window.location.origin + "/",
      });
    } else {
      await authgearCapacitor.logout();
    }
  } catch (e) {
    console.error("Logout error:", e);
  } 
}, []);
```

Finally, implement the onClick method in `AuthenticationScreen` :

```typescript
const onClickLogout = useCallback(
  (e: MouseEvent<HTMLIonButtonElement>) => {
    e.preventDefault();
    e.stopPropagation();

    logout();
  },
  [logout]
);
```

### Step 8: Get UserInfo

The Authgear SDK offers a `fetchUserInfo()` method that can return details such as User ID, email, phone number, and so on about the current user. In this step, we'll demonstrate how to call `fetchUserInfo` in our app.

First, add a **Fetch User Info** button to your `AuthenticationScreen` component just below the Logout button:

```tsx
<IonButton className="button" onClick={onClickFetchUserInfo}>
  Fetch User Info
</IonButton>
```

Next, add a `fetchUserInfo()` method to the `AuthenticationScreen` component:

```typescript
const fetchUserInfo = useCallback(async () => {
  try {
    const authgear = isPlatformWeb() ? authgearWeb : authgearCapacitor;
    const userInfo = await authgear.fetchUserInfo();
    alert(JSON.stringify(userInfo, null, 2)); // Keep for demonstration, but consider a better way to display info
  } catch (e) {
    console.error("FetchUserInfo error:", e);
  } 
}, []);
```

Finally, add the method that will handle click on the **Fetch User Info** button:

```typescript
const onClickFetchUserInfo = useCallback(
  (e: MouseEvent<HTMLIonButtonElement>) => {
    e.preventDefault();
    e.stopPropagation();

    fetchUserInfo();
  },
  [fetchUserInfo]
);
```

### Step 9: Open User Settings Screen

Authgear provides a default User Settings page where your users can view details about their profile and change details or security settings like their password.

To allow users to open the settings page from your app, first add a **User Settings** button to you r `AuthenticationScreen` component just below the **Fetch User Info** button:

```tsx
<IonButton
    className="button"
    onClick={onClickUserSettings}
    >
    User Settings
</IonButton>
```

Now add an `openUserSettings()` method in `AuthenticationScreen` component:

```typescript
const openUserSettings = useCallback(async () => {
  try {
    if (isPlatformWeb()) {
      authgearWeb.open(Page.Settings);
    } else {
      authgearCapacitor.open(Page.Settings);
    }
  } catch (e) {
    console.error("Error:", e)
  } 
}, []);
```

Finally, implement the onClick method for the **User Settings** button:

```typescript
const onClickUserSettings = useCallback(
  (e: MouseEvent<HTMLIonButtonElement>) => {
    e.preventDefault();
    e.stopPropagation();

    openUserSettings();
  },
  [logout]
);
```

### Step 10: Deploy app to mobile

To deploy your app to a mobile device (for example Android) run the following commands:

First build your project by running:

```sh
npm run build
```

Then sync the changes to the mobile project using this command:

```sh
npx cap sync
```

You can run the project by opening the `android` project folder in Android Studio or `ios` folder in Xcode.

You can quickly open the project in Android Studio using the following command:

```sh
npx cap open android
```

Or run the following command to open your project in Xcode for iOS:

```sh
npx cap open ios
```

Once your project builds successfully, you can try the Login, Signup, Fetch User Info, and Logout buttons.

## Conclusion

Authgear Capacitor SDK makes it easier to use Authgear in your Ionic application. It provides many helpful methods and interfaces for interacting with the Authgear service from your Ionic application. To learn more about the SDK check [the SDK Reference](https://authgear.github.io/authgear-sdk-js/docs/capacitor/). Also, check out the complete repo for the Authgear Ionic SDK example app [here](https://github.com/authgear/authgear-sdk-js/tree/master/example/capacitor).


# Xamarin SDK

How to integrate with a Xamarin app

This guide provides instructions on integrating Authgear with a Xamarin app. Supported packages include:

* Xamarin.Essentials 1.7.2 or higher
* Xamarin.Forms 5.0.0.2401 or higher

## Setup Application in Authgear

Signup for an Authgear Portal account in <https://portal.authgear.com/>. Or you can use your self-deployed Authgear.

From the Project listing, create a new Project or select an existing Project. After that, we will need to create an application in the project.

**Step 1: Create an application in the Portal**

1. Go to **Applications** on the left menu bar.
2. Click **⊕Add Application** in the top tool bar.
3. Input the name of your application and select **Native App** as the application type. Click "Save".
4. You will see a list of guides that can help you for setting up, then click "Next".

<figure><img src="/files/5HnjKzfiu8kNJEfrIjiE" alt=""><figcaption></figcaption></figure>

**Step 2: Configure the application**

1. In your IDE (e.g. Visual Studio), define a custom URI scheme that the users will be redirected back to your app after they have authenticated with Authgear, e.g. `com.myapp.example://host/path`.\[^1]
2. Head back to Authgear Portal, fill in the Redirect URI that you have defined in the previous steps.
3. Click "Save" in the top tool bar and keep the **Client ID**. You can also obtain it again from the Applications list later.

<figure><img src="/files/32xb3B6jUKWpYEPUkJcD" alt=""><figcaption></figcaption></figure>

## Create a Xamarin app

* Open Visual Studio
* Create a new project
* Choose the Xamarin.Forms template

## Install the SDK

* Search for "Authgear.Xamarin" on nuget.org and add it to your base project.\
  <https://www.nuget.org/packages/Authgear.Xamarin/>
* Authgear.Xamarin targets MonoAndroid 12.0 on Android, and Xamarin.iOS10 on iOS. Update the target framework of the Android and iOS projects to match Authgear.Xamarin's target frameworks.
* Update Android and iOS project's Xamarin.Essentials to 1.7.2.

## Platform Integration

To finish the integration, setup the app to handle the redirectURI specified in the application. This part requires platform specific integration.

### Android

#### Define your own callback activity

```csharp
using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using Authgear.Xamarin;
using Xamarin.Forms;

namespace MyApp.Droid
{
    [Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
    [IntentFilter(new[] { Android.Content.Intent.ActionView },
        Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
        DataScheme = "com.myapp.example")]
    public class WebAuthenticationCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity
    {
    }
}
```

#### Targeting API level 30 or above (Android 11 or above)

If your Android app is targeting API level 30 or above (Android 11 or above), you need to add a `queries` section to `AndroidManifest.xml`.

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Other elements such <application> -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>
```

#### Initialize a global AuthgearSdk instance

In your MainActivity.cs

```csharp
using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;

using Xamarin.Forms;
using Authgear.Xamarin;

namespace MyApp.Droid
{
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            // ...

            var authgear = new AuthgearSdk(this, new AuthgearOptions
            {
                ClientId = CLIENT_ID,
                AuthgearEndpoint = ENDPOINT
            });
            DependencyService.RegisterSingleton<AuthgearSdk>(authgear);
            LoadApplication(new App());

            // ...
        }

        // other methods are omitted for brevity.
    }
}
```

### iOS

#### Add the following key-value pair in your iOS project Info.plist

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <!-- other entries ... -->
        <key>CFBundleURLTypes</key>
        <array>
          <dict>
            <key>CFBundleURLName</key>
            <string>com.myapp.example://host/path</string>

            <key>CFBundleURLSchemes</key>
            <array>
              <string>com.myapp.example</string>
            </array>

            <key>CFBundleTypeRole</key>
            <string>Editor</string>
          </dict>
        </array>
</dict>
</plist>
```

#### Initialize a global AuthgearSdk instance

In your AppDelegate.cs

```csharp
using Xamarin.Essentials;
using Xamarin.Forms;
using Authgear.Xamarin;

namespace MyApp.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            var authgear = new AuthgearSdk(app, new AuthgearOptions
            {
                ClientId = CLIENT_ID,
                AuthgearEndpoint = ENDPOINT
            });
            DependencyService.RegisterSingleton<AuthgearSdk>(authgear);
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

        public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
        {
            return Xamarin.Essentials.Platform.OpenUrl(app, url, options);
        }

        public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
        {
            if (Xamarin.Essentials.Platform.ContinueUserActivity(application, userActivity, completionHandler))
                return true;
            return base.ContinueUserActivity(application, userActivity, completionHandler);
        }
    }
}
```

## Try authenticate

### Edit your MainPage.xaml

```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="testauthgear.MainPage">

    <StackLayout>
        <Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
            <Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
        </Frame>
        <Button Text="Configure" Clicked="Configure_Clicked" />
        <Button Text="Authenticate" Clicked="Authenticate_Clicked"/>
    </StackLayout>

</ContentPage>
```

### Edit your MainPage.xaml.cs

```csharp
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Authgear.Xamarin;

namespace MyApp
{
    public partial class MainPage : ContentPage
    {
        private AuthgearSdk authgear;


        public MainPage()
        {
            InitializeComponent();
            authgear = DependencyService.Get<AuthgearSdk>();
        }

        async void Configure_Clicked(object sender, EventArgs e)
        {
            // You must configure the instance before use.
            // Typically you should do this once on app launch.
            await authgear.ConfigureAsync();
        }

        async void Authenticate_Clicked(object sender, EventArgs e)
        {
            var userInfo = await authgear.AuthenticateAsync(new AuthenticateOptions
            {
                RedirectUri = REDIRECT_URI
            });
        }
    }
}
```

## Get the Logged In State

When you start launching the application. You may want to know if the user has logged in. (e.g. Show users the login page if they haven't logged in). The `SessionState` reflects the user logged in state in the SDK locally on the device. That means even the `SessionState` is `Authenticated`, the session may be invalid if it is revoked remotely. After initializing the Authgear SDK, call `FetchUserInfoAsync` to update the `SessionState` as soon as it is proper to do so.

```csharp
// value can be NoSession or Authenticated
// After Authgear.ConfigureAsync, it only reflects local state.
var sessionState = authgear.SessionState;

if (sessionState == SessionState.Authenticated)
{
    try
    {
        var userInfo = await authgear.FetchUserInfoAsync();
        // sessionState is now up to date
    }
    catch (Exception ex)
    {
        // sessionState is now up to date
        // it will change to NoSession if the session is invalid
    }
}
```

The value of `SessionState` can be `Unknown`, `NoSession` or `Authenticated`. Initially, the `SessionState` is `Unknown`. After a call to `authgear.configure`, the session state would become `Authenticated` if a previous session was found, or `NoSession` if such session was not found.

## Fetching User Info

In some cases, you may need to obtain current user info through the SDK. (e.g. Display email address in the UI). Use the `FetchUserInfoAsync` function to obtain the user info, see [example](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo).

## Logout

To log out the user from the current app session, you need to invoke the`logout`function.

```csharp
await authgear.LogoutAsync();
```

## Calling An API

To include the access token to the HTTP requests to your application server, you set the bearer token manually by using `authgear.AccessToken`.

### Using HttpClient

You can get the access token through `authgear.AccessToken`. Call `RefreshAccessTokenIfNeededAsync` every time before using the access token, the function will check and make the network call only if the access token has expired. Then, include the access token into the Authorization header of the http request.

```csharp
await authgear.RefreshAccessTokenIfNeededAsync();
// Access token is ready to use
// AccessToken can be string or undefined
// It will be empty if user is not logged in or session is invalid
var accessToken = authgear.AccessToken;
var client = GetHttpClient();  // Get the re-used http client of your app, as per recommendation.
var httpRequestMessage = new HttpRequestMessage(myHttpMethod, myUrl);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
```

## Next steps

To protect your application server from unauthorized access. You will need to **integrate your backend with Authgear**.

{% content-ref url="/pages/xp8Yw44yzS7vmIMf3OOh" %}
[Backend/API Integration](/get-started/backend-api)
{% endcontent-ref %}

## Xamarin SDK Reference

For detailed documentation on the Xamarin SDK, visit [Xamarin SDK Reference](https://authgear.github.io/authgear-sdk-xamarin/)

### Footnote

\[^1]: For further instruction on setting up custom URI scheme in Xamarin, see <https://www.xamarinhelp.com/uri-scheme/> \[^2]: For more explanation on JWT, see <https://en.wikipedia.org/wiki/JSON_Web_Token>


# Using Authgear without SDK (Client side)

Integrate Authgear on the client side in mobile apps without SDK

Authgear is built based on OIDC 2.0 standard, you can integrate it into any application framework without SDK.

### Supported grant types

**Authorization Code Flow with Proof Key for Code Exchange (PKCE)**

For public clients like mobile apps and SPAs, PKCE flow should be used. It uses `code_challenge_method` & `code_challenge` parameters in the authentication requests and the code\_verifier parameter in the code exchange step. It ensures that the application that starts the code flow is the same one that finishes it.

### OpenID Connect Configurations

This endpoint serves as a JSON document containing the OpenID Connect configuration of your Authgear project. That includes the authorization endpoint, the token endpoint, and the JWKs endpoint. The URL looks like:

```
https://AUTHGEAR_ENDPOINT/.well-known/openid-configuration
```

### Initiate Signup and Login

To singup or login, redirect the user to the `/authorize` endpoint. The URL should look like this:

```url
https://AUTHGEAR_ENDPOINT/oauth2/authorize
?response_type=code
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URL
&scope=openid+offline_access+https://authgear.com/scopes/full-userinfo
&code_challenge_method=S256
&code_challenge=CODE_CHALLENGE
&state=abc
```

**Code Example**

{% tabs %}
{% tab title="curl" %}

```bash
curl -X GET 'https://AUTHGEAR_ENDPOINT/oauth2/authorize' \
--data-urlencode 'response_type=code' \
--data-urlencode 'client_id=CLIENT_ID' \
--data-urlencode 'redirect_uri=REDIRECT_URL' \
--data-urlencode 'scope=openid offline_access https://authgear.com/scopes/full-userinfo' \
--data-urlencode 'code_challenge_method=S256' \
--data-urlencode 'code_challenge=CODE_CHALLENGE' \
--data-urlencode 'state=abc'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const randomData = secureRandomBytes(32); // (1)
const code_verifier = base64url(randomData); // (2) 
const hash = await sha256(utf8(code_verifier));
const code_challenge = base64url(hash);

// (1) A 32-byte long secure random data using secure random number generator
// (2) this string must be between 43 and 126 bytes long

const params = new URLSearchParams();
params.set("response_type", "code");
params.set("client_id", "CLIENT_ID");
params.set("redirect_uri", "REDIRECT_URL");
params.set("scope", ["openid", "offline_access", "https://authgear.com/scopes/full-userinfo"].join(" "));
params.set("code_challenge_method", "S256");
params.set("code_challenge", code_verifier);
params.set("state", "abc");

const endpoint = new URL("https://AUTHGEAR_ENDPOINT/oauth2/authorize");
const authorizationURL = endpoint.toString() + "?" + params.toString();
```

{% endtab %}
{% endtabs %}

* “S256” is supported in code\_challenge\_method.
* The `code_verifier` is a string of length between 43 and 128 generated on the client side. It should be unique for each authorization request. The `code_challenge` is a SHA-256 hash of the verifier.
* The state parameter is optional. Authgear will include the value of the state parameter when redirecting the user back to the client application. Learn more at [Use the OAuth 2.0 State Parameter](/faq-for-authentication/how-to-use-the-oauth-2.0-state-parameter)
* After login, the user will be redirected to `[redirect_uri]?code=[AUTH_CODE]`, the `AUTH_CODE` is needed in the next step
* See the supported scopes at [Supported Scopes](/reference/apis/oauth-2.0-and-openid-connect-oidc/supported-scopes)

### Handling Callback

After receiving the `AUTH_CODE` in your application, make a POST request to the `/token` endpoint.

{% tabs %}
{% tab title="curl" %}

```sh
curl -X POST 'https://AUTHGEAR_ENDPOINT/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'client_id=CLIENT_ID' \
-d 'code=AUTH_CODE' \
-d 'redirect_uri=REDIRECT_URL' \
-d 'code_verifier=CODE_VERIFIER'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const tokenEndpoint = new URL("https://AUTHGEAR_ENDPOINT/oauth2/token");
const form = new URLSearchParams();
form.set("grant_type", "authorization_code");
form.set("client_id", "CLIENT_ID");
form.set("code", AUTH_CODE); //Authorization code returned from authgear server
form.set("redirect_uri", "REDIRECT_URL");
form.set("code_verifier", code_verifier);
const response = await fetch(tokenEndpoint.toString(), {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body: form,
});
const responseJSON = await response.json();
```

{% endtab %}
{% endtabs %}

* Include the `code_verifier` used for generating the `code_challenge` to prove both requests come from the same client.
* The response may contain the ID token, the access token and the refresh token depending on the `scope` supplied by the previous step.

### Logout

When the user logs out, revoke the refresh token by making a POST request to the `revocation_endpoint` with the refresh token in the body in `application/x-www-form-urlencoded` as content-type. The endpoint looks like `https://AUTHGEAR_ENDPOINT/oauth2/revoke`, it can be found in OpenID Connect configuration document.

Then clear the access token and refresh token stored locally in your app.

### Verifying JWT Access Token

Include the access token in the Authorization headers of the frontend requests to verify your user’s identity. Follow this guide to learn about validating the JWT in your application server [Validate JWT in your backend](/get-started/backend-api/jwt)


# Regular Web App

Traditional web app that runs on the server

If your application is a traditional web app running on a server, for example, **Java EE, Express, PHP, GO, Laravel, or** [**ASP.NET**](http://asp.net/) **Core MVC,** you can leverage the OpenID Connect (OIDC) protocol to authenticate users via Authgear. A wide range of plug-and-play libraries can be found that simplify the integration process. These libraries handle crucial tasks such as authentication requests, session management, and redirecting users back to your application seamlessly.

{% content-ref url="/pages/B8pK11upMfo3w4xZG7F8" %}
[Express](/get-started/regular-web-app/express)
{% endcontent-ref %}

{% content-ref url="/pages/8nQYH8fElKTHw2uaS1RL" %}
[Next.js](/get-started/regular-web-app/nextjs)
{% endcontent-ref %}

{% content-ref url="/pages/xP3aVpZFt0cFH9Q7ren6" %}
[Python Flask App](/get-started/regular-web-app/python-flask-app)
{% endcontent-ref %}

{% content-ref url="/pages/X6ybf9cw2VmVHwEFrPQU" %}
[Java Spring Boot](/get-started/regular-web-app/java-spring-boot)
{% endcontent-ref %}

{% content-ref url="/pages/In5P7U3NT4JygKlUsZUR" %}
[ASP.NET Core MVC](/get-started/regular-web-app/asp.net-core-mvc)
{% endcontent-ref %}

{% content-ref url="/pages/glHG4YqXTwGYNDxNZcI3" %}
[Laravel](/get-started/regular-web-app/laravel)
{% endcontent-ref %}

{% content-ref url="/pages/DZiHpOE5zjDh639ONAKm" %}
[PHP](/get-started/regular-web-app/php)
{% endcontent-ref %}


# Next.js

Authentication for Next.js app with Authgear

Integrate Authgear authentication into a Next.js App Router application using the `@authgear/nextjs` SDK. You will set up login, logout, display user info, and protect a server-side API route.

A complete example application is available at [authgear/authgear-example-nextjs](https://github.com/authgear/authgear-example-nextjs).

**What you will build:**

* A home page that shows a Login button when unauthenticated, and the user's identity + Logout button when authenticated
* A protected API route (`/api/me`) that returns the current user's info

***

### Setting Up Your Application in Authgear

#### Step 1: Create an Application in the Portal

1. Sign in to the [Authgear Portal](https://portal.authgear.com/)
2. Select or create a Project
3. Navigate to **Applications** in the left menu
4. Click **⊕ Add Application**
5. Enter an application name and select **Single Page Application** as the application type
6. Click **Save**

#### Step 2: Configure the Application

1. Under **URIs**, find the **Authorized Redirect URIs** field
2. Add `http://localhost:3000/api/auth/callback`
3. Note down your **Client ID** and **Endpoint** (e.g. `https://your-project.authgear.cloud`) — you will need these shortly
4. Click **Save**

***

### Building Your Next.js Application

#### Step 1: Create a Next.js Project

```bash
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
```

#### Step 2: Install the Authgear SDK

```bash
npm install @authgear/nextjs
```

#### Step 3: Configure Environment Variables

Create `.env.local` with the following content:

```bash
AUTHGEAR_ENDPOINT=https://your-project.authgear.cloud
AUTHGEAR_CLIENT_ID=your-client-id
AUTHGEAR_REDIRECT_URI=http://localhost:3000/api/auth/callback
SESSION_SECRET=a-random-string-of-at-least-32-characters
```

{% hint style="info" %}
`SESSION_SECRET` encrypts the session cookie stored in the browser. It must be at least 32 characters. Use a random string generator to create one.
{% endhint %}

#### Step 4: Create the Shared Authgear Config

Create `src/lib/authgear.ts`. This file holds your Authgear configuration and is imported by both server-side and client-side code:

```typescript
// src/lib/authgear.ts
import type { AuthgearConfig } from "@authgear/nextjs";

export const authgearConfig: AuthgearConfig = {
  endpoint: process.env.AUTHGEAR_ENDPOINT!,
  clientID: process.env.AUTHGEAR_CLIENT_ID!,
  redirectURI: process.env.AUTHGEAR_REDIRECT_URI!,
  sessionSecret: process.env.SESSION_SECRET!,
};
```

#### Step 5: Add the OAuth Route Handler

Create `src/app/api/auth/[...authgear]/route.ts`. This catch-all route handles all Authgear auth endpoints automatically:

```typescript
// src/app/api/auth/[...authgear]/route.ts
import { createAuthgearHandlers } from "@authgear/nextjs";
import { authgearConfig } from "@/lib/authgear";

export const { GET, POST } = createAuthgearHandlers(authgearConfig);
```

`createAuthgearHandlers` registers the following routes for you:

| Method | Path                 | Purpose                                              |
| ------ | -------------------- | ---------------------------------------------------- |
| `GET`  | `/api/auth/login`    | Start the OAuth login flow                           |
| `GET`  | `/api/auth/callback` | Handle the OAuth callback and set the session cookie |
| `GET`  | `/api/auth/logout`   | Clear the session and revoke tokens                  |
| `POST` | `/api/auth/refresh`  | Refresh an expired access token                      |
| `GET`  | `/api/auth/userinfo` | Return the current user's info                       |
| `GET`  | `/api/auth/open`     | Open a pre-authenticated Authgear-hosted page        |

#### Step 6: Add AuthgearProvider to Your Layout

`AuthgearProvider` makes the user's session state available to all Client Components via React context. Because it uses browser APIs, it must be a Client Component.

Create `src/app/providers.tsx`:

```typescript
// src/app/providers.tsx
"use client";

import { AuthgearProvider } from "@authgear/nextjs/client";

export default function Providers({ children }: { children: React.ReactNode }) {
  return <AuthgearProvider>{children}</AuthgearProvider>;
}
```

Then wrap your root layout in `src/app/layout.tsx`:

```typescript
// src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Providers from "./providers";

const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Next.js + Authgear",
  description: "Example app demonstrating Authgear authentication with Next.js",
};

export default function RootLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

On mount, `AuthgearProvider` fetches `/api/auth/userinfo` to check for an existing session. The result is available via the `useAuthgear()` hook.

#### Step 7: Implement the Home Page

Replace `src/app/page.tsx`. The page is a Client Component so it can use `useAuthgear()` to read session state and react to changes:

```typescript
// src/app/page.tsx
"use client";

import { useState } from "react";
import { useAuthgear, SignInButton, SignOutButton } from "@authgear/nextjs/client";

export default function Home() {
  const { isAuthenticated, user } = useAuthgear();
  const [apiResult, setApiResult] = useState<string | null>(null);

  async function testProtectedApi() {
    const res = await fetch("/api/me");
    const data = await res.json();
    setApiResult(JSON.stringify(data, null, 2));
  }

  return (
    <main className="flex min-h-screen flex-col items-center justify-center gap-6">
      <h1 className="text-3xl font-bold">Next.js + Authgear</h1>

      {isAuthenticated ? (
        <>
          <p className="text-gray-600">
            Logged in as: <span className="font-mono">{user?.sub}</span>
          </p>
          {(user?.email ?? user?.phoneNumber) && (
            <p className="text-gray-600">
              {user?.email ?? user?.phoneNumber}
            </p>
          )}
          <button
            onClick={testProtectedApi}
            className="rounded-md bg-green-600 px-6 py-2 text-white hover:bg-green-700"
          >
            Test Protected API
          </button>
          {apiResult && (
            <pre className="rounded-md bg-gray-100 dark:bg-gray-800 p-4 text-sm">{apiResult}</pre>
          )}
          <SignOutButton className="rounded-md bg-red-600 px-6 py-2 text-white hover:bg-red-700">
            Logout
          </SignOutButton>
        </>
      ) : (
        <SignInButton className="rounded-md bg-blue-600 px-6 py-2 text-white hover:bg-blue-700">
          Login
        </SignInButton>
      )}
    </main>
  );
}
```

**Key hooks and components:**

* `useAuthgear()` — returns `{ isAuthenticated, user, state, isLoaded, signIn, signOut }`. `user` is a `UserInfo` object with `sub`, `email`, `phoneNumber`, and other profile fields.
* `<SignInButton>` — navigates to `/api/auth/login` on click, starting the OAuth flow.
* `<SignOutButton>` — navigates to `/api/auth/logout` on click, clearing the session.

#### Step 8: Create a Protected API Route

Server-side route handlers can verify authentication using `currentUser()` from `@authgear/nextjs/server`. It reads the encrypted session cookie and returns the current user, or `null` if not authenticated.

Create `src/app/api/me/route.ts`:

```typescript
// src/app/api/me/route.ts
import { currentUser } from "@authgear/nextjs/server";
import { authgearConfig } from "@/lib/authgear";
import { NextResponse } from "next/server";

export async function GET() {
  const user = await currentUser(authgearConfig);

  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json({ user });
}
```

`currentUser()` automatically refreshes the access token if expired before fetching user info from the Authgear userinfo endpoint.

***

### Running the Application

```bash
npm run dev
```

Open <http://localhost:3000> in your browser.

***

### Testing the Integration

#### Login flow

1. Visit `http://localhost:3000` — you should see a **Login** button
2. Click **Login** — you are redirected to the Authgear hosted login page
3. Sign in with your credentials
4. You are redirected back to the home page, now showing your user ID and email/phone number

#### Logout flow

1. Click **Logout** — the session is cleared and you are returned to the login screen

#### Protected API

1. After logging in, click **Test Protected API**
2. The page calls `GET /api/me`, which verifies your session and returns your user info:

```json
{
  "user": {
    "sub": "...",
    "email": "...",
    "emailVerified": true
  }
}
```

3. To verify the route is protected, open an incognito window and run:

```bash
curl http://localhost:3000/api/me
```

Expected response with status `401`:

```json
{ "error": "Unauthorized" }
```

***

### Project Structure

```
src/
├── app/
│   ├── api/
│   │   ├── auth/
│   │   │   └── [...authgear]/
│   │   │       └── route.ts      # OAuth route handler
│   │   └── me/
│   │       └── route.ts          # Protected API route
│   ├── layout.tsx                # Root layout with AuthgearProvider
│   ├── page.tsx                  # Home page with login/logout UI
│   └── providers.tsx             # Client component wrapping AuthgearProvider
└── lib/
    └── authgear.ts               # Shared Authgear config
```

***

### Additional Topics

#### Reading the Session in Server Components

Use `auth()` to read the raw session (state + tokens) without making a network request:

```typescript
import { auth } from "@authgear/nextjs/server";
import { authgearConfig } from "@/lib/authgear";

export default async function MyServerComponent() {
  const session = await auth(authgearConfig);

  if (session.state !== "AUTHENTICATED") {
    return <p>Not logged in</p>;
  }

  return <p>Access token expires at: {session.expiresAt}</p>;
}
```

Use `currentUser()` when you need the full user profile — it makes an extra request to the userinfo endpoint and handles token refresh automatically.

#### Verifying a Bearer Token in an External API

If you have a separate API server that receives Authgear-issued access tokens (e.g. from a mobile app), use `verifyAccessToken()` to validate them:

```typescript
import { verifyAccessToken } from "@authgear/nextjs/server";
import { authgearConfig } from "@/lib/authgear";
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const token = authHeader.slice(7);

  try {
    const payload = await verifyAccessToken(token, authgearConfig);
    return NextResponse.json({ sub: payload.sub });
  } catch {
    return NextResponse.json({ error: "Invalid token" }, { status: 401 });
  }
}
```

{% hint style="info" %}
`verifyAccessToken` validates the JWT signature and expiry locally using the Authgear JWKS endpoint — no session cookie is involved.
{% endhint %}

#### Checking Session State

`useAuthgear()` returns `isLoaded` to indicate whether the initial session check has completed. Use it to avoid a flash of unauthenticated UI:

```typescript
const { isAuthenticated, isLoaded } = useAuthgear();

if (!isLoaded) return <p>Loading...</p>;
```

#### Opening User Settings

The `<UserSettingsButton>` component opens Authgear's hosted [Settings page](/customization/built-in-ui/user-settings) in a new tab with the current user already authenticated — no Server Action required.

**Add the button to your component:**

```typescript
import { UserSettingsButton } from "@authgear/nextjs/client";

export default function Dashboard() {
  return (
    <UserSettingsButton>Account Settings</UserSettingsButton>
  );
}
```

The button must be rendered inside `<AuthgearProvider>`. It exchanges the user's session for a short-lived token and opens the pre-authenticated Settings URL in a new tab.

**Style it like any button** — `<UserSettingsButton>` accepts all standard HTML button props:

```tsx
<UserSettingsButton
  className="btn btn-primary"
  style={{ padding: "0.5rem 1rem" }}
  disabled={!isAuthenticated}
>
  Account Settings
</UserSettingsButton>
```

The default label is `"Account Settings"` if no children are provided.

**Customising the route path**

By default, `<UserSettingsButton>` sends requests to `/api/auth/open`. If your auth routes live at a different path, set `openPagePath` on `<AuthgearProvider>`:

```tsx
<AuthgearProvider openPagePath="/api/my-auth/open">
  {children}
</AuthgearProvider>
```

**Error behaviour**

If the user is not authenticated or their session has no refresh token, the new tab will display an error rather than the Settings page. Guard the button with an authentication check to avoid this:

```tsx
const { isAuthenticated } = useAuthgear();

<UserSettingsButton disabled={!isAuthenticated}>
  Account Settings
</UserSettingsButton>
```

#### Controlling Single Sign-On Behaviour

By default, Authgear reuses its server-side session across sign-ins (SSO enabled). You can change this globally via `isSSOEnabled` in `createAuthgearHandlers`, or override it per sign-in call using the `prompt` option.

**Disable SSO globally** — always show the login form:

```typescript
// src/app/api/auth/[...authgear]/route.ts
import { createAuthgearHandlers } from "@authgear/nextjs";
import { authgearConfig } from "@/lib/authgear";

export const { GET, POST } = createAuthgearHandlers({
  ...authgearConfig,
  isSSOEnabled: false,
});
```

**Override per sign-in call** using `PromptOption`:

```typescript
import { PromptOption } from "@authgear/nextjs";

// via hook
const { signIn } = useAuthgear();
signIn({ prompt: PromptOption.Login }); // always show login form

// via component
<SignInButton signInOptions={{ prompt: PromptOption.Login }}>Sign In</SignInButton>
```

| `PromptOption`       | Effect                                                |
| -------------------- | ----------------------------------------------------- |
| `PromptOption.Login` | Always show the login form                            |
| `PromptOption.None`  | Never show the login form; error if not authenticated |

Per-call `prompt` takes precedence over the global `isSSOEnabled` setting.

### Next Steps

* [Protect additional pages with middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)
* [Backend API integration guide](https://docs.authgear.com/get-started/backend-api)
* [`@authgear/nextjs` API reference](https://authgear.github.io/authgear-sdk-nextjs/)


# Express

Authentication for Express.JS apps with Authgear and OAuth2

Authgear makes it easy to add user authentication to a regular web app that is not powered by any framework. You can do this by integrating Authgear into your application as an OIDC identity provider.

In this post, you'll learn how to add user authentication to an Express.js application using Authgear.

### What You Will Learn

* How to create an Authgear Application.
* How to add User Login to an Express app using Authgear.
* How to request user info from Authgear.

### Pre-requisites

You'll need the following to follow along with this tutorial:

* Node.js Installed
* A free Authgear account. Sign up for one [here](https://portal.authgear.com/).

Follow this guide to add user authentication to an Express application using Authgear in 🕐 **15-minute**.

{% hint style="info" %}
**Check out and clone** [**the Sample Project on GitHub**](https://github.com/authgear/authgear-example-express)**.**
{% endhint %}

## Setup Application in Authgear

Login to your Authgear account (you can sign up for one [here](https://portal.authgear.com/)) to perform the steps in this section.

### Create an application in the Portal

In this step, we'll create the Authgear client application that we'll use later to connect Authgear to the Express application.

After you log in to Authgear Portal, select a project then navigate to **Applications** on the left menu bar.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption><p>navigate to applications</p></figcaption></figure>

Next, click on the **⊕Add Application** button on the top toolbar to open the **New Application** page.

Enter an application name (for example: "My App") and select `OIDC/SAML Client Application` as the Application Type. Click Save to create the app.

<figure><img src="/files/6oDkcVt4JubJQXMk7njt" alt=""><figcaption><p>new app form oidc app</p></figcaption></figure>

On the next screen, you'll see links to tutorials for different frameworks. Click **Next** to skip to the application configuration page.

{% hint style="info" %}
Note the Client ID and Client Secret for your new application as you'll use in a later step to configure your Express app.
{% endhint %}

### Configure Authorize Redirect URI

The Authorized Redirect URI should be a page on your Express application where you make an HTTP(S) request to the Authgear token endpoint and exchange an Authorization Code for an Access Token.

For our demo Express application for this guide, this URL will be `http://localhost:3000/auth-redirect`. So, scroll to the URI section of your application configuration page in the Authgear Portal and add `http://localhost:3000/auth-redirect` as an authorized redirect URI.

<figure><img src="/files/ERSqFNytWaMwN7qRDsn6" alt="" width="375"><figcaption><p>authgear set redirect uri</p></figcaption></figure>

Click **Save** to keep your changes.

## How to Add User Authentication to Express.js App using Authgear

In this section, we'll be building a simple Express app that has the following features:

* A landing page with a login link that takes users to the login route.
* A `/start-login` route that initiates the authentication flow.
* Logic that exchanges the authorization code from Authgear for an access token.
* A page that uses that access token to fetch user info from Authgear.
* A logout button to end the current user session.
* Refresh access token.

### Step 1: Create Express App

It is now time to create the Express app that will be connecting to our Authgear client application. Run the following commands to create a new folder for the project and set it as your current working directory:

```sh
# create project directory
mkdir my-express-app
#set project directory as current working directory
cd my-express-app
```

#### Install basic project dependencies

Next, install the Express npm package and other dependency packages like `axios`, `nodemon`, `session`, and `dotenv`.

We'll be using the `axios` library to make HTTP requests in the example app. We'll also use `nodemon` to add hot reload to our development environment. Run the following commands from the root of your Express project directory to install Express and other dependencies:

```sh
# Generate package.json
npm init -y
# Install Express 
npm install express
# Install axios
npm install axios
# Install express-session
npm install express-session
# Install dotenv
npm install dotenv
# Install Nodemon (Used to add hot road to JavaScript development)
npm install -D nodemon
```

#### Create `app.js` file

Once Express and the other dependencies are installed, create a new `app.js` file in the root of your project folder and add the following code to the file:

```javascript
//app.js
const express = require('express');
const axios = require("axios");
const session = require("express-session");
require("dotenv").config();

const app = express();
const port = process.env.PORT || 3000;
app.use(express.urlencoded({ extended: true }));
app.use(
  session({
    secret: "your_strong_secret_key", // Replace with a strong, randomly generated secret
    cookie: {},
  })
);

app.get("/", async (req, res) => {
    res.send(`
        <div style="max-width: 650px; margin: 16px auto; background-color: #EDEDED; padding: 16px;">
          <p>Hi there!</p>
          <p>This demo app shows you how to add user authentication to your Express app using Authgear</p>
            <p>Checkout <a href="https://docs.authgear.com">docs.authgear.com</a> to learn more about adding Authgear to your apps.</p>
          <a href="/login">Login</a>
        </div>
      `);
  });

app.get("/login", async(req, res) => {
    // TODO add implementation here
});

app.get("/auth-redirect", async(req, res) => {
    // TODO add implementation here
});

app.listen(port, () => {
    console.log(`server started on port ${port}!`);
});
```

#### **Update package.json**

Add the following code to the `scripts` section of the `package.json` file in the root directory of your Express project:

```json
"start": "node app.js",
"dev": "nodemon app.js"
```

#### Checkpoint

Now run the `npm run dev` command and you should get a page like this on a web browser when you visit `localhost:3000`:

<figure><img src="/files/iy9cODaHZ3eoEEl7d3LP" alt=""><figcaption><p>express demo app homepage</p></figcaption></figure>

### Step 2: Implement Login Route

Here we'll be implementing the login route in our Express app. This route will initiate the authentication flow by redirecting the user's browser to the login page (AuthUI). This process involves redirecting the user to your Authgear project/client application's authorization URL (`https://<AUTHGEAR_ENDPOINT>/oauth2/authorize` ).

Update the code for the `app.get("/login", ...)` route to the following:

```javascript
app.get("/login", async(req, res) => {
    const scopes = "openid offline_access";
    const authorizedUrl = new URL("/oauth2/authorize", process.env.AUTHGEAR_ENDPOINT);
    authorizedUrl.searchParams.set('client_id', process.env.AUTHGEAR_CLIENT_ID);
    authorizedUrl.searchParams.set('redirect_uri', process.env.AUTHGEAR_REDIRECT_URL);
    authorizedUrl.searchParams.set('response_type', 'code');
    authorizedUrl.searchParams.set('scope', scopes);
    res.redirect(authorizedUrl);
});
```

Create a `.env` file on the root directory of your project and add your Authgear client application configuration (Client ID, secret, endpoint) using the following fields:

```
AUTHGEAR_CLIENT_ID=<CLIENT_ID>
AUTHGEAR_CLIENT_SECRET=<CLIENT_SECRET>
AUTHGEAR_ENDPOINT=<AUTHGEAR_ENDPOINT>
AUTHGEAR_REDIRECT_URL=http://localhost:3000/auth-redirect
```

You can get the value for Client ID and Client Secret from the configuration page for the client application you created in the earlier step, [Create an application in the Portal](#create-an-application-in-the-portal). The Authgear Endpoint looks like `https://project_id.authgear.cloud`if you are using Authgear Cloud version.

Now if you save your code and restart your app, clicking on the Login link should redirect to the Authgear authorization page.

<figure><img src="/files/Cs8I3NeFH4JSuG82EhKc" alt=""><figcaption><p>authui login page</p></figcaption></figure>

On successful login, an authorization code will be sent back to your Express application.

### Step 3: Exchange Authorization Code For Access Token

After the user logs in and grants authorization to your app on the Login page, they are redirected back to the [redirect URL you specified earlier](#configure-authorize-redirect-uri). In addition to this redirect, an authorization code is sent via a `code` URL query parameter.

In this step, we will be exchanging the authorization code for an access code that users can later use to access protected resources.

Update the code for the `app.get("/auth-redirect", ...)` route to the following:

```javascript
app.get("/auth-redirect", async(req, res) => {
        if (req.query.code != null) {
            const data = {
              client_id: process.env.AUTHGEAR_CLIENT_ID,
              client_secret: process.env.AUTHGEAR_CLIENT_SECRET,
              code: req.query.code,
              grant_type: 'authorization_code',
              response_type: 'code',
              redirect_uri: process.env.AUTHGEAR_REDIRECT_URL
            };
        
            try {
              const tokenUrl = new URL("/oauth2/token", process.env.AUTHGEAR_ENDPOINT);
              const getToken = await axios.post(tokenUrl, data, {
                headers: { "Content-Type": "application/x-www-form-urlencoded" }
              });
        
              const accessToken = getToken.data.access_token;
        
              req.session.access_token = accessToken;
              res.redirect("/");

            } catch (error) {
              res.send("An error occurred! Login could not complete. Error data: " + error);
            }
          } else {
            res.send("No Authorization code in URL");
          }
        
});
```

The above code sends an HTTP POST request to the token endpoint (`https://<AUTHGEAR_ENDPOINT>/oauth2/token`). The authorization code we got from the previous step is sent along with other client credentials in the HTTP request body. The header should contain `"Content-Type": "application/x-www-form-urlencoded"`

A valid access token is returned in the response to the HTTP(S) request in `response.data.access_token`. In the above code sample, we've saved this access token temporally using express-session so we can access it from `"/"` route after redirecting the user.

We can now use this access token to make authenticated requests to protected resources in our app or from the Authgear User Info endpoint. In the next step, we'll attempt to get the current user's info from Authgear using the access token.

### Step 4: Get User Info

Authgear provides an endpoint where your application can request user info (`https://<AUTHGEAR_ENDPOINT>/oauth2/userinfo`). This endpoint will return the user's details on Authgear like their email address, gender, full name, and more.

To get user info in our example app, update the `app.get("/", ...)` `app.js`, to use the following conditional statement to render a different block of code when a valid access token is set for the current session:

```javascript
app.get("/", async (req, res) => {
  if (req.session.access_token != null) {
    const accessToken = req.session.access_token;
    //Now use access token to get user info.
  try {
        const userInfoUrl = new URL(
            "/oauth2/userinfo",
            process.env.AUTHGEAR_ENDPOINT
          );
          const getUserInfo = await axios.get(userInfoUrl, {
            headers: { Authorization: "Bearer " + accessToken },
          });
          const userInfo = getUserInfo.data;
          res.send(`
              <div style="max-width: 650px; margin: 16px auto; background-color: #EDEDED; padding: 16px;">
                <p>Welcome ${userInfo.email}</p>
                <p>User Info:</p>
                <div>
                  <pre>${JSON.stringify(userInfo, null, 2)}</pre>
                </div>
                  <p> 
                      <a href="/logout">Logout</a>
                  </p>
              </div>
          `);
    }
    catch (error) {
        res.send("Unable to get User Info: " + error);
    }
  } else {
    res.send(`
            <div style="max-width: 650px; margin: 16px auto; background-color: #EDEDED; padding: 16px;">
              <p>Hi there!</p>
              <p>This demo app shows you how to add user authentication to your Express app using Authgear</p>
                <p>Checkout <a href="https://docs.authgear.com">docs.authgear.com</a> to learn more about adding Authgear to your apps.</p>
              <a href="/login">Login</a>
            </div>
          `);
  }
});
```

Here our app sends another HTTP(S) request, but this time to the user info endpoint and the request type is GET. The access token is sent as a *Bearer* authorization header.

At this point, save all changes and restart the application. Try logging in all over again, at the end you should be greeted with "Welcome \[your email address]" and a dump of the response from the UserInfo endpoint if the access token in your authorization header is valid.

<figure><img src="/files/HcOsj8iaS8la82l9pkzk" alt=""><figcaption><p>express demo</p></figcaption></figure>

### Step 5: Logout

To allow your users log out, add a new `/logout` route to your Express application. Within the route, you'll implement code that will call express-session `destroy()` method to delete the access token from the session. Then you'll redirect the user to Authgear's logout endpoint to log the user's session on Authgear.

To redirect the user back to your Express app after they logout, add `http://localhost:3000` as a Post Logout Redirect URI for your client application in Authgear Portal.

<figure><img src="/files/Kv9nsTVzKRkxuH6KBXUG" alt="" width="563"><figcaption><p>set post logout redirect uri</p></figcaption></figure>

Your implementation of the logout route should look like this:

```javascript
app.get("/logout", async (req, res) => {
  const accessToken = req.session.access_token;
  const endSessionUrl = new URL(
    "/oauth2/end_session",
    process.env.AUTHGEAR_ENDPOINT
  );
  endSessionUrl.searchParams.set("post_logout_redirect_uri", "http://localhost:3000");
  
  // Remove access token, and refresh token from express-session
  req.session.destroy();
  
  res.set("Authorization", "Bearer " + accessToken);
  res.redirect(endSessionUrl);
});
```

Finally, place a link to the logout route below the `</prev>` tag using the following code:

```html
<a href="/logout">Logout</a>
```

### Step 6: Refresh Access Token

An access token is usually only valid for a short period. However, you can use the refresh token, also included in the response from the token endpoint in [step 3](#step-3-exchange-authorization-code-for-access-token) to request a new access token.

{% hint style="info" %}
Note that you must include `offline_access` in the scope parameter of your authorization request to get a refresh token from Authgear.
{% endhint %}

We recommend that you check that an access token is still valid before using it in your authorization header to access protected resources like the User Info endpoint.

To add the capability of refreshing an expired access token to our Express app, add the following function to app.js:

```javascript
const refreshAccessTokenIfExpired = async (refreshToken, expiresAt, req) => {
    const currentTime = (new Date(Date.now()).getTime());
    if (expiresAt<currentTime) {
        const tokenUrl = new URL("/oauth2/token", process.env.AUTHGEAR_ENDPOINT);
        const data = {
            client_id: process.env.AUTHGEAR_CLIENT_ID,
            client_secret: process.env.AUTHGEAR_CLIENT_SECRET,
            grant_type: "refresh_token",
            refresh_token: refreshToken
          };

          try {
            const getToken = await axios.post(tokenUrl, data, {
              headers: { "Content-Type": "application/x-www-form-urlencoded" },
            });
        
            const accessToken = getToken.data.access_token;
            const expiresAt = new Date(Date.now()).getTime() + getToken.data.expires_in * 1000;
        
            req.session.access_token = accessToken;
            req.session.expire_at = expiresAt;
            return accessToken;
          } catch (error) {
            throw new Error('Failed to refresh access token: ' + error);
          }
    } else {
        return req.session.access_token;
    }
};
```

Next, find the following line within the `app.get("/auth-redirect", ...)` route:

```javascript
req.session.access_token = accessToken;
```

Add the following code just after the above line:

```javascript
req.session.expire_at = new Date(Date.now()).getTime() + getToken.data.expires_in * 1000;
req.session.refresh_token = getToken.data.refresh_token;
```

The above code will store the values of `refresh_token` and `expires_in` that were returned in [step 3](#step-3-exchange-authorization-code-for-access-token) in express-session. To convert expire\_in to a time in the future, we multiply it by 1000 and add that to the current time.

Finally, find the following line in the `app.get("/", ...)` route:

```javascript
const accessToken = req.session.access_token;
```

Replace the above line with the following code:

```javascript
const accessToken = await refreshAccessTokenIfExpired(req.session.refresh_token, req.session.expire_at, req);
```

Now our Express app gets an access token by first calling the new `refreshAccessTokenIfExpired()` method. If the current access token is expired, the method will make a request to Authgear's token endpoint for a new access token. This request needs to have a `grant_type` of `refresh_token`, and the `refresh_token` should be included in the POST request body.

See more about refreshing access tokens [here](/reference/tokens/refresh-token).

## Next steps, Calling an API <a href="#next-steps-calling-an-api" id="next-steps-calling-an-api"></a>

To access restricted resources on your backend application server, the HTTP requests should include the access token in their Authorization headers.

For example:

```javascript
axios.get("https://<your_backend_url>", {
        headers: { Authorization: "Bearer " + accessToken },
      });
```

### Conclusion

And there you have it, you've successfully added user authentication to your Express app using Authgear as the OAuth provider.

You can add so much more to your app with the new Authgear authentication, like protecting your own app endpoint with the access code. You can also store the access token securely to persist the user session using express-session and cookies.

Here's a link to the complete code for [our example code on Github](https://github.com/authgear/authgear-example-express).


# Python Flask App

Authentication for a Python web application

This guide demonstrates how to add authentication with Authgear to a Python web application built with the [Flask](https://palletsprojects.com/p/flask/) framework using the [Authlib](https://authlib.org/) OAuth library. The full source code for this sample project can be found on the [GitHub repo](https://github.com/authgear/authgear-example-python-flask).

### Learning objectives

You will learn the following:

* How to create an app on Authgear.
* How to enable Email-based login.
* Add sign-up and login features to the Flask app.

### **Prerequisites**

Before you begin, you'll need the following:

* A **free Authgear account**. [Sign up](https://accounts.portal.authgear.com/signup) if you don't have one already.
* Make sure that [Python](https://www.python.org/downloads/) 3.10 or above is installed on your machine.
* Download and Install [Pip](https://pip.pypa.io/en/stable/installation/) to manage project packages.

### Part 1: Configure Authgear

To use Authgear services, you’ll need to have an application set up in the Authgear [Dashboard](https://portal.authgear.com/). This setup allows users in Authgear to sign in to the Flask application automatically once they are authenticated by Authgear.

#### Step 1: Configure an application

To set up the application, navigate to the [Authgear Portal UI](https://portal.authgear.com/) and select **Applications** on the left-hand navigation bar. Use the interactive selector to create a new **Authgear OIDC Client application** or select an existing application that represents the project you want to integrate with.

<figure><img src="/files/QtjQ1IaUJEWCYIWZfyDd" alt=""><figcaption></figcaption></figure>

Every application in Authgear is assigned an alphanumeric, unique client ID that your application code will use to call Authgear APIs through the Authlib client library in the Flask app. Record the generated Authgear Issuer `Domain` (for example, `example-auth.authgear-apps.com`), `CLIENT ID`, `CLIENT SECRET` from the output. You will use these values in Part 2 for the Flask app config.

<figure><img src="/files/vkRW8iKkWPkz2tmJyreb" alt=""><figcaption></figcaption></figure>

#### Step 2: Configure **Redirect URI**

An **Authorized Redirect URI** of your application is the URL that Authgear will redirect to after the user has authenticated in the Authgear to complete the authentication process. In our case, it will be a home page for our Flask and it will run at <http://localhost:3000>.

Set the following [http://localhost:3000/callback](#learning-objectives) to the **Authorized Redirect URIs** field. If not set, users will not be returned to your application after they log in.

#### Step 3: Choose a Login method

After you create the **Authgear app**, you choose how users need to **authenticate on the login page**. From the **Authentication** tab, navigate to **Login Methods**, you can choose a **login method** from various options including, by email, mobile, or social, just using a username or the custom method you specify. For this demo, we choose the **Email+Passwordless** approach where our users are asked to register an account and log in by using their emails. They will receive a One-time password (OTP) to their emails and verify the code to use the app.

<figure><img src="/files/YhpLYaqlP8aNp6K61JBb" alt=""><figcaption></figcaption></figure>

### Part 2: **Create a Flask application**

Next, create a Flask application with a single page and routes for home, callback, login, and logout flows.

#### Step 1: Configure an .env file <a href="#configure-your-env-file" id="configure-your-env-file"></a>

Start with creating a `requirements.txt` file in your project directory:

```
flask>=2.0.3
python-dotenv>=0.19.2
authlib>=1.0
requests>=2.27.1
```

Run `pip install -r requirements.txt` from your command-line interface to make these dependencies available to the Python project.

#### Step 2: Setup the application <a href="#configure-your-env-file" id="configure-your-env-file"></a>

Create a `server.py` file in the project directory that contains application logic. Add the necessary libraries the application uses.

```python
import json
from os import environ as env
from urllib.parse import quote_plus, urlencode

from authlib.integrations.flask_client import OAuth
from dotenv import find_dotenv, load_dotenv
from flask import Flask, redirect, render_template, session, url_for
```

Load the configuration `.env` file to use values such as `AUTHGEAR_CLIENT_ID AUTHGEAR_CLIENT_SECRET`, `AUTHGEAR_DOMAIN` and `APP_SECRET_KEY` in the app.

Configure Authlib to handle the application's authentication with Authgear based on OIDC:

```python
oauth = OAuth(app)

oauth.register(
    "authgear",
    client_id=env.get("AUTHGEAR_CLIENT_ID"),
    client_secret=env.get("AUTHGEAR_CLIENT_SECRET"),
    client_kwargs={
        "scope": "openid offline_access",
    },
    server_metadata_url=f'https://{env.get("AUTHGEAR_DOMAIN")}/.well-known/openid-configuration',
)
```

#### Step 3: Setup the application routes <a href="#setup-your-routes" id="setup-your-routes"></a>

When visitors to the app visit the `/login` route, they'll be redirected to Authgear to begin the authentication flow.

```python
@app.route("/login")
def login():
    return oauth.authgear.authorize_redirect(
        redirect_uri=url_for("callback", _external=True)
    )
```

Once users complete the login process using Authgear, they will be redirected back to the application's `/callback` route. This route ensures that the user's session is saved, so they won't need to log in again during subsequent visits.

```python
@app.route("/callback", methods=["GET", "POST"])
def callback():
    token = oauth.authgear.authorize_access_token()
    session["user"] = token
    return redirect("/")
```

**Refresh Token**

Calling the `authorize_access_token()` method of the Flask Authlib package will include a refresh token in the token response, provided your Flask application has `offline_access` as one of the OAuth 2.0 scopes.

Authlib will also use the refresh token to obtain a new access token automatically when the current access token has expired.

**Logout**

The route `/logout` manages the user's logout process from the application. It clears the user's session within the app and momentarily redirects to Authgear's logout endpoint to guarantee a thorough session clearance. After this, users are navigated back to your home route (which we'll discuss shortly).

```python
@app.route("/logout")
def logout():
    session.clear()
    return redirect(
        "https://"
        + env.get("AUTHGEAR_DOMAIN")
        + "/oauth2/end_session"
    )
```

The home route will either display the details of a logged-in user or provide an option for visitors to sign in.

```python
@app.route("/")
def home():
    return render_template(
        "home.html",
        session=session.get("user"),
        pretty=json.dumps(session.get("user"), indent=4),
    )
```

#### Step 4: Add UI page <a href="#setup-your-routes" id="setup-your-routes"></a>

Create a new sub-directory in the project folder named `templates`, and create a file `home.html`.

```html
<html>
  <head>
    <meta charset="utf-8" />
    <title>Authgear Flak Login Example</title>
  </head>
  <body>
    <div data-gb-custom-block data-tag="if">
    <h1>Welcome {{session.userinfo.name}}!</h1>
    <p><a href="/logout" id="qsLogoutBtn">Logout</a></p>
    <div><pre>{{pretty}}</pre></div>
    <div data-gb-custom-block data-tag="else"></div>
    <h1 id="profileDropDown">Welcome Guest</h1>
    <p><a href="/login" id="qsLoginBtn">Login</a></p>
    </div>
  </body>
</html>
```

#### Step 5: Run the application

Run the application from the project root directory:

`python server.py`

The application should now be accessible to open from a browser at [http://localhost:3000](http://localhost:3000/).

### Next steps

There is so much more you can do with Authgear. Explore other means of login methods such as using [Magic links](https://docs.authgear.com/strategies/email-login-link) in an email, [social logins](https://docs.authgear.com/strategies/how-to-setup-sso-integrations), or [WhatsApp OTP](https://docs.authgear.com/strategies/whatsapp-otp-login). For the current application, you can also [add more users](https://docs.authgear.com/strategies/user-identity-and-authenticator) from the Authgear portal.


# Java Spring Boot

Authentication for Spring Boot App with Authgear and OAuth2

In this guide, you will learn how to add authentication to your Java Spring Boot application using [OAuth2](https://tools.ietf.org/html/rfc6749) with Authgear as the **Identity Provider (IdP)**.

### Learning objectives

You will learn the following:

* How to create an app on Authgear.
* How to enable Email based login.
* Add sign-up and login features to Spring Boot App.

### **Prerequisites**

Before you get started, you will need the following:

* Java 17 or higher.
* A **free Authgear account**. [Sign up](https://oursky.typeform.com/to/S5lvI8rN) if you don't have one already.

### Add login to your Spring Webapp

### Part 1: Configure Authgear

To use Authgear services, you’ll need to have an application set up in the Authgear [Dashboard](http://portal.authgear.com/). The Authgear application is where you will configure how you want authentication to work for the project you are developing.

#### Step 1: Configure an application

Use the interactive selector to create a new **Authgear OIDC Client application** or select an existing application that represents the project you want to integrate with.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae4853e73acf7a9284f8a8_Untitled%20(2)%20(1).png" alt=""><figcaption></figcaption></figure>

Every application in Authgear is assigned an alphanumeric, unique client ID that your application code will use to call Authgear APIs through the Spring Boot [OAuth 2 Client](https://docs.spring.io/spring-security/reference/reactive/oauth2/client/index.html). Note down the Authgear issuer (for example, <https://example-auth.authgear.cloud/>), CLIENT ID, CLIENT SECRET, and OpenID endpoints from the output. You will use these values in the next step for the client app config.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae485914ea3016deaaad18_Untitled%20(3)%20(1).png" alt=""><figcaption></figcaption></figure>

#### Step 2: Configure **Redirect URI**

A **Redirect URI** is a URL in your application that you would like Authgear to redirect users to after they have authenticated. In our case, it will be a home page for our Spring Boot App. If not set, users will not be returned to your application after they log in.

To follow the example in this post, add the following URL as a redirect URI:

```
http://localhost:8080/login/oauth2/code/authgear
```

#### Step 3: Choose a Login method

After you create the Authgear app, you choose how users need to **authenticate on the login page**. From the “Authentication” tab, navigate to “Login Methods”, you can choose a **login method** from various options including by email, mobile, or social, just using a username or the custom method you specify. For this demo, we choose the **Email+Passwordless** approach where our users are asked to register an account and log in by using their emails. They will receive a One-time password (OTP) to their emails and verify the code to use the app.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae48ba190172f7f9f9cc0e_Untitled%20(4)%20(1).png" alt=""><figcaption></figcaption></figure>

### Part 2: Configure Spring Boot application

#### Step 1: Add Spring dependencies

To create a new Spring Boot application you use the [Spring Initializr](https://start.spring.io/). Then you add dependencies to pom.xml file such as [spring-boot-starter-oauth2-client](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client) starter provides all the Spring Security dependencies needed to add authentication to your web application and Thymeleaf is used just to build a single page UI.

```

  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-oauth2-client</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
      <dependency>
          <groupId>org.thymeleaf.extras</groupId>
          <artifactId>thymeleaf-extras-springsecurity6</artifactId>
          <version>3.1.1.RELEASE</version>
      </dependency>
  </dependencies>
  
```

#### Step 2: Configure OIDC authentication with Authgear

Spring Security makes it easy to configure your application for authentication with OIDC providers such as Authgear. We need to add the client credentials to the **application.properties** file with your Auhgear provider configuration. You can use the sample below and replace properties with the values from your Authgear app:

```properties

spring.security.oauth2.client.registration.authgear.client-id={your-client-id}
spring.security.oauth2.client.registration.authgear.client-secret={your-client-secret}
spring.security.oauth2.client.registration.authgear.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.authgear.scope=openid,offline_access
spring.security.oauth2.client.registration.authgear.redirect-uri=http://localhost:8080/login/oauth2/code/authgear/
spring.security.oauth2.client.provider.authgear.token-uri=https://{DOMAIN}/oauth2/token
spring.security.oauth2.client.provider.authgear.authorization-uri=https://{DOMAIN}/oauth2/authorize

# To logout from the app
authgear.oauth2.end-session-endpoint=https://{DOMAIN}/oauth2/end_session
  
```

#### Step 3: Add login to your application

To enable user login with Authgear, create a class that will provide an instance of **SecurityFilterChain**, add the `@EnableMethodSecurity` annotation, and override the necessary method:

```java

@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {

    @Value("${authgear.oauth2.end-session-endpoint}")
    private String endSessionEndpoint;

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((requests) -> requests
                // allow anonymous access to the root page
                .requestMatchers("/").permitAll()
                // authenticate all other requests
                .anyRequest().authenticated())
            // enable OAuth2/OIDC
            .oauth2Login(withDefaults())
            // configure logout handler
            .logout(logout -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/")
                .addLogoutHandler(oidcLogoutHandler()));
        return http.build();
    }

    LogoutHandler oidcLogoutHandler() {
        return (request, response, authentication) -> {
            try {
                response.sendRedirect(endSessionEndpoint);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        };
    }
}
  
```

#### Step 4: Add the front page

We create a simple home.html page using Thymeleaf templates. When a user opens the page running on <http://localhost:8080/>, we show the page with buttons for login or logout:

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae4b0a99ffe304fc9e50f4_Untitled%20(11).png" alt=""><figcaption></figcaption></figure>

#### Step 5: Add controller

Next, we create a controller class to handle the incoming request. This controller renders the home.html page. When the user authenticates, the application retrieves the user's profile information attributes to render the page.

```

@Controller
public class HomeController {
    @GetMapping("/")
    String home() {
        return "home";
    }
}
  
```

#### Step 6: Run the Application

To run the application, you can execute the `mvn spring-boot:run` goal. Or run from your editor the main ExampleApplication.java file. The sample application will be available at <http://localhost:8080/>.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae61c4c93bc48ef731785f_Untitled%20(13).png" alt=""><figcaption></figcaption></figure>

Click on the **Login** button to be redirected to the Authgear login page.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae61ca49cc298d411f03d6_Untitled%20(14).png" alt=""><figcaption></figcaption></figure>

You can also customize the login page UI view from the Authgear Portal. After you sign up, you will receive an OTP code in your email to verify your identity.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae61e40046a0f0dbeff9d0_Untitled%20(15).png" alt=""><figcaption></figcaption></figure>

And log into your new account, you will be redirected back to the home page:

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64ae61eb29d7e0cb537f5f01_Untitled%20(16).png" alt=""><figcaption></figcaption></figure>

You have successfully configured a Spring Boot application to use Authgear for authentication. Now users can sign up for a new account, log in, and log out. The full source code of the examples can be found [on GitHub](https://github.com/Boburmirzo/authgear-spring-oauth2-example).

### Next steps

There is so much more you can do with Authgear. Explore other means of login methods such as using [Magic links](https://docs.authgear.com/strategies/email-login-link) in an email, [social logins](https://docs.authgear.com/strategies/how-to-setup-sso-integrations), or [WhatsApp OTP](https://docs.authgear.com/strategies/whatsapp-otp-login). For the current application, you can also [add more users](https://docs.authgear.com/strategies/user-identity-and-authenticator) from the Authgear portal.


# ASP.NET Core MVC

Add authentication for ASP.NET app with Authgear

In this guide, you will learn how to add authentication features with [Authgear](https://www.authgear.com/) by implementing an [OpenID Connect](https://docs.authgear.com/concepts/identity-fundamentals#open-id-connect) flow, then retrieving OAuth tokens, to call APIs. View [implementation](https://github.com/authgear/authgear-example-dotnet) on GitHub.

### Learning objectives

You will learn the following throughout the article:

* How to add user login, sign-up, and logout to [ASP.NET](http://asp.net) Core Applications.
* How to use the [ASP.NET](http://asp.net) Core Authorization Middleware to protect [ASP.NET](http://asp.net) Core application routes.

## Add authentication to [ASP.NET](http://asp.net) Core App

### **Prerequisites**

Before you get started, you will need the following:

* A **free Authgear account**. [Sign up](https://accounts.portal.authgear.com/signup) if you don't have one already.
* [.NET 7](https://dotnet.microsoft.com/en-us/download) downloaded and installed on your machine. You can also use [Visual Studio](https://visualstudio.microsoft.com/) and [VS code](https://code.visualstudio.com/) to automatically detect the .NET version.

### Part 1: Configure Authgear

To use Authgear services, you’ll need to have an application set up in the Authgear [Dashboard](https://portal.authgear.com/). The Authgear application is where you will configure how you want to authenticate and manage your users.

#### Step 1: Configure an application

Use the interactive selector to create a new **Authgear OIDC Client application** or select an existing application that represents the project you want to integrate with.

<figure><img src="/files/QtjQ1IaUJEWCYIWZfyDd" alt=""><figcaption></figcaption></figure>

Every application in Authgear is assigned an alphanumeric, unique client ID that your application code will use to call Authgear APIs through the OpenID Connect Client in the .NET app. Note down the Authgear `ISSUER` (for example, <https://example-auth.authgear.cloud>), `CLIENT ID`, `CLIENT SECRET`, and `OpenID Token Endpoint` (<https://example-auth.authgear.cloud/oauth2/token>) from the output. You will use these values in the next step for the client app config.

<figure><img src="/files/vkRW8iKkWPkz2tmJyreb" alt=""><figcaption></figcaption></figure>

#### Step 2: Configure **Redirect URI**

A **Redirect URI** of your application is the URL that Authgear will redirect to after the user has authenticated in order for the **OpenID Connect middleware** to complete the authentication process. In our case, it will be a home page for our [ASP.NET](http://asp.net) and it will run at<http://localhost:5002>.

Set the following redirect URI: <http://localhost:5002/signin-oidc> If not set, users will not be returned to your application after they log in.

#### Step 3: Enable Access Token

Also, enable **Issue JWT as an access token** option under the **Access Token** section of the app configuration:

<figure><img src="/files/VD3nGw6F4vuQwPDuFH2C" alt=""><figcaption></figcaption></figure>

#### Step 4: Choose a Login method

After you create the **Authgear app**, you choose how users need to **authenticate on the login page**. From the **Authentication** tab, navigate to **Login Methods**, you can choose a **login method** from various options including, by email, mobile, or social, just using a username or the custom method you specify. For this demo, we choose the **Email+Passwordless** approach where our users are asked to register an account and log in by using their emails. They will receive a One-time password (OTP) to their emails and verify the code to use the app.

<figure><img src="/files/YhpLYaqlP8aNp6K61JBb" alt=""><figcaption></figcaption></figure>

### Part 2: Configure [ASP.NET](http://asp.net) Core application to use Authgear

This guide will be used to provide a way for your users to log in to your [ASP.NET](http://asp.net/) Core application. The [project source code](https://github.com/authgear/authgear-example-dotnet) can be found on GitHub. If you are familiar with the steps, you can skip this part and clone the code repository and run the code sample by following the [README.md](https://github.com/authgear/authgear-example-dotnet/blob/main/README.md) file there.

#### Step 1: Install dependencies

To integrate Authgear with [ASP.NET](http://asp.net/) Core you will use both the Cookie and OpenID Connect (OIDC) authentication handlers. If you are not using a sample project and are integrating Authgear into your own existing project, then please make sure that you add `Microsoft.AspNetCore.Authentication.OpenIdConnect` packages to your application. Run the following command in your terminal or use your editor to include the NuGet package there:

```bash
Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect
```

#### Step 2: **Install and configure OpenID Connect Middleware**

To enable authentication in your [ASP.NET](http://asp.net) Core application, use the OpenID Connect (OIDC) middleware. Open `Startup` the class and in the `ConfigureServices` method, add the authentication services and call the `AddAuthentication` method. To enable cookie authentication, call the `AddCookie` method. Next, configure the OIDC authentication handler by adding method `AddOpenIdConnect` implementation. Configure other parameters, such as `Issuer`, `ClientId`, `ClientSecret` , and `Scope`. Here, is what looks like `Startup.cs` after you apply these changes:

```csharp
public class Startup
 {

     public IWebHostEnvironment Environment { get; }
     public IConfiguration Configuration { get; }

     public Startup(IWebHostEnvironment environment, IConfiguration config)
     {
         Environment = environment;
         Configuration = config;
     }

     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
     {
         app.UseRouting();
         app.UseAuthentication();
         app.UseAuthorization();
         app.UseEndpoints(endpoints =>
         {
             endpoints.MapRazorPages();
         });
     }

     public void ConfigureServices(IServiceCollection services)
     {
         // Prevent WS-Federation claim names being written to tokens
         JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

         services.AddAuthentication(options =>
         {
             options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
             options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
         })
         .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
         {
             // Use the strongest setting in production, which also enables HTTP on developer workstations
             options.Cookie.SameSite = SameSiteMode.Strict;
         })
         .AddOpenIdConnect(options =>
         {

             // Use the same settings for temporary cookies
             options.NonceCookie.SameSite = SameSiteMode.Strict;
             options.CorrelationCookie.SameSite = SameSiteMode.Strict;

             // Set the main OpenID Connect settings
             options.Authority = Configuration.GetValue<string>("OpenIdConnect:Issuer");
             options.ClientId = Configuration.GetValue<string>("OpenIdConnect:ClientId");
             options.ClientSecret = Configuration.GetValue<string>("OpenIdConnect:ClientSecret");
             options.ResponseType = OpenIdConnectResponseType.Code;
             options.ResponseMode = OpenIdConnectResponseMode.Query;
             string scopeString = Configuration.GetValue<string>("OpenIDConnect:Scope");
             options.Scope.Clear();
             scopeString.Split(" ", StringSplitOptions.TrimEntries).ToList().ForEach(scope =>
             {
                 options.Scope.Add(scope);
             });

             // If required, override the issuer and audience used to validate ID tokens
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 ValidIssuer = options.Authority,
                 ValidAudience = options.ClientId
             };

             // This example gets user information for display from the user info endpoint
             options.GetClaimsFromUserInfoEndpoint = true;

             // Handle the post logout redirect URI
             options.Events.OnRedirectToIdentityProviderForSignOut = (context) =>
             {
                 context.ProtocolMessage.PostLogoutRedirectUri = Configuration.GetValue<string>("OpenIdConnect:PostLogoutRedirectUri");
                 return Task.CompletedTask;
             };

             // Save tokens issued to encrypted cookies
             options.SaveTokens = true;

             // Set this in developer setups if the OpenID Provider uses plain HTTP
             options.RequireHttpsMetadata = false;
         });

         services.AddAuthorization();
         services.AddRazorPages();

         // Add this app's types to dependency injection
         services.AddSingleton<TokenClient>();
     }
 }
```

#### Step 3: Add Protected resource

Assume that there is a protected resource like a Razor page `Protected.cshtml` that is used to represent views:

```html
@page "/protected"
@model ProtectedModel

@addTagHelper*, Microsoft.AspNetCore.Mvc.TagHelpers

<style type="text/css">
button
{
  width: 200px;
}
</style>

<h1>Protected View</h1>

<h3>
    <p>Welcome: @Model.Username</a>
    <p>Current Access Token: @Model.AccessToken</a>
    <p>Current Refresh Token: @Model.RefreshToken</a>
    
    <form method="post">
        <p><button value="RefreshToken" asp-page-handler="RefreshToken">Refresh Token</button></p>
        <p><button value="Logout" asp-page-handler="Logout">Logout</button></p>
    </form>
</h3>
```

And `ProtectedModel.cs` class to which `Authorize` the attribute is applied requires authorization.

```csharp
[Authorize]
public class ProtectedModel : PageModel
{
    public string Username { get; set; }
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }

    private readonly TokenClient tokenClient;

    public ProtectedModel(TokenClient tokenClient)
    {
        this.tokenClient = tokenClient;
    }

    public async Task OnGet()
    {
        ClaimsPrincipal user = this.User;
        var givenName = user.FindFirstValue("given_name");
        var familyName = user.FindFirstValue("family_name");
        this.Username = $"{givenName} {familyName}";

        this.AccessToken = await this.tokenClient.GetAccessToken(this.HttpContext);
        this.RefreshToken = await this.tokenClient.GetRefreshToken(this.HttpContext);
    }

    public async Task<IActionResult> OnPostRefreshToken()
    {
        await this.tokenClient.RefreshAccessToken(this.HttpContext);
        this.AccessToken = await this.tokenClient.GetAccessToken(this.HttpContext);
        this.RefreshToken = await this.tokenClient.GetRefreshToken(this.HttpContext);
        return Page();
    }

    public async Task OnPostLogout()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
    }
}
```

To see protected data, users need to go through the authentication process via Authgear.

<figure><img src="/files/uu7takwrUAfZAcucqwv7" alt=""><figcaption></figcaption></figure>

If a user has not authenticated yet, `Unauthenticated.chtml` the page is rendered, an OpenID Connect redirect flow is triggered and the user needs to authenticate through the Authgear login page. See **Run the Application** section

After successful authentication, you should see the protected page with the following details:

<figure><img src="/files/RiyFtwOaUaYNC4dAJ4FP" alt="" width="531"><figcaption></figcaption></figure>

#### Step 4: Get and Use Refresh Token

As part of the OAuth 2.0 standard, we can use the refresh token returned by the token endpoint to get a new access token. Doing so enables our application to replace an expired access token without requiring the user to repeat the entire login process.

The following code in `ProtectedModel.cs` is responsible for doing that:

```csharp
public async Task<IActionResult> OnPostRefreshToken()
{
    await this.tokenClient.RefreshAccessToken(this.HttpContext);
    this.AccessToken = await this.tokenClient.GetAccessToken(this.HttpContext);
    this.RefreshToken = await this.tokenClient.GetRefreshToken(this.HttpContext);
    return Page();
}
```

**Note:** You must include `offline_access` in your OAuth 2.0 scope for the Authgear authorization server to return a refresh token.

#### Step 5: Logout

The Logout button on the `Protected.cshtml` page calls the `OnPostLogout()` method in ProtectedModel.cs. The method will delete the current user session and redirect to Authgear's end session endpoint for the user to complete the logout process.

The code sample below shows the implementation of the `OnPostLogout()` method:

```csharp
public async Task OnPostLogout()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
    }
```

#### Step 6: Set up and run the application

Start by cloning the project into your local machine:

```bash
git clone 
```

Make the project directory your current working directory:

```bash
cd authgear-example-dotnet
```

Update the following configuration variables in the `appsettings.json` file with your Authgear app settings values from **Part1** such as `Issuer`, `ClientId`, `ClientSecret`, and Authgear endpoint:

```bash
{
    "OpenIDConnect": {
        "ClientId": "{your-client-id}",
        "ClientSecret": "{your-client-secret}",
        "Issuer": "{your-authgear-app-endpoint}",
        "Scope": "openid offline_access",
        "PostLogoutRedirectUri": "<http://localhost:5002>",
        "TokenEndpoint": "{your-authgear-app-endpoint}/oauth2/token"
    },
    "Urls": "<http://localhost:5002>",
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    }
}
```

Execute the following command to run the [ASP.NET](http://asp.net/) Core web application:

```bash
dotnet build
dotnet run
```

You can now visit <http://localhost:5002> to access the application. When you click on the **"View Protected Data"** button, [ASP.NET](http://asp.net) Core takes you to the **Authgear’s Login page**.

<figure><img src="/files/PY6xDp2oNQqJI9HOdOzF" alt="" width="464"><figcaption></figcaption></figure>

Your users can log in to your application through a page hosted by Authgear, which provides them with a secure, standards-based login experience that you can customize with your own branding and various authentication methods, such as [social logins](https://www.authgear.com/features/social-login), [passwordless](https://www.authgear.com/features/passwordless-authentication), [biometrics logins](https://www.authgear.com/features/biometric-authentication), [one-time-password (OTP)](https://www.authgear.com/features/whatsapp-otp) with SMS/WhatsApp, and multi-factor authentication (MFA).

<figure><img src="/files/b5pZjwCdra8wLnXUz3pe" alt="" width="464"><figcaption></figcaption></figure>

After you have authenticated, a protected view is rendered. The application receives an Access token that it uses to present user data on the screen, and tokens that could be used in upstream requests to some backend API, to access data on behalf of the user.

<figure><img src="/files/RiyFtwOaUaYNC4dAJ4FP" alt=""><figcaption></figcaption></figure>

#### Next steps

This guide showed how to quickly implement an end-to-end OpenID Connect flow in .NET with Authgear. Only simple code is needed, after which protected views are secured with built-in UI login pages.


# Laravel

Authentication for Laravel websites with Authgear and OAuth2

In this guide, you'll learn how to add user authentication to a Laravel app using Authgear as an OIDC provider.

Authgear supports multiple ways to allow users to log in to apps such as passwordless sign-in, phone OTP, and 2FA. In this post, we'll show you how to enable all these options in your Laravel app without worrying about the underlying logic.

This guide targets **Laravel 12** and **PHP 8.2+**.

### What You Will Learn

* How to create an Authgear Application.
* How to request an OAuth 2.0 authorization code from Authgear.
* How to get user info from Authgear using an OAuth 2.0 access token.
* How to replace Breeze's local password login with Authgear, linking users by their Authgear subject (`sub`).

### Prerequisites

To follow along with the example, you should have the following in place:

* A free Authgear account. [Sign up](https://accounts.portal.authgear.com/signup) if you don't have an account yet.
* PHP 8.2 or later, Composer 2, and Node.js 18 or later.

You can also clone the finished app from the [Laravel Example GitHub repo](https://github.com/authgear/authgear-example-laravel) and follow along.

#### What We Will Build

The example app we'll build uses Laravel Breeze for its UI scaffolding — the Blade layout, dashboard, and profile pages. Authgear is the only identity provider. We remove Breeze's local register, login, password-reset, and email-verification flows, so users sign in through Authgear and nothing else.

By handing authentication to Authgear, you get passwordless sign-in, phone OTP, 2FA, and more without writing or maintaining that logic yourself.

<figure><img src="/files/98FH6Vs5UB21nXf43I4K" alt=""><figcaption></figcaption></figure>

### How to Add User Authentication to Laravel with Authgear as an OAuth Provider

In this section, we'll walk through the complete steps for building the example app.

#### Step 1: Configure Authgear Application

Before you can use Authgear as an OAuth identity provider, set up an application on the Authgear portal.

Log in to Authgear and select a project. Navigate to the **Applications** section for your project. Create a new application or configure an existing one with **OIDC Client Application** as the Application Type, as shown below:

<figure><img src="/files/R1tysMamZeUkLqFyo21Q" alt=""><figcaption></figcaption></figure>

Click **Save** to go to the application configuration page. This page reveals the application credentials and OAuth 2.0 endpoints.

<figure><img src="/files/cUu0uprD3s2IAVSI37Kw" alt=""><figcaption></figcaption></figure>

Note down the Client ID, Client Secret, and the endpoints. You'll use them later in your Laravel project.

#### Step 2: Add a Redirect URI

While you're still on the application configuration page, scroll down to the URL section and click **Add URI**. Enter `localhost:8000/oauth/callback` in the text field if you'll run your Laravel app on your local machine. Click **Save**.

Authgear redirects users to this URI after authorization, so it must point to a valid route in your Laravel app.

#### Step 3: Create a Laravel Project

Create a new Laravel project by running the following command. This installs Laravel 12:

```sh
composer create-project laravel/laravel authgear-laravel-example
```

Open the project folder in your editor. Delete `resources/views/welcome.blade.php` and create a new `resources/views/index.blade.php` with this content:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Authgear + Laravel Demo</title>
</head>
<body>
    <h1>Authgear + Laravel demo</h1>
    <p>This demo shows adding user authentication to a Laravel app with Authgear (OIDC / OAuth 2.0).</p>
    @if ($errors->any())
        <p style="color: #b00020;">{{ $errors->first() }}</p>
    @endif
    <p><a href="{{ route('login') }}">Login with Authgear</a></p>
</body>
</html>
```

Then point the root route at this view. Open `routes/web.php` and set:

```php
Route::get('/', function () {
    return view('index');
});
```

Run `php artisan serve` and open `localhost:8000` in a browser. You should see the landing page with a login link.

<figure><img src="/files/98FH6Vs5UB21nXf43I4K" alt=""><figcaption></figcaption></figure>

#### Step 4: Install Laravel Breeze

Breeze is the official starter kit for Laravel. Here we use it only for UI scaffolding — the Blade layout, dashboard, and profile pages — while Authgear handles authentication.

Install Breeze:

```sh
composer require laravel/breeze --dev
```

Run the installer to scaffold the resources:

```sh
php artisan breeze:install
```

During setup, select `blade` as the stack and leave the other options as default.

Next, add an `oauth_uid` column to the `users` table. This field stores a user's unique ID from Authgear after a successful login.

In Laravel 12, the users table is defined in `database/migrations/0001_01_01_000000_create_users_table.php` (this single file also creates the `sessions` and cache tables — there's no date-stamped users migration). Add the following line inside the `Schema::create('users', ...)` block:

```php
$table->string('oauth_uid')->nullable()->index();
```

Then add `oauth_uid` to the `$fillable` array in `app/Models/User.php` so the field can be mass-assigned:

```php
protected $fillable = [
    'name',
    'email',
    'oauth_uid',
    'password',
];
```

This example uses SQLite, so you don't need a database server. A fresh Laravel project already ships with `DB_CONNECTION=sqlite` in `.env`. Create the database file and run the migrations:

```bash
touch database/database.sqlite
php artisan migrate
```

#### Step 5: Add the Authgear Configuration

Keep your Authgear settings in a dedicated config file rather than calling `env()` from your controllers. This is what lets `php artisan config:cache` work in production — once the config is cached, `env()` returns `null` outside config files.

Create `config/authgear.php` with the following content:

```php
<?php

return [
    // Your Authgear project endpoint, e.g. https://my-project.authgear.cloud
    'project_url' => env('AUTHGEAR_PROJECT_URL', ''),

    'client_id' => env('AUTHGEAR_APP_CLIENT_ID', ''),
    'client_secret' => env('AUTHGEAR_APP_CLIENT_SECRET', ''),
    'redirect_uri' => env('AUTHGEAR_APP_REDIRECT_URI', ''),

    // OAuth 2.0 / OIDC scopes requested during authorization.
    'scopes' => env('AUTHGEAR_SCOPES', 'openid email profile'),

    // OIDC endpoints derived from the project URL.
    'authorize_endpoint' => env('AUTHGEAR_PROJECT_URL', '').'/oauth2/authorize',
    'token_endpoint' => env('AUTHGEAR_PROJECT_URL', '').'/oauth2/token',
    'userinfo_endpoint' => env('AUTHGEAR_PROJECT_URL', '').'/oauth2/userInfo',
    'end_session_endpoint' => env('AUTHGEAR_PROJECT_URL', '').'/oauth2/end_session',
];
```

Add your Authgear application's credentials to your project's `.env` file:

```
AUTHGEAR_PROJECT_URL=
AUTHGEAR_APP_CLIENT_ID=
AUTHGEAR_APP_CLIENT_SECRET=
AUTHGEAR_APP_REDIRECT_URI=http://localhost:8000/oauth/callback
```

{% hint style="info" %}
Your Authgear project URL is the hostname of any of your endpoint URLs. For a project with an authorization endpoint of `https://laravel-app.authgear.cloud/oauth2/authorize`, the project URL is `https://laravel-app.authgear.cloud`.
{% endhint %}

#### Step 6: Bind the OAuth Provider

We'll use the `league/oauth2-client` package to talk to Authgear's OAuth endpoints. Install it:

```sh
composer require league/oauth2-client
```

Bind a single configured `GenericProvider` in the service container so it can be injected into your controller. This keeps the OAuth setup in one place and makes the controller testable.

Open `app/Providers/AppServiceProvider.php` and add the binding to the `register()` method:

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use League\OAuth2\Client\Provider\GenericProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(GenericProvider::class, function () {
            return new GenericProvider([
                'clientId' => config('authgear.client_id'),
                'clientSecret' => config('authgear.client_secret'),
                'redirectUri' => config('authgear.redirect_uri'),
                'urlAuthorize' => config('authgear.authorize_endpoint'),
                'urlAccessToken' => config('authgear.token_endpoint'),
                'urlResourceOwnerDetails' => config('authgear.userinfo_endpoint'),
            ]);
        });
    }

    public function boot(): void
    {
        //
    }
}
```

#### Step 7: Send the OAuth Authorization Request

In this step, you'll create the route that redirects users from your app to Authgear's authorization page, where they grant your app access to their account. If you've signed in to a site using Google before, you've seen an authorization page like this.

Create the controller that handles all OAuth operations:

```sh
php artisan make:controller OAuthController
```

Open `app/Http/Controllers/OAuthController.php`. Inject the provider through the constructor and add the `startAuthorization()` method:

```php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\GenericProvider;

class OAuthController extends Controller
{
    public function __construct(private GenericProvider $provider)
    {
    }

    public function startAuthorization(Request $request): RedirectResponse
    {
        $authorizationUrl = $this->provider->getAuthorizationUrl([
            'scope' => config('authgear.scopes'),
        ]);

        // Persist the state value to validate it on the callback (CSRF protection).
        $request->session()->put('oauth2state', $this->provider->getState());

        return redirect()->away($authorizationUrl);
    }
}
```

`startAuthorization()` stores the OAuth `state` value in the session before redirecting. You'll check it on the callback to protect against CSRF.

Now register the routes. Open `routes/web.php` and replace its contents with:

```php
<?php

use App\Http\Controllers\OAuthController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('index');
});

// Authgear OAuth 2.0 / OIDC flow.
Route::get('/login', [OAuthController::class, 'startAuthorization'])->name('login');
Route::get('/oauth/callback', [OAuthController::class, 'handleRedirect']);
Route::post('/logout', [OAuthController::class, 'logout'])->name('logout');

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
```

The `/login` route is named `login` so Breeze's `auth` middleware redirects unauthenticated users to Authgear. You'll add the `handleRedirect()` and `logout()` methods in the next steps.

Because Authgear is now the only identity provider, you no longer need Breeze's local-auth routes. Delete `routes/auth.php` entirely — it defined the register, login, password-reset, and email-verification routes. The route file above already drops the `require __DIR__.'/auth.php';` line that Breeze added to `web.php`. You can also delete the matching controllers in `app/Http/Controllers/Auth/` and their Blade views if you want to keep the project tidy.

At this point, visiting `/login` should redirect to the Authgear authorization page.

<figure><img src="/files/4qKI6o9i3sRVUt2x2Jb5" alt=""><figcaption></figcaption></figure>

#### Step 8: Handle the Redirect

After a user authorizes your app, Authgear redirects back to `/oauth/callback` with an authorization code. The `handleRedirect()` method validates the request, exchanges the code for an access token, and fetches the user's info.

Add the `handleRedirect()` method to `OAuthController`:

```php
public function handleRedirect(Request $request): RedirectResponse
{
    $state = $request->query('state');
    $expectedState = $request->session()->pull('oauth2state');

    if (empty($state) || ! is_string($expectedState) || ! hash_equals($expectedState, $state)) {
        return redirect('/')->withErrors(['oauth' => 'Invalid OAuth state. Please try logging in again.']);
    }

    $code = $request->query('code');
    if (empty($code)) {
        return redirect('/')->withErrors(['oauth' => 'Authorization code missing.']);
    }

    try {
        $accessToken = $this->provider->getAccessToken('authorization_code', [
            'code' => $code,
        ]);

        $userInfo = $this->provider->getResourceOwner($accessToken)->toArray();
    } catch (IdentityProviderException $e) {
        report($e);

        return redirect('/')->withErrors(['oauth' => 'Failed to authenticate with Authgear.']);
    }

    if (empty($userInfo['sub'])) {
        return redirect('/')->withErrors(['oauth' => 'Authgear did not return a user identifier.']);
    }

    $user = $this->findOrCreateUser($userInfo);

    Auth::guard('web')->login($user);
    $request->session()->regenerate();

    return redirect()->intended('/dashboard');
}
```

A few things this method does that protect your app:

* **Validates `state`.** It compares the returned `state` against the value stored in Step 7 using `hash_equals`. A mismatch rejects the request, which blocks CSRF attacks against the callback.
* **Reads from the request, not `$_GET`.** Using `$request->query()` keeps the code consistent with the rest of Laravel and testable.
* **Fails gracefully.** Any error during the token exchange or userinfo call is reported and turned into a friendly redirect, instead of dumping an exception to the browser.

If you dump the `$userInfo` array (`dd($userInfo)`), you'll see the claims Authgear returns:

```json
[
  "custom_attributes" => []
  "email" => "users-email@gmail.com"
  "email_verified" => true
  "https://authgear.com/claims/user/can_reauthenticate" => true
  "https://authgear.com/claims/user/is_anonymous" => false
  "https://authgear.com/claims/user/is_verified" => true
  "sub" => "e1234323-f123-4b99-91d8-c2ca55a6a3dc"
  "updated_at" => 1683898685
]
```

The `sub` and `email_verified` claims drive the account-linking logic in the next step.

#### Step 9: Link the Authgear User to a Laravel Session

`handleRedirect()` calls `findOrCreateUser()` to map the Authgear user to a local Laravel user, so your app can start a normal authenticated session and guard protected routes.

Add the `findOrCreateUser()` method to `OAuthController`:

```php
private function findOrCreateUser(array $userInfo): User
{
    // Match on the stable Authgear subject identifier, never on email alone.
    $user = User::query()->where('oauth_uid', $userInfo['sub'])->first();

    if ($user) {
        return $user;
    }

    // Link to an existing local account by email ONLY when Authgear reports
    // the email as verified. Linking on an unverified email would allow
    // account takeover.
    if (! empty($userInfo['email']) && ($userInfo['email_verified'] ?? false) === true) {
        $existing = User::query()->where('email', $userInfo['email'])->first();

        if ($existing) {
            $existing->oauth_uid = $userInfo['sub'];
            $existing->save();

            return $existing;
        }
    }

    return User::create([
        'name' => $userInfo['email'] ?? $userInfo['sub'],
        'email' => $userInfo['email'] ?? null,
        'oauth_uid' => $userInfo['sub'],
        'password' => Hash::make(Str::random(40)),
    ]);
}
```

How this resolves a user:

1. **Match on `sub` first.** The Authgear subject identifier (`sub`) is stable and unique per user, so it's the reliable key. Email addresses can change or be reassigned.
2. **Link by email only when verified.** If no local user has this `sub` yet, link to an existing account by email — but only when `email_verified` is `true`. Linking on an unverified email would let an attacker claim someone else's account.
3. **Otherwise create a new user.** New users get a random local password they never use, since they always sign in through Authgear.

{% hint style="info" %}
This example doesn't store the access or refresh token in the session, and it doesn't request the `offline_access` scope. The scopes are `openid email profile`. The app reads the user's identity once at login and relies on the Laravel session from then on.
{% endhint %}

Find the complete `OAuthController` [here](https://github.com/authgear/authgear-example-laravel/blob/main/app/Http/Controllers/OAuthController.php).

Now run the app, open the landing page, and click the login link. You're redirected to the Authgear authorization page. After you authorize, Authgear sends you back to the callback route, and on success you land on the Breeze dashboard:

<figure><img src="/files/5pnu8I1YnsD9B6c0RM9x" alt=""><figcaption></figcaption></figure>

#### Step 10: Logout

To log a user out, clear the local Laravel session and then end the Authgear session so the user is fully signed out. Use the OIDC `end_session` endpoint rather than revoking a token — the app doesn't hold any tokens to revoke.

Add the `logout()` method to `OAuthController`:

```php
public function logout(Request $request): RedirectResponse
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();

    // If Authgear is configured, end its session too for a full sign-out.
    if (! empty(config('authgear.project_url'))) {
        return redirect()->away(config('authgear.end_session_endpoint'));
    }

    return redirect('/');
}
```

The `POST /logout` route is already wired up in `routes/web.php` from Step 7, so any Breeze logout button that posts to the `logout` route will trigger this method.

#### Verify It Works

The example repo ships a feature test in `tests/Feature/OAuthTest.php` that covers the state check, the code exchange, and account linking. Run the suite to confirm the flow behaves as expected:

```bash
php artisan test
```

### What's Next

Try enabling the different login methods on Authgear from the portal — 2FA, passwordless login, phone OTP, and more — without changing any code in your app.

The two hardening choices in this guide are worth keeping in any production integration: validating the OAuth `state` on the callback, and linking accounts by email only when Authgear reports the email as verified.

Find the complete code for the example app in the [Laravel Example GitHub repo](https://github.com/authgear/authgear-example-laravel).


# PHP

Authentication for PHP websites with Authgear and OAuth2

Using OAuth, you can use Authgear to add user authentication in a vanilla PHP application.

In this guide, we'll cover how to implement OAuth 2.0 login in a regular PHP web application with Authgear as the Identity Provider.

### What You Will Learn

At the end of this post, you'll learn the following:

* How to create an Authgear Application
* How to enable email and password sign-in
* How to sign in with Authgear from a PHP app
* How to request user info from Authgear
* How to use a refresh token
* And finally how to log users out and revoke access tokens.

### Prerequisites

To follow along, you'll need the following:

* PHP runtime (E.g XAMPP for testing offline on Windows devices)
* An Authgear account. [Sign up](https://accounts.portal.authgear.com/signup) for free if you don't have an account yet.
* [Composer](https://getcomposer.org/download/) (PHP package manager) installation
* Your preferred code editor (e.g VS Code).

#### What We Will Build

In this guide, we'll build a basic PHP application that lets a user sign in with their registered email and password.

The application will welcome the user with their email address after they sign in successfully. If the user is not signed in, the application will display links to Register or Login.

The following screenshot shows what the User Interface for the app will look like:

<figure><img src="/files/u3iKqqIFdxeAA46O1E4W" alt=""><figcaption></figcaption></figure>

### How to Add User Authentication to PHP with Authgear

Now let's dive into the actual steps of how to add Authgear to a PHP application.

### Part 1: Configure an Authgear Application

Under this section, we will cover the steps for configuring the Authgear application our PHP website will be connecting to. We'll do all these configurations in the Authgear Portal.

#### Step 1: Set up Authgear Application

The first step you need to take is to create a new application or configure an existing application on the Authgear [Portal](https://portal.authgear.com).

To do that, log in to the Authgear Portal, and select your project (or create a new one if you don't have any yet). From your project dashboard navigate to the **Applications** section and enter the details for your new application as shown below:

<figure><img src="/files/R1tysMamZeUkLqFyo21Q" alt=""><figcaption></figcaption></figure>

Once you're done, click on the Save button to continue. Then, click on Next to see the configuration page for your application.

<figure><img src="/files/cUu0uprD3s2IAVSI37Kw" alt=""><figcaption></figcaption></figure>

The application configuration page contains basic information like `Client ID` and `Client Secret` that we'll use later in this tutorial. Hence, try to note the values down.

In addition to basic information, you can find other configuration information including endpoints and Authorized Redirect URIs.

#### Step 2: Authorized Redirect URIs

The Authorized Redirect URIs section contains a link to the page you want Authgear to redirect users to after login.

Update the value for Authorized Redirect URIs to a page on your application. For our example PHP application the value will be `http://localhost` because we plan to test run it offline using XAMPP. Also, try to note this value down as we'll be using it in later steps.

### Part 2: Implement PHP Project

Here we will cover the steps for implementing a PHP website that interacts with Authgear using the Open ID Connect (OIDC) standard.

#### Step 1: Create a PHP Project

Create a new PHP project on your computer and add an `index.php` file to the root of the project folder.

Add the following code to index.php to create the User Interface of the example app.

```php
<?php
$appUrl = "REPLACE WITH YOUR AUTHGEAR PROJECT URL";
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PHP Demo - Home</title>
</head>
<body style="background-color: #DEDEDE">
    <div style="max-width: 650px; margin: 16px auto; background-color: #FFFFFF; padding: 16px;">
        <h3>Hello world!</h3>
        <p>This demo app shows you how to add user authentication to your app using Authgear</p>
        <p>Checkout <a href="https://docs.authgear.com">docs.authgear.com</a> to learn more about adding Authgear to your apps.</p>
        
        <p><a href="login.php">Login</a></p>
        <p><a href="logout.php">Logout</a></p>
    </div>
</body>
</html>
```

**Note**: The Login and Logout links in the above code currently point to login.php and logout.php respectively, we'll create both files later.

#### Step 2: Add Authgear Configuration to PHP Project

In this step, we'll add our Authgear application configuration to the PHP project.

We'll use the [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client) PHP package for the configuration.

Install the package manually from Github or via Composer by running the following command from your PHP project's root directory:

```sh
composer require league/oauth2-client
```

Next, after the package is installed, create a new `config.php` file in the PHP project folder. Add the following code to the file:

```php
<?php
require 'vendor/autoload.php';

$appUrl = "PLACE_YOUR_PROJECT_URL_HERE";
$clientID = "PLACE_YOUR_CLIENT_ID_HERE";
$clientSecret = "PLACE_YOUR_CLIENT_SECRET_HERE";
$redirectUri = "PLACE_YOUR_REDIRECT_URI_HERE";

$provider = new \League\OAuth2\Client\Provider\GenericProvider([
    'clientId'                => $clientID,    // The client ID assigned to you by the provider (authgear).
    'clientSecret'            => $clientSecret,    // The client secret is assigned to you by the provider.
    'redirectUri'             => $redirectUri, // The authorized Redirect URI you specified in your authgear app.
    'urlAuthorize'            => $appUrl.'/oauth2/authorize',
    'urlAccessToken'          => $appUrl.'/oauth2/token',
    'urlResourceOwnerDetails' => $appUrl.'/oauth2/userInfo',
    'scopes' => 'openid offline_access'
]);
?>
```

**Note**: Replace the values for `clientId`, `clientSecret`, `redirectUri` with corresponding values from the Authgear application you created in Step 1.

Including the `offline_access` scope is required to get a refresh token from Authgear.

#### Step 3: Add Login Authorization

The flow for Login on our app is as follows:

1. The user clicks on the Login button
2. User is redirected to the Authgear authorization page where they can sign in using email and password or any other sign-in methods you have enabled for your Authgear project.
3. The user is redirected back to your website with an authorization code.

In order to implement the above, you need to create a `login.php` file in your project's root directory. Add the following code to the login.php file:

```php
<?php
include "config.php";

if (!isset($_GET['code'])) {
    // Fetch the authorization URL from the provider; this returns the
    // urlAuthorize option and generates and applies any necessary parameters
    // (e.g. state).
    $authorizationUrl = $provider->getAuthorizationUrl();

    // Redirect the user to the authorization URL.
    header('Location: ' . $authorizationUrl);
    exit;
}
?>
```

At this point, if you try running the example app in a browser and click the Login link in index.php, your app should redirect to the Authgear login page. If you sign in successfully, you should be redirected back to the redirect URL you specified earlier in your project configuration.

<figure><img src="/files/4qKI6o9i3sRVUt2x2Jb5" alt=""><figcaption></figcaption></figure>

Authgear will redirect to your Authorized Redirect URI with extra parameters like `code` or an error message in the URL. The value for the code parameter is your authorization code. In the next step, we'll use the authorization code to generate an access token.

#### Step 4: Get Access Token and Request User Info

Usually, after successful sign-in, you'll want to start using the current user's info to offer custom experience in your app.

In this step, we'll use the PHP OAuth 2.0 Client once more to interact with our Authgear app.

First, open `index.php` and search for the line with the following code:

```php
$appUrl = "REPLACE WITH YOUR AUTHGEAR PROJECT URL";
```

Replace the above line with this code:

```php
include "config.php";
session_start();

// if code is set, get access token
if (isset($_GET['code'])) {
    $code = $_GET['code'];

    try {
        $accessToken = $provider->getAccessToken('authorization_code', [
            'code' => $code
        ]);
        $_SESSION['accessToken'] = $accessToken;
        $_SESSION['refreshToken'] = $accessToken->getRefreshToken(); //store refresh token so that you can use it later
    } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
        // Failed to get the access token or user details.
        exit($e->getMessage());
    }
}
```

The above code exchanges the authorization code returned in the redirect for an access token. It then stores the access token in the PHP session so that we can use this token in future requests to protected resources.

Now that we have the access token, let's try to get the current user's details. To do that, update the HTML part in index.php like this:

```php
<div style="max-width: 650px; margin: 16px auto; background-color: #FFFFFF; padding: 16px;">
        <h3>Hello world!</h3>
        <p>This demo app shows you how to add user authentication to your app using Authgear</p>
        <p>Checkout <a href="https://docs.authgear.com">docs.authgear.com</a> to learn more about adding Authgear to your apps.</p>
        <?php
        if (isset($_SESSION['accessToken'])) {

            //if access token exists in session, attempt to fetch user info
            $storedAccessToken = $_SESSION['accessToken'];
            $resourceOwner = $provider->getResourceOwner($storedAccessToken);
            $userInfo = $resourceOwner->toArray();

            echo "Welcome back " . $userInfo['email'];
            echo "<br/>";
            echo '<a href="">Logout</a>';
            
        } else { ?>
        <p><a href="login.php">Login</a></p>
        
        <?php
        } 
        ?>
    </div>
```

Now test the app on your browser again and you should get the following page after login:

<figure><img src="/files/cztLQvW40aloqgJNOuvk" alt=""><figcaption></figcaption></figure>

We've successfully added user authentication to our PHP app using Authgear as the identity provider. The above page displays a welcome message with the email address the user registered with on your Authgear project. You can display other info about the user from the value of `$userInfo` variable.

#### Step 5: Getting and Using a Refresh Token

In OAuth 2.0, a refresh token is a key that's usually included in the response from the token endpoint when a client application exchanges the authorization code for an access token.

Access tokens expire after some time. Hence, we can use this refresh token to request a new access token without requiring our application users to log in again. In this step, we'll show you how to use the refresh token.

In the last step, we stored the value for the refresh token in the `$_SESSION['refreshToken']` variable. So, to get the refresh token, simply read the value from that variable.

Now, add the following code to index.php to read and use the refresh token to get a new access token:

First, find the line with the following code:

```php
$resourceOwner = $provider->getResourceOwner($storedAccessToken);
```

Replace that line with the following blocks of code:

```php
if ($storedAccessToken->hasExpired()) {              
    $newAccesstoken = $provider->getAccessToken('refresh_token', [
        'refresh_token' => $_SESSION['refreshToken']
    ]);

    $_SESSION['accessToken'] = $newAccesstoken;
    $resourceOwner = $provider->getResourceOwner($newAccesstoken);
} else {
    $resourceOwner = $provider->getResourceOwner($storedAccessToken);
}
```

**Note:** It is required to include **offline\_access** in your OAuth 2.0 scopes to get a refresh token from Authgear.

#### Step 6: Logout

Authgear provides a token revoke endpoint that you can use to revoke a refresh token and all the access associated with it.

To use the token revoke endpoint to log users out of your application, create a new `logout.php` file in your project directory then add the following code to the file:

```php
<?php 
include "config.php";
session_start();
if (isset($_SESSION['accessToken'])) {
    $options = [];
    $options['headers']['content-type'] = 'application/x-www-form-urlencoded';
    $options['body'] = http_build_query(['token'=>$_SESSION['refreshToken']]);
    $request = $provider->getRequest(
        'POST',
        $appUrl. '/oauth2/revoke',
        $options
    );
    $provider->getResponse($request);

    $_SESSION['accessToken'] = null;
    $_SESSION['refreshToken'] = null;
}
```

The above code will revoke your refresh token and delete all the session variables.

### Summary

In this post, we covered how to get started with adding Authgear to a regular web app built with PHP and no framework.

We also tried out an example of using the Authgear authorization code to retrieve an access token, then we used the token to access the user info endpoint.

Here's a link complete source code for [our example app on Github](https://github.com/authgear/authgear-example-php).

There's so much more you can do with Authgear and you can continue learning by checking out more topics on the [documentation page](https://docs.authgear.com/get-started).


# Backend/API Integration

An API or service protected by Authgear

If your API or backend service needs authentication, you can validate the session in your application server code. Each request from the client to your application server should contain an access token or a cookie. Your backend server should validate them for each HTTP request.

There are different approaches to verify the requests based on whether you validate JWT (JSON Web Tokens) in your server, or forward authentication to Authgear Resolver Endpoint.

|                           | Validate JSON Web Token (JWT) in your application server                                                                                                                         | Forward Authentication to Authgear Resolver Endpoint                                                                   |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Reliability               | <p><strong>Medium</strong><br>JWT only updates when expire. That means before the token expires, your application may see the user as valid even if they have been disabled.</p> | <p><strong>High</strong><br>Update near real-time, based on your reserve proxy cache setting</p>                       |
| Integration difficulties  | <p><strong>Easy</strong><br>You only need to add code in your application to validate and decode JWT</p>                                                                         | <p><strong>Medium</strong><br>Need to setup extra reverse proxy to resolve authentication information</p>              |
| Transportation of session | **Access Token** in `Authorization` header                                                                                                                                       | <p><strong>Session ID</strong> in Cookies or<br><strong>Access Token</strong> in <code>Authorization</code> header</p> |

## Simple: Validate JWT in your server

Authgear uses [JSON Web Token (JWT)](https://jwt.io/?_gl=1*1ybgym6*rollup_ga*MTI1NDM1NjUwMy4xNjg3NzEyNTIz*rollup_ga_F1G3E656YZ*MTY5MTEzNjEzNS45NS4xLjE2OTExMzYxNDguNDcuMC4w*_ga*MTI1NDM1NjUwMy4xNjg3NzEyNTIz*_ga_QKMSDV5369*MTY5MTEzNjEzNS44Ny4xLjE2OTExMzYxNDguNDcuMC4w&_ga=2.165043391.1472871049.1691063710-1254356503.1687712523) for secure data transmission, authentication, and authorization.

```mermaid
sequenceDiagram
autonumber
participant app as Mobile/Single Page Application
app->>Authgear: User authenticates with Authgear
Authgear-->>app: Access token & refresh token
participant server as Your app server
app->>server: User sends request with access token
server->>server: Verify request
server -->> app: Server responds with requested information
```

Authgear returns the `access token` and `refresh token` to the client app after authentication. Your client app should call the backend with the access token in the Authorization header. The tokens should be parsed and validated in the backend server to ensure they are not compromised and the signature is authentic.

Request example:

```bash
> GET /api_path HTTP/1.1
> Host: yourdomain.com
> Authorization: Bearer <AUTHGEAR_ACCESS_TOKEN_IN_JWT>
```

Read more on [Validate JWT in your application server](/get-started/backend-api/jwt) guide.

{% content-ref url="/pages/NBU5LasbVvo4StH1QZjP" %}
[Validate JWT in your backend](/get-started/backend-api/jwt)
{% endcontent-ref %}

## Advanced: Forward Authentication to Authgear Resolver Endpoint

Forward Authentication is a process where an intermediate **reverse** **proxy or API Gateway** is responsible for authenticating a request before it reaches the intended application or service. This can add an extra layer of security and centralize the authentication logic. An intermediate service forwards each incoming HTTP request to the Authgear Resolver Endpoint to verify the access token or cookie in the HTTP header.

Read more on [Forward Authentication to Authgear Resolver Endpoint](/get-started/backend-api/nginx) guide.

### Forward Access Token in Authorization Header

```mermaid
sequenceDiagram
  autonumber
  Browser ->> Authgear: User authenticate with Authgear
  Authgear -->> Browser: Access token and refresh token
  Browser ->> Reverse Proxy: Request with access token
  participant Resolver as Authgear Resolver Endpoint
  rect rgb(191, 223, 255)
  note left of Resolver: Verify access token
  Reverse Proxy ->> Resolver: Forward request authorization header
  Resolver -->> Reverse Proxy: Response with "x-authgear-headers"
  end
  participant App as Your app server
  Reverse Proxy ->> App: Pass request with updated headers
  App -->> Reverse Proxy: Response
  Reverse Proxy -->> Browser: Response
  
```

Instead of validating the access token in the backend, a reverse proxy forwards the request to an [Authgear Resolver Endpoint](/get-started/backend-api/nginx#authgear-resolver-endpoint). This endpoint resolves and verifies the access token in the **Authorization Header** of the request.

### Forward Cookie in HTTP header

```mermaid
sequenceDiagram
  autonumber
  Browser ->> Authgear: User authenticate with Authgear
  Authgear -->> Browser: Authgear sets cookies
  Browser ->> Reverse Proxy: Request with cookies
  participant Resolver as Authgear Resolver Endpoint
  rect rgb(191, 223, 255)
  note left of Resolver: Verify cookie
  Reverse Proxy ->> Resolver: Forward request cookie header
  Resolver -->> Reverse Proxy: Response with "x-authgear-headers"
  end
  participant App as Your app server
  Reverse Proxy ->> App: Pass request with updated headers
  App -->> Reverse Proxy: Response
  Reverse Proxy -->> Browser: Response
  
```

In this approach, instead of validating the token in authorization header, Authgear returns `Set-Cookie` headers and sets cookies to the browser. The cookies are HTTP only and share under the same root domains. So you will need to setup the **custom domain** for Authgear, such as `identity.yourdomain.com`.

If you have multiple applications under `yourdomain.com`, all applications would share the same session cookie automatically. After that, you can verify the cookies using the [Resolver Endpoint](/get-started/backend-api/nginx).

Request example:

```javascript
> GET /api_path HTTP/1.1
> Host: yourdomain.com
> cookie: session=<AUTHGEAR_SESSION_ID>
```


# Client SDK to make authorized API calls to backend

How to make authorized request to your application server after login with Authgear

## Using Authgear SDK to call your application server

In this section, we are going to explain how to make an authorized request to your backend API by using Authgear SDK. Authgear SDKs make it easy to refresh access token if needed and maintain session state.

If you are using **Cookie-based authentication** in your web application, you can skip this section as session cookies are handled by the browser automatically.

## Overview

1. To determine which user is calling your server, you will need to include the Authorization header in every request that send to your application server.
2. On your Backend/API, you will need to set up [Backend integration](/get-started/backend-api). Authgear will help you to handle the Authorization header to determine whether the incoming HTTP request is authenticated or not.

In the below section, we will explain how to set up SDK to for the purpose of making authorized API calls to your backend.

## SDK Setup

Configure the Authgear SDK with the Authgear endpoint and client id. The SDK must be properly configured before use by calling `configure`. No network call will be triggered during `configure`.

{% tabs %}
{% tab title="Javascript" %}

```javascript
authgear
    .configure({
        clientID: "<YOUR_APPLICATION_CLIENT_ID>",
        endpoint: "<YOUR_AUTHGEAR_ENDPOINT>",
    })
    .then(() => {
        // configured successfully
    })
    .catch((e) => {
        // failed to configured
    });
```

{% endtab %}

{% tab title="iOS" %}

```swift
let authgear = Authgear(clientId: clientId, endpoint: endpoint)
authgear.configure() { result in
    switch result {
    case .success():
        // configured successfully
    case let .failure(error):
        // failed to configured
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
ConfigureOptions configureOptions = new ConfigureOptions();
Authgear authgear = new Authgear(getApplication(), clientID, endpoint);
authgear.configure(configureOptions, new OnConfigureListener() {
    @Override
    public void onConfigured() {
        // configured successfully
    }

    @Override
    public void onConfigurationFailed(@NonNull Throwable throwable) {
        // failed to configured
    }
});
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
var authgearOptions = new AuthgearOptions
{
    ClientId = "<YOUR_APPLICATION_CLIENT_ID>",
    AuthgearEndpoint: "<YOUR_AUTHGEAR_ENDPOINT>",
};
#if __ANDROID__
var authgear = new AuthgearSdk(GetActivity().ApplicationContext, authgearOptions);
#else
#if __IOS__
var authgear = new AuthgearSdk(UIKit.UIApplication.SharedApplication, authgearOptions);
#endif
#endif
await authgear.ConfigureAsync();
```

{% endtab %}
{% endtabs %}

## UserInfo and Session State

`sessionState` reflect the user logged in state. However the session state is cached locally and only updated after each server call.

Usually right after login/signup via `authorize`,You will call `fetchUserInfo` as soon as possible with `authgear.sessionState` became `AUTHENTICATED`

{% tabs %}
{% tab title="Javascript" %}

```javascript
// value can be NO_SESSION or AUTHENTICATED
// After authgear.configure, it only reflect SDK local state.
let sessionState = authgear.sessionState;

if (sessionState === "AUTHENTICATED") {
    authgear
        .fetchUserInfo()
        .then((userInfo) => {
            // sessionState is now up to date
            // read the userInfo if needed
        })
        .catch((e) => {
            // sessionState is now up to date
            // it will change to NO_SESSION if the session is invalid
        });
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
// value can be .noSession or .authenticated.
// After authgear.configure, it only reflect SDK local state.
let sessionState = authgear.sessionState

// call fetchUserInfo to see if the session is valid
if authgear.sessionState == .authenticated {
    authgear.fetchUserInfo { userInfoResult in
        // sessionState is now up to date
        // it will change to .noSession if the session is invalid
        let sessionState = authgear.sessionState

        switch userInfoResult {
        case let .success(userInfo):
            // read the userInfo if needed
        case let .failure(error):
            // failed to fetch user info
            // the refresh token maybe expired or revoked
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
// value can be NO_SESSION or AUTHENTICATED
// After authgear.configure, it only reflect SDK local state.
SessionState sessionState = authgear.getSessionState();

if (sessionState == SessionState.AUTHENTICATED) {
    authgear.fetchUserInfo(new OnFetchUserInfoListener() {
        @Override
        public void onFetchedUserInfo(@NonNull UserInfo userInfo) {
            // sessionState is now up to date
            // read the userInfo if needed
        }

        @Override
        public void onFetchingUserInfoFailed(@NonNull Throwable throwable) {
            // sessionState is now up to date
            // it will change to NO_SESSION if the session is invalid
        }
    });
}
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
// value can be NoSession or Authenticated
// After Authgear.ConfigureAsync, it only reflects local state.
var sessionState = authgear.SessionState;

if (sessionState == SessionState.Authenticated)
{
    try
    {
        var userInfo = await authgear.FetchUserInfoAsync();
        // sessionState is now up to date
    }
    catch (Exception ex)
    {
        // sessionState is now up to date
        // it will change to NoSession if the session is invalid
    }
}
```

{% endtab %}
{% endtabs %}

## Makeing an API call

When you make a API call to Backend API, you will need to include the access token in the `Authorization` header. Access token is also short lived and need to be regularly rotated by Refresh token for security purpose. Authgear SDKs provide the following functions to simplify both steps.

### **The `fetch` function (JavaScript Only)**

**Javascript Only**. Authgear SDK provides `fetch` function for you to call your application server. The `fetch` function will include Authorization header in your application request, and handle refresh access token automatically. `authgear.fetch` implement [fetch](https://fetch.spec.whatwg.org/).

If you are using another networking library, you will need to use `refreshAccessTokenIfNeeded().` and include the `Authorization` header yourself, as described in the next paragraph.

{% tabs %}
{% tab title="Javascript" %}

```javascript
authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));
```

{% endtab %}
{% endtabs %}

### The refreshAccessTokenIfNeeded function

You will need to include the Authorization header in your application request. Call `refreshAccessTokenIfNeeded` every time before using the access token, the function will check and make the network call only if the access token has expired. Include the access token into the Authorization header of your application request.

{% tabs %}
{% tab title="Javascript" %}

```java
authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear.refreshAccessTokenIfNeeded() { result in
    switch result {
    case .success():
        // access token is ready to use
        // accessToken can be empty
        // it will be empty if user is not logged in or session is invalid

        // include Authorization header in your application request
        if let accessToken = authgear.accessToken {
            // example only, you can use your own networking library
            var urlRequest = URLRequest(url: "YOUR_SERVER_URL")
            urlRequest.setValue(
                "Bearer \(accessToken)", forHTTPHeaderField: "authorization")
            // ... continue making your request
        }
    case let .failure(error):
        // failed to refresh access token
        // the refresh token maybe expired or revoked
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
// Suppose we are preparing an http request in a background thread.

// Setting up the request, e.g. preparing a URLConnection

try {
    authgear.refreshAccessTokenIfNeededSync();
} catch (OauthException e) {
    // failed to refresh access token
    // the refresh token maybe expired or revoked
}
// access token is ready to use
// accessToken can be string or undefined
// it will be empty if user is not logged in or session is invalid
String accessToken = authgear.getAccessToken();
HashMap<String, String> headers = new HashMap<>();
headers.put("authorization", "Bearer " + accessToken);

// Submit the request with the headers...
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
try
{
    await authgear.RefreshAccessTokenIfNeededAsync();
}
catch (OauthException ex)
{
    // failed to refresh access token
    // the refresh token maybe expired or revoked
}
// access token is ready to use
// accessToken can be string or undefined
// it will be empty if user is not logged in or session is invalid
var accessToken = authgear.AccessToken;
var client = GetHttpClient();  // Get the re-used http client of your app, as per recommendation.
var httpRequestMessage = new HttpRequestMessage(myHttpMethod, myUrl);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Send the request with the headers...
```

{% endtab %}
{% endtabs %}

## Handle revoked sessions

If the session is revoked from the management portal, the client will call your Backend API with an invalid access token. Your application server can check that by looking at the [resolver headers](/get-started/backend-api/nginx).

For example, you application may return HTTP status code 401 for unauthorized requests. Depending on your application flow, you may want to show your user login page again or reset the SDK `sessionState` to `NO_SESSION` locally. To clear the `sessionState`, you can use `clearSessionState` function.

{% tabs %}
{% tab title="Javascript" %}

```javascript
// example only
// if your application server return HTTP status code 401 for unauthorized request
async function fetchAppServer() {
    var response = await authgear.fetch("YOUR_SERVER_URL");
    if (response.status === 401) {

        // if you want to clear the session state locally, call clearSessionState
        // `authgear.sessionState` will become `NO_SESSION` after calling
        await authgear.clearSessionState();
        throw new Error("user session invalid");
    }
    // ...
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
// example only
// if your application server return HTTP status code 401 for unauthorized request
if let response = response as? HTTPURLResponse {
    if response.statusCode == 401 {

        // if you want to clear the session state locally, call clearSessionState
        // `authgear.sessionState` will become `.noSession` after calling
        authgear.clearSessionState { result in
            switch result {
            case .success():
                // clear SDK session state locally
                // `authgear.sessionState` becomes `.noSession`
            case let .failure(error):
                // failed to clear session state
            }
        }
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
// example only
// if your application server return HTTP status code 401 for unauthorized request
responseCode = httpConn.getResponseCode();
if (responseCode == HttpURLConnection.Unauthorized) {

    // if you want to clear the session state locally, call clearSessionState
    // `authgear.getSessionState()` will become `NO_SESSION` after calling
    authgear.clearSessionState();
}
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
// example only
// if your application server return HTTP status code 401 for unauthorized request
statusCode = httpResponseMessage.StatusCode;
if (statusCode == HttpStatusCode.Unauthorized)
{
    // if you want to clear the session state locally, call ClearSessionState
    // `authgear.SessionState` will become `NoSession` after calling
    authgear.ClearSessionState();
}
```

{% endtab %}
{% endtabs %}


# Validate JWT in your backend

Authenticate the incoming HTTP requests by validating JWT in your application server

In this section, we will go through how to decode the JWT token to obtain the currently logged-in user.

{% hint style="info" %}
Building with **NestJS**? The [`@authgear/nestjs`](/get-started/backend-api/nestjs) SDK provides a ready-made module, guard, and decorators so you don't have to wire up JWT validation by hand.
{% endhint %}

Before we start, make sure the option **Issue JWT as access token** is enabled in your Application settings in the Portal.

<figure><img src="/files/g7Z4BSG3uvianJtcdiEz" alt="" width="375"><figcaption><p>Enable this option in application settings in the portal</p></figcaption></figure>

With the **Issue JWT as access token** option turned on in your application, Authgear will issue JWT as access tokens. The incoming HTTP requests should include the access token in their `Authorization` headers. Without setting the reverse proxy, your backend server can use your Authgear **JWKS** to verify the request and decode user information from the JWT access token.

## Payload of the JWT access token

See the claims in the access token in this reference: [JWT Access Token](/reference/tokens/jwt-access-token). Learn more about [Add custom fields to a JWT Access Token or ID Token](/integration/add-custom-fields-to-a-jwt-access-token) for adding claims into the JWT.

## Find the JSON Web Key Sets (JWKS) endpoint

This Discovery endpoint serves as a JSON document containing the OpenID Connect configuration of your app. It includes the authorization endpoint, the token endpoint, and the JWKS endpoint.

`https://<YOUR_AUTHGEAR_ENDPOINT>/.well-known/openid-configuration`

The JSON Web Key Sets (JWKS) endpoint can be found in `jwks_uri` in the configuration.

**OpenID Connect Configuration JSON Example**

Here is [an example of how it looks](https://accounts.portal.authgear.com/.well-known/openid-configuration).

```json
{
    "issuer": "https://project-id.authgear.cloud",
    "authorization_endpoint": "https://project-id.authgear.cloud/oauth2/authorize",
    "jwks_uri": "https://project-id.authgear.cloud/oauth2/jwks", // the JWKS endpoint
    ...
}
```

## Decode user from an access token

Follow this step-by-step example to verify and decode the JWT token.

{% tabs %}
{% tab title="Python" %}
**Step 1: Install packages**

```bash
pip install cryptography
pip install PyJWT
```

**Step 2: Find the JSON Web Key Sets (JWKS) endpoint**

Define a function to find the JWKS endpoint from the OpenID Connect configuration. Use your Authgear endpoint as the `base_address`

```python
import json
from contextlib import closing
from urllib.request import urlopen

base_address = "https://<your_app_endpoint>"

def fetch_jwks_uri(base_address):
    doc_url = base_address + "/.well-known/openid-configuration"
    with closing(urlopen(doc_url)) as f:
        doc = json.load(f)
    jwks_uri = doc["jwks_uri"]
    if not jwks_uri:
        raise Exception('Failed to fetch jwks uri.')
    return jwks_uri
```

**Step 3: Get the JWT token from the Authorization header**

Define a function to extract the access token from the Authorization header in the incoming request. It should look like `Authorization: Bearer <access_token>`.

```python
def parse_header(authz_header):
    parts = authz_header.split(" ")
    if len(parts) != 2:
        return

    scheme = parts[0]
    if scheme.lower() != "bearer":
        return

    return parts[1]
```

**Step 4: Verify and decode the JWT token**

Here we show an example of using the Flask web framework to guard a path. You may need to adjust some of the codes to suit your technologies.

```python
from flask import request
import jwt
from jwt import PyJWKClient

@app.route("/hello")
def hello():
    authz_header = request.headers.get("Authorization")
    if not authz_header:
        return {
            "message": "authz header not found"
        }

    # get jwt token from Authorization header
    token = parse_header(authz_header)
    if token:
        try:
            # fetch jwks_uri from the Authgear Discovery Endpoint
            jwks_uri = fetch_jwks_uri(base_address)
            # Reuse PyJWKClient for better performance
            jwks_client = PyJWKClient(jwks_uri)
            signing_key = jwks_client.get_signing_key_from_jwt(token)
            user_data = jwt.decode(
                token,
                signing_key.key,
                algorithms=["RS256"],
                audience=base_address,
                options={"verify_exp": True},
            )
            return {
                "message": "Hello!",
                "user_data": user_data
            }
        except:
            return {
                "message": "JWT decode failed"
            }
    else:
        return {
            "message": "no token"
        }
```

{% endtab %}

{% tab title="Node.js" %}
**Step 1: Install dependencies**

```bash
npm install --save axios jwks-rsa jsonwebtoken
```

**Step 2: Find the JWKS Endpoint**

Use the following method to get the JWKS URI (you'll need to URI to extract the public signing key from a JWT).

```javascript
const appUrl = ""; //place your authgear app endpoint here
const getJwksUri = async (appUrl) => {
    const config_endpoint = appUrl + "/.well-known/openid-configuration";
    const data = await axios.get(config_endpoint);
    return data.data.jwks_uri;
}
```

**Step 3: Extract JWT from Request Header**

Use the following code to extract only the token part from a `Bearer [token]` authorization header in your Express app:

```javascript
const express = require("express");
const axios = require("axios");
const node_jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

const app = express();
const port = 3002;
app.get('/', async (req, res) => {

    const requestHeader = req.headers;
    if (requestHeader.authorization == undefined) {
        res.send("Invalid header");
        return;
    }
    const authorizationHeader = requestHeader.authorization.split(" ");
    const access_token = authorizationHeader[1];

}
```

**Step 4: Decode Access Token**

Next, decode the access token so that you can extract the JWT `kid` from the result. You'll need this \`kid to get the public signing key. Use the following code to decode the JWT:

```javascript
const decoded_access_token = node_jwt.decode(access_token, {complete: true});
```

**Step 5: Get JWT Signing Keys and Verify the JWT**

Use the following code to extract the JWT public keys then verify the JWT using the keys:

```javascript
const jwks_uri = await getJwksUri(appUrl);
    const client = jwksClient({
        strictSsl: true,
        jwksUri: jwks_uri
    });
    const signing_key = await client.getSigningKey(decoded_access_token.header.kid);

    try {
        const verify = node_jwt.verify(access_token, signing_key.publicKey, { algorithms: ['RS256'] });
        res.send(JSON.stringify(verify))
    }
    catch(error) {
        res.send(error);  
    }
    
```

Here's what your Express app should look like after putting the code in all the steps together:

```javascript
const express = require("express");
const axios = require("axios");
const node_jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

const app = express();
const port = 3002;

const appUrl = "https://demo-1-ea.authgear.cloud";
const getJwksUri = async (appUrl) => {
    const config_endpoint = appUrl + "/.well-known/openid-configuration";
    const data = await axios.get(config_endpoint);
    return data.data.jwks_uri;
}

app.get('/', async (req, res) => {

    const requestHeader = req.headers;
    if (requestHeader.authorization == undefined) {
        res.send("Invalid header");
        return;
    }
    const authorizationHeader = requestHeader.authorization.split(" ");
    const access_token = authorizationHeader[1];
    const decoded_access_token = node_jwt.decode(access_token, {complete: true});
    const jwks_uri = await getJwksUri(appUrl);
    const client = jwksClient({
        strictSsl: true,
        jwksUri: jwks_uri
    });
    const signing_key = await client.getSigningKey(decoded_access_token.header.kid);

    try {
        const verify = node_jwt.verify(access_token, signing_key.publicKey, { algorithms: ['RS256'] });
        res.send(JSON.stringify(verify))
    }
    catch(error) {
        res.send(error);  
    }
});

app.listen(port, () => {
    console.log(`server started on port ${port}`);
});
```

{% endtab %}

{% tab title="Go" %}
Use your Authgear endpoint as `base_address`

```go
import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "time"

    "github.com/lestrrat-go/jwx/v3/jwk"
    "github.com/lestrrat-go/jwx/v3/jwt"
)

var (
    baseAddress = "https://<your_app_endpoint>"
)

type OIDCDiscoveryDocument struct {
    JWKSURI string `json:"jwks_uri"`
}

func FetchOIDCDiscoveryDocument(endpoint string) (*OIDCDiscoveryDocument, error) {
    resp, err := http.DefaultClient.Get(endpoint)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf(
            "failed to fetch discovery document: unexpected status code: %d",
            resp.StatusCode,
        )
    }

    var document OIDCDiscoveryDocument
    err = json.NewDecoder(resp.Body).Decode(&document)
    if err != nil {
        return nil, err
    }
    return &document, nil
}

func FetchJWK(baseAddress string) (jwk.Set, error) {
    doc, err := FetchOIDCDiscoveryDocument(
        baseAddress + "/.well-known/openid-configuration",
    )
    if err != nil {
        return nil, err
    }

    set, err := jwk.Fetch(context.Background(), doc.JWKSURI)
    return set, err
}

// DecodeUser parse request Authorization header and obtain user id and claims
func DecodeUser(r *http.Request) (string, bool, bool, error) {
    // fetch jwks_uri from Authgear
    // you can cache the value of jwks using jwk.Cache to have better performance
    set, err := FetchJWK(baseAddress)
    if err != nil {
        return "", false, false, fmt.Errorf("failed to fetch JWK: %s", err)
    }

    // parse jwt token
    token, err := jwt.ParseRequest(
        r, 
        // This may not work out of the box depending on the jwk.Set.
        // Please read about requirements for "kid" and "alg" (and possibly
        // "WithDefaultKey") when using jwk.Set in the jwt.WithKeySet documentation.
        jwt.WithKeySet(set),
    )
    if err != nil {
        return "", false, false, fmt.Errorf("invalid token: %s", err)
    }

    // validate jwt token
    err = jwt.Validate(token,
        jwt.WithClock(jwt.ClockFunc(
            func() time.Time { return time.Now().UTC() },
        )),
        jwt.WithAudience(baseAddress),
    )
    if err != nil {
        return "", false, false, fmt.Errorf("invalid token: %s", err)
    }

    var verified bool
    var anonymous bool
    // ignore errors -- if the claim does not exist, it doesn't matter
    _ = token.Get("https://authgear.com/claims/user/is_verified", &verified)
    _ = token.Get("https://authgear.com/claims/user/is_anonymous", &anonymous)

    // In jwx v3, Subject() returns (value, ok).
    sub, _ := token.Subject()
    return sub, verified, anonymous, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
    // decode user example
    userid, isUserVerified, isAnonymousUser, err := DecodeUser(r)

    // ... your handler logic
}
```

{% endtab %}

{% tab title="Java" %}
The following example uses Spring Boot.

**Step 1: Install dependencies**

Add the following dependencies to your build.gradle file:

```gradle
dependencies {
	implementation("com.nimbusds:nimbus-jose-jwt:10.2")
	implementation("org.json:json:20250107")
}
```

Then add the following imports to the top of your controller file:

```java
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
```

**Step 2: Get JWKS Endpoint**

Implement the following method to fetch the JWKS URI:

```java

private static String fetchJwksUri(String baseAddress) throws Exception {
	String docUrl = baseAddress + "/.well-known/openid-configuration";
	HttpURLConnection conn = (HttpURLConnection) new URL(docUrl).openConnection();
	conn.setRequestMethod("GET");

	try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
		StringBuilder response = new StringBuilder();
		String line;
		while ((line = reader.readLine()) != null) {
			response.append(line);
		}

		String jwksUri = new org.json.JSONObject(response.toString()).getString("jwks_uri");
		if (jwksUri == null || jwksUri.isEmpty()) {
			throw new Exception("Failed to fetch JWKS URI.");
		}
		return jwksUri;
	}
}
```

**Step 3: Get Signing Key**

Get the signing key from the JWK using the following method:

```java
private static RSAKey getSigningKeyFromJwks(String jwksUri, String token) throws Exception {
	JWKSet jwkSet = JWKSet.load(new URL(jwksUri));
	List<com.nimbusds.jose.jwk.JWK> keys = jwkSet.getKeys();

	SignedJWT signedJWT = SignedJWT.parse(token);
	String keyId = signedJWT.getHeader().getKeyID();

	return keys.stream()
			.filter(jwk -> jwk.getKeyID().equals(keyId))
			.findFirst()
			.map(jwk -> (RSAKey) jwk)
			.orElse(null);
}
```

**Step 4: Validate JWT**

To demonstrate how to validate a JWT, we'll implement a `validateJWT` endpoint in a Spring Boot application. The endpoint will read access tokens from the bearer authorization header.

It will call the `fetchJwksUri()` and `getSigningKeyFromJwks()` from steps 1 and 2 to get the JWK URI and signing key required to parse the JWT.

```java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;


@SpringBootApplication
@RestController
public class DemoApplication {

	//paste implementation for fetchJwksUri() method below this line.
	
	
	//paste implemetation of getSigningKeyFromJwks() method below this line.

	private static final String BASE_ADDRESS = ""; //place your authgear app endpoint here

	@GetMapping("/validateJwt")
	public Object validateJwt(@RequestHeader("Authorization") String authorizationHeader) {
		if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
			return new ResponseMessage("authorization header not found");
		}

		String token = authorizationHeader.substring(7); // Extract token

		try {
			// Fetch JWKS URI dynamically
			String jwksUri = fetchJwksUri(BASE_ADDRESS);

			// Get signing key from JWKS
			RSAKey signingKey = getSigningKeyFromJwks(jwksUri, token);
			if (signingKey == null) {
				return new ResponseMessage("JWT decode failed: Signing key not found");
			}

			// Validate and decode JWT
			SignedJWT signedJWT = SignedJWT.parse(token);
			JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();

			return new ResponseMessage("Hello!", claimsSet.toJSONObject());

		} catch (Exception e) {
			return new ResponseMessage("JWT decode failed: " + e.getMessage());
		}
	}

	private static String fetchJwksUri(String baseAddress) throws Exception {
		String docUrl = baseAddress + "/.well-known/openid-configuration";
		HttpURLConnection conn = (HttpURLConnection) new URL(docUrl).openConnection();
		conn.setRequestMethod("GET");

		try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
			StringBuilder response = new StringBuilder();
			String line;
			while ((line = reader.readLine()) != null) {
				response.append(line);
			}

			String jwksUri = new org.json.JSONObject(response.toString()).getString("jwks_uri");
			if (jwksUri == null || jwksUri.isEmpty()) {
				throw new Exception("Failed to fetch JWKS URI.");
			}
			return jwksUri;
		}
	}

	private static RSAKey getSigningKeyFromJwks(String jwksUri, String token) throws Exception {
		JWKSet jwkSet = JWKSet.load(new URL(jwksUri));
		List<com.nimbusds.jose.jwk.JWK> keys = jwkSet.getKeys();

		SignedJWT signedJWT = SignedJWT.parse(token);
		String keyId = signedJWT.getHeader().getKeyID();

		return keys.stream()
				.filter(jwk -> jwk.getKeyID().equals(keyId))
				.findFirst()
				.map(jwk -> (RSAKey) jwk)
				.orElse(null);
	}

	static class ResponseMessage {
		public String message;
		public Object user_data;

		public ResponseMessage(String message) {
			this.message = message;
		}

		public ResponseMessage(String message, Object user_data) {
			this.message = message;
			this.user_data = user_data;
		}
	}
}

```

{% endtab %}

{% tab title="PHP" %}
**Step 1: Install Packages**

First, install the dependencies required by running these com

```bash
composer require firebase/php-jwt
```

```bash
composer require guzzlehttp/guzzle
```

**Step 2: Find the JWKS Endpoint**

Create a function that finds the JWKS endpoint from your Authgear application endpoint using the following code:

```php
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;

use Firebase\JWT\JWK;
use Firebase\JWT\Key;
use GuzzleHttp\Client;

$appUrl = ""; //place your authgear app endpoint here

function getJwksUri($appUrl) {
    $configEndpoint = $appUrl . "/.well-known/openid-configuration";
    $httpClient = new Client();
    $response = $httpClient->request('GET', $configEndpoint);
    $responseObject = json_decode($response->getBody());
    return $responseObject->jwks_uri;
}
```

**Step 3: Get Signing Key**

Add the following code to your application to get the JWT signing key:

```php
$jwksUri = getJwksUri($appUrl);
$httpClient = new Client();
$jwksUriResponse = $httpClient->request('GET', $jwksUri);
$keysObject = json_decode($jwksUriResponse->getBody());

$jwks = (array) ($keysObject->keys)[0];

$parsedKey = JWK::parseKey($jwks, "RS256");
$signingKey = $parsedKey->getKeyMaterial();
```

**Step 4: Extract the JWT From the Request Header**

To extract the access token from the HTTP request use the following code:

```php
if (!isset($_SERVER['HTTP_AUTHORIZATION']))
    throw new Exception("Invalid authorization header");
$authorizationHeader = $_SERVER['HTTP_AUTHORIZATION'];
$jwt = (explode(" ", $authorizationHeader))[1];
```

**Step 5: Validate and Decode JWT**

Finally, decode the JWT signing key.

```php
$decoded = JWT::decode($jwt, new Key($signingKey, 'RS256'));
echo json_encode($decoded);
```

{% endtab %}

{% tab title="ASP.NET" %}
**Step 1: Install NuGet packages**

```bash
dotnet add package NSwag.AspNetCore
```

then add these imports to the top of your program.cs file:

```c#
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
```

**Step 2: Configure JWT Authentication**

This tells ASP.NET Core to use JWT Bearer tokens for authentication

```c#
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;

builder.Services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(x => // here we configure what to validate in JWT tokens  
{
    // .NET will automatically fetch JWKS keys from {authority}/.well-known/openid-configuration
    x.Authority = ""; // place your authgear app endpoint here,
    x.RequireHttpsMetadata = false; // Allow HTTP for development
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = x.Authority,
        ValidateAudience = false, // set to true if you validate audience
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
    };
});
```

**Step 3: Add authorization**

```c#
builder.Services.AddAuthorization();
```

**Step 4: Configure middleware pipeline**

Order is important! Authentication must come before Authorization

```c#
app.UseAuthentication();
app.UseAuthorization();
```

**Step 5: Create a protected endpoint**

The following example uses the Minimal API model

```c#
app.MapGet("/", (HttpContext context) => 
{
    var user = context.User;
    Console.WriteLine($"Authorization header: {context.Request.Headers.Authorization}");
    Console.WriteLine($"User authenticated: {user.Identity?.IsAuthenticated}");
    if (user.Identity?.IsAuthenticated == true)
    {
        var claims = user.Claims.ToDictionary(c => c.Type, c => c.Value);
        return Results.Ok(claims);
    }
    return Results.Json(new { error = "Unauthorized" }, statusCode: 401);
}).RequireAuthorization(); // require auth from this endpoint
```

For Controller-based APIs, simply add \[Authorize] to your controller class or individual action methods to protect them
{% endtab %}
{% endtabs %}

### Check the validity of JWT

The `auth_time` claim in an **OIDC ID token** represents the time **when the user authentication occurred**. Extract the `auth_time` claim from the token, which should represent the time of the original authentication in seconds. If the difference between the current time and `auth_time` exceeds your threshold (for example, 5 minutes), initiate the [re-authentication](/authentication-and-access/authentication/reauthentication) process.

See an example of how to verify the signature of the ID token, and then validate the claims `auth_time` inside [here](/authentication-and-access/authentication/reauthentication#backend-integration).

## Decode user from cookies

Validating JWT in your application server is *currently* only available for **Token-based authentication**.

{% hint style="info" %}
For Cookie-based authentication, JWT in cookies is not supported yet. [You can track the issue here](https://github.com/authgear/authgear-server/issues/1180).
{% endhint %}


# Forward Authentication to Authgear Resolver Endpoint

Authenticate the incoming HTTP requests by forwarding Authentication to Authgear Resolver Endpoint

In this section, we will explain how to set up a reverse proxy in NGINX to protect your app server from unauthorized access with the Authgear resolver. You can forward the requests without the request body to the resolver endpoint. Authgear will look at the `Authorization` and `Cookie` in the HTTP header, verify the token, and respond to HTTP 200 with `X-Authgear-` headers for session validity, the user id...etc.

If you use a popular reverse proxy on your deployment, such as [NGINX](https://www.nginx.com/), [Traefik](https://traefik.io/traefik/), or API Gateways such as [Apache APISIX](https://apisix.apache.org/), you can configure it with a few simple lines of forward auth config. Your backend should read the returned headers to determine the identity of the user of the HTTP request.

You can also use the forward authentication features of the other popular reverse proxy. e.g.

* [Traefik ForwardAuth middleware](https://doc.traefik.io/traefik/v2.0/middlewares/forwardauth/)

## Authgear Resolver Endpoint

Authgear provides an endpoint for forward authentication. Subrequests should be made to the following endpoint for authentication.

`https://<your_app_endpoint>/_resolver/resolve`

## How Forward Authentication Works

![](/files/-MbicWRgOJ0qakVbcKnx)

1. After the user is logged in, send an application request to your server from the client app with access token/cookies.
2. Set up a reverse proxy in your infrastructure to authenticate HTTP requests. The reverse proxy will forward the incoming HTTP requests without the request body to the Authgear Resolver Endpoint.
3. Authgear resolver parses the access token and returns HTTP headers including the user login state. The headers are starting with `x-authgear-`.
4. You have to instruct your reverse proxy to include those extra headers, before forwarding the request to your backend server.
5. Your backend server looks at the headers and responds to the client app accordingly. e.g. Returns the user's content or HTTP 401 if the user is not logged in.

There are so many reverse proxies available in the wild. So here we are going to illustrate the idea of using Nginx as the reverse proxy.

## Using Nginx as the reverse proxy

We will use the module `auth_request` in NGINX. The module is not built by default, it should be enabled with the `--with-http_auth_request_module`configuration parameter.

Run this command and verify that the output includes `--with-http_auth_request_module`:

```bash
$ nginx -V 2>&1 | grep -- 'http_auth_request_module'
```

The trick here is to declare an internal `location` and use `auth_request` to initiate a subrequest to the resolved endpoint.

### Example configuration

```
server {
  # Use variable in proxy_pass with resolver to respect DNS TTL.
  # Note that /etc/hosts and /etc/resolv.conf are NOT consulted if resolver is used.
  # See https://www.nginx.com/blog/dns-service-discovery-nginx-plus/
  resolver 8.8.8.8;
  
  # Location that requires request authentication
  location / {
    set $backend http://www.mycompany.com;
    proxy_pass $backend;
    proxy_set_header Host $host;
    # Specify the auth_request directive to initiate subrequests to the to the internal location.
    # This corresponds to the Step 2.
    auth_request /_auth;

    # Copy the `x-authgear-*` headers from the response of the subrequest to Nginx variables.
    # This corresponds to the Step 3.
    auth_request_set $x_authgear_session_valid $upstream_http_x_authgear_session_valid;
    auth_request_set $x_authgear_user_id $upstream_http_x_authgear_user_id;
    auth_request_set $x_authgear_user_anonymous $upstream_http_x_authgear_user_anonymous;
    auth_request_set $x_authgear_user_verified $upstream_http_x_authgear_user_verified;
    auth_request_set $x_authgear_session_acr $upstream_http_x_authgear_session_acr;
    auth_request_set $x_authgear_session_amr $upstream_http_x_authgear_session_amr;
    auth_request_set $x_authgear_session_authenticated_at $upstream_http_x_authgear_session_authenticated_at;
    auth_request_set $x_authgear_user_can_reauthenticate $upstream_http_x_authgear_user_can_reauthenticate;

    # Include the headers in the request that will be sent to your backend server.
    # This corresponds to the Step 4.
    proxy_set_header x-authgear-session-valid $x_authgear_session_valid;
    proxy_set_header x-authgear-user-id $x_authgear_user_id;
    proxy_set_header x-authgear-user-anonymous $x_authgear_user_anonymous;
    proxy_set_header x-authgear-user-verified $x_authgear_user_verified;
    proxy_set_header x-authgear-session-acr $x_authgear_session_acr;
    proxy_set_header x-authgear-session-amr $x_authgear_session_amr;
    proxy_set_header x-authgear-session-authenticated-at $x_authgear_session_authenticated_at;
    proxy_set_header x-authgear-user-can-reauthenticate $x_authgear_user_can_reauthenticate;

    # Your backend must inspect the request headers to determine whether the request is authenticated or not.
    # This corresponds to the Step 5.
  }

  location = /_auth {
    # Set this location for internal use only
    internal;
    # Replace <YOUR_AUTHGEAR_ENDPOINT> with your authgear endpoint.
    # For example, https://example.authgear.cloud
    set $resolver <YOUR_AUTHGEAR_ENDPOINT>/_resolver/resolve;
    proxy_pass $resolver;
    # Set the host so that authgear knows which app is calling the resolver endpoint
    # Replace <YOUR_AUTHGEAR_ENDPOINT_HOST> with the host of your authgear endpoint.
    # For example, example.authgear.cloud
    proxy_set_header Host <YOUR_AUTHGEAR_ENDPOINT_HOST>;
    # The body is supposed to be consumed by your backend server.
    # Pass only the headers to the resolver
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
  }
}
```

{% hint style="info" %}
See docs for `auth_request` in NGINX for more details. <http://nginx.org/en/docs/http/ngx_http_auth_request_module.html>
{% endhint %}

## Optimizing the performance

If the reverse proxy, Authgear, and your backend server are in different regions, authenticating every request could result in a huge downgrade in the performance.

You may consider enabling caching.

```
http {
  # ...
  proxy_cache_path /tmp/cache keys_zone=auth_cache:10m;

  # The server block.
  server {
    # ...
    location = /_auth {
      # ...
      proxy_cache auth_cache;
      proxy_cache_key "$cookie_session|$http_authorization";
      proxy_cache_valid 200 10m;  # Adjust cache duration as desired.
    }
  }
}
```

## Reference on the headers

See the list of `x-authgear-` headers in the specs: <https://github.com/authgear/authgear-server/blob/master/docs/specs/api-resolver.md>


# NestJS API

Protect a NestJS API with Authgear using the @authgear/nestjs SDK

Protect a [NestJS](https://nestjs.com/) API with Authgear using the `@authgear/nestjs` SDK. The SDK validates Authgear **JWT access tokens** offline (via OIDC discovery and JWKS) and gives you a NestJS module, an authentication guard, and decorators — so protecting a route takes just a few lines.

A complete example application is available at [authgear/authgear-example-nestjs](https://github.com/authgear/authgear-example-nestjs).

{% hint style="info" %}
This SDK is for the **resource server** side — it verifies the access tokens your API receives. It does not perform the login flow. Your users log in through a separate client (a [Single-Page App](/get-started/single-page-app), a [mobile app](/get-started/native-mobile-app), or any OAuth client), which then calls your NestJS API with the access token.
{% endhint %}

**What you will build:**

* A public route (`GET /health`) that needs no token
* A protected route (`GET /me`) that returns the authenticated user's claims

***

### Setting Up Your Application in Authgear

#### Step 1: Enable JWT access tokens

The SDK validates JWT access tokens offline, so the application that issues the tokens your API will accept must issue JWTs.

1. Sign in to the [Authgear Portal](https://portal.authgear.com/)
2. Select your Project, then go to **Applications** and open the application your client uses to log in (or [create one](/get-started/single-page-app) — e.g. a **Single Page Application**)
3. Under the **Access Token** section, turn on **Issue JWT as access token**
4. Note down your **Endpoint** (e.g. `https://your-project.authgear.cloud`) and **Client ID** — you will need these shortly
5. Click **Save**

{% hint style="warning" %}
Without **Issue JWT as access token**, Authgear issues opaque access tokens, which this SDK cannot validate offline. Requests would be rejected with `401`.
{% endhint %}

***

### Building Your NestJS Application

#### Step 1: Create a NestJS Project

```bash
npm i -g @nestjs/cli
nest new my-api
cd my-api
```

#### Step 2: Install the Authgear SDK

```bash
npm install @authgear/nestjs
```

This also installs `@nestjs/config`, which we use to read configuration from the environment:

```bash
npm install @nestjs/config
```

#### Step 3: Configure Environment Variables

Create a `.env` file in the project root:

```bash
AUTHGEAR_ENDPOINT=https://your-project.authgear.cloud
AUTHGEAR_CLIENT_ID=your-client-id
```

{% hint style="info" %}
`AUTHGEAR_CLIENT_ID` is optional. When set, the SDK additionally asserts that the token's `client_id` claim matches it.
{% endhint %}

#### Step 4: Register the Authgear Module

Register `AuthgearModule` in your root module. Setting `global: true` registers the guard as a global guard, so **every route is protected by default** unless explicitly marked public.

```typescript
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthgearModule } from '@authgear/nestjs';
import { AppController } from './app.controller';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    AuthgearModule.forRootAsync({
      global: true,
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        endpoint: config.getOrThrow<string>('AUTHGEAR_ENDPOINT'),
        clientID: config.get<string>('AUTHGEAR_CLIENT_ID'),
      }),
    }),
  ],
  controllers: [AppController],
})
export class AppModule {}
```

{% hint style="info" %}
If your configuration is static, you can use `AuthgearModule.forRoot({ endpoint: '...', global: true })` instead of `forRootAsync`.
{% endhint %}

#### Step 5: Protect Your Routes

With the guard registered globally, mark public routes with `@Public()`. Read the authenticated user with the `@CurrentUser()` parameter decorator.

```typescript
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Public, CurrentUser, AuthgearClaims } from '@authgear/nestjs';

@Controller()
export class AppController {
  // Public — no token required
  @Public()
  @Get('health')
  health() {
    return { status: 'ok' };
  }

  // Protected — requires a valid Authgear access token
  @Get('me')
  me(@CurrentUser() user: AuthgearClaims) {
    return {
      sub: user.sub,
      isVerified: user.isVerified,
      isAnonymous: user.isAnonymous,
    };
  }
}
```

`AuthgearClaims` exposes the common claims (`sub`, `iss`, `aud`, `clientID`, `isVerified`, `isAnonymous`, `canReauthenticate`) plus the full decoded payload as `raw` for any custom claims.

{% hint style="info" %}
Prefer to protect routes individually instead of globally? Omit `global: true` and apply the guard per controller or handler with `@UseGuards(AuthgearAuthGuard)`.
{% endhint %}

***

### Running the Application

```bash
npm run start:dev
```

The API listens on <http://localhost:3000>.

***

### Testing the Integration

The public route works without a token:

```bash
curl -i http://localhost:3000/health
# 200 {"status":"ok"}
```

The protected route is rejected without a valid token:

```bash
curl -i http://localhost:3000/me
# 401 {"message":"Missing bearer token","error":"Unauthorized","statusCode":401}
```

To call the protected route, send an Authgear access token as a Bearer token:

```bash
curl -i http://localhost:3000/me \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
# 200 {"sub":"...","isVerified":true,"isAnonymous":false}
```

{% hint style="info" %}
Obtain an access token by logging a user in through a client application. The [example project](https://github.com/authgear/authgear-example-nestjs) includes a small frontend that signs in and calls the protected API for you. To build your own client, see the [Single-Page App](/get-started/single-page-app) or [Native/Mobile App](/get-started/native-mobile-app) guides.
{% endhint %}

***

### Module Options

`forRoot()` and the object returned by the `forRootAsync()` factory accept:

| Option                  | Type      | Required | Default | Description                                                             |
| ----------------------- | --------- | -------- | ------- | ----------------------------------------------------------------------- |
| `endpoint`              | `string`  | ✓        | —       | Authgear project endpoint. Used for OIDC discovery and JWKS.            |
| `clientID`              | `string`  |          | —       | If set, the verifier also asserts the token's `client_id` claim.        |
| `global`                | `boolean` |          | `false` | Register the guard as a global `APP_GUARD` so all routes are protected. |
| `jwksCacheMaxAge`       | `number`  |          | —       | JWKS cache max age in milliseconds.                                     |
| `clockToleranceSeconds` | `number`  |          | `0`     | Leeway in seconds for `exp`/`iat` checks.                               |

You can also inject `AuthgearTokenService` to verify a token outside of the guard.

***

### Next Steps

* [JWT access token reference](/reference/tokens/jwt-access-token) — the full list of claims
* [Validate JWT in your backend](/get-started/backend-api/jwt) — the framework-agnostic approach
* [`@authgear/nestjs` documentation & API reference](https://authgear.github.io/authgear-sdk-nestjs/)


# Machine-to-Machine (M2M) Applications

Enable secure, automated authorization for your backend systems, microservices, and IoT devices, ensuring only trusted apps and devices can access your APIs.

### What Are M2M Applications? <a href="#what-are-m2m-applications" id="what-are-m2m-applications"></a>

Machine-to-machine (M2M) applications enable backend services, CLIs, scheduled jobs, and smart devices to obtain access tokens and interact with APIs using dedicated credentials. Instead of human user authentication, these apps use their own credentials to securely access resources, streamlining automation and integration across your stack.

## Common Use Cases <a href="#common-use-cases" id="common-use-cases"></a>

* **Application Backends:** Facilitates secure transfer of data, files, or logs between microservices or external systems.
* **CLI Tools:** Allows tools running on developer or deployment machines to access APIs with short-lived tokens.
* **Scheduled Jobs & Daemons:** Empowers background tasks (e.g., cronjobs, queuing systems) to access protected resources safely.
* **IoT Devices:** Enables each smart device to authenticate independently and send data securely to cloud APIs.

## Set Up M2M Applications in Authgear <a href="#steps-to-set-up-m2m-applications-in-authgear" id="steps-to-set-up-m2m-applications-in-authgear"></a>

### 1. Register Your API Resources

Go to the **API Resources** page in the Authgear portal:

* Create the API resource your apps/devices will access.
* When registering the resource, specify its **identifier**—this acts as the unique URI representing your API (for example, `https://myapi.com/api`).
  * This identifier will be set as the `aud` (audience) claim in the JWT access tokens issued for this resource.
* Define **scopes** for granular permissions, such as `read:data`, `write:data`, or `manage:config`.
* Scopes specify exactly **what** operations clients can perform on your API.

### 2. Register Your Application

In the **Applications** page:

* Create a client application for each backend service, CLI tool, job, or device that needs API access.
* Assign the relevant **API resources** and **scopes** to the application, ensuring least-privilege access.
* After creating the application, you’ll be able to view its **Client ID** and **Client Secret** in the portal. Keep the Client Secret secure. It should never be exposed in client-side code or public repositories.

### 3. Request a Token Using the Client Credentials Flow

The **Client Credentials Flow** defined in [OAuth 2.0 RFC 6749, section 4.4](https://tools.ietf.org/html/rfc6749#section-4.4) is designed for non-user, automated access.

To obtain an access token in your backend, use the Client ID and Client Secret from your registered application in the portal:

{% tabs %}
{% tab title="curl" %}

```
curl --request POST \
  --url https://myproject.authgear.cloud/oauth2/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data grant_type=client_credentials \
  --data resource=IDENTIFIER_OF_API_RESOURCE \
  --data client_id=CLIENT_ID \
  --data client_secret=CLIENT_SECRET
```

{% endtab %}

{% tab title="Python" %}

```python
import urllib.parse
import urllib.request
import json

url = "https://myproject.authgear.cloud/oauth2/token"
headers = {
    "Content-Type": "application/x-www-form-urlencoded"
}
data = {
    "grant_type": "client_credentials",
    "resource": "IDENTIFIER_OF_API_RESOURCE",
    "client_id": "CLIENT_ID",
    "client_secret": "CLIENT_SECRET"
}

encoded_data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data=encoded_data, headers=headers, method='POST')

with urllib.request.urlopen(req) as response:
    response_status = response.getcode()
    response_body = response.read().decode('utf-8')
    print("Response Status Code:", response_status)
    print("Response Body:", json.loads(response_body))

```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
  "fmt"
  "io/ioutil"
  "net/http"
  "net/url"
  "strings"
)

func main() {
  data := url.Values{}
  data.Set("grant_type", "client_credentials")
  data.Set("resource", "IDENTIFIER_OF_API_RESOURCE")
  data.Set("client_id", "CLIENT_ID")
  data.Set("client_secret", "CLIENT_SECRET")

  req, _ := http.NewRequest("POST", "https://myproject.authgear.cloud/oauth2/token", strings.NewReader(data.Encode()))
  req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

  resp, _ := http.DefaultClient.Do(req)
  defer resp.Body.Close()

  body, _ := ioutil.ReadAll(resp.Body)

  fmt.Println("Response Status Code:", resp.StatusCode)
  fmt.Println("Response Body:", string(body))
}

```

{% endtab %}

{% tab title="Node JS" %}

```javascript
async function makeRequest() {
  const url = "https://myproject.authgear.cloud/oauth2/token";
  const data = new URLSearchParams();
  data.append("grant_type", "client_credentials");
  data.append("resource", "IDENTIFIER_OF_API_RESOURCE");
  data.append("client_id", "CLIENT_ID");
  data.append("client_secret", "CLIENT_SECRET");

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: data,
  });

  const responseBody = await response.json();
  console.log("Response Status Code:", response.status);
  console.log("Response Body:", responseBody);
}

makeRequest();

```

{% endtab %}
{% endtabs %}

You’ll receive an access token that includes only the permitted scopes for the client.

For details on token structure and claims, see [M2M Tokens](/reference/tokens/m2m-tokens).

### 4. Access Protected APIs

Send requests to your API by including the access token in the `Authorization` header:

```
Authorization: Bearer <access_token>
```

Your backend can now verify the token and authorize the permitted operations.

### 5. Verify Tokens on the API Server

Always validate incoming JWT tokens in your API servers:

* Verify issuer, audience, scopes, expiration, and signature.
* Reject requests with invalid or expired tokens.

Find sample code and best practices at [Validate JWT in your backend](/get-started/backend-api/jwt).


# Integration by OIDC

Using Authgear as an OpenID Connect Provider for any OIDC compatible applications for Single Sign-On.

If your application supports logging in using an OpenID Connect provider, you can use Authgear as the provider.

## Setting up Authgear in the Portal

1. Go to **Applications** on the left menu bar.
2. Click **⊕Add Application** in the top tool bar.
3. Input the name and select the application type **OIDC Client Application**. Click "Save".
4. You will see a link to this guide that can help you for setting up, then click "Next".
5. In the **URIs** section, fill in the **Authorized Redirect URIs** with your application's redirect uri.
6. Obtain the OpenID Connect configuration:
   1. You can obtain the **Client ID** and **Client Secret** from the **Basic Info** section.
   2. You can obtain the **OIDC Endpoints** from the **Endpoints** section.
7. Provide the OpenID Connect configuration to your application.

🎉 Done! You should be able to use Authgear to log in to your application.

## WordPress Example

In this section, we are going to demonstrate how to use Authgear as the OIDC provider for WordPress login.

1. Follow the previous section ([Setting up Authgear in the Portal](#setting-up-authgear-in-the-portal)) to setup an **OIDC Client Application**.
2. We are going to use plugin [OpenID Connect Generic Client](https://wordpress.org/plugins/daggerhart-openid-connect-generic/). Or you can use any other OIDC compatible plugin. Download and activate it in your WordPress site.
3. Go to **Setting** > **OpenID Connect Client**.
4. Fill in the form
   1. **Client ID**: Obtain the **Client ID** from the **Basic Info** section.
   2. **Client Secret Key**: Obtain the **Client Secret** from the **Basic Info** section.
   3. **OpenID Scope**: Space separated list of scopes the plugin could access.
      * Example: `openid offline_access https://authgear.com/scopes/full-userinfo`.
      * `https://authgear.com/scopes/full-userinfo` is needed to obtain user's profile (e.g. email). Otherwise the plugin will be able to get the user id only.
   4. **Login Endpoint URL**: Obtain **Authorization Endpoint** from the **Endpoints** section.
      * Example: `https://{AUTHGEAR_APP_DOMAIN}/oauth2/authorize`.
   5. **Userinfo Endpoint URL**: Obtain **Userinfo Endpoint** from the **Endpoints** section.
      * Example: `https://{AUTHGEAR_APP_DOMAIN}/oauth2/userinfo`.
   6. **Token Validation Endpoint URL**: Obtain **Token Endpoint** from the **Endpoints** section.
      * Example: `https://{AUTHGEAR_APP_DOMAIN}/oauth2/token`.
   7. **End Session Endpoint URL**: Keep it empty.
   8. **Identity Key**: Where in the user claim to find the user's identification data.
      * Suggest to use `sub` which is the user id in Authgear.
   9. Setup the user claim keys based on your project login method setting.
      * If your project is using **email** to login
        * **Nickname Key**: Set it to `email`.
        * **Email Formatting**: Set it to `{email}`.
      * If your project is using **phone** to login
        * **Nickname Key**: Set it to `phone_number`.
        * **Email Formatting**: Clear it.
      * If your project is using **username** to login
        * **Nickname Key**: Set it to `preferred_username`.
        * **Email Formatting**: Clear it.
5. At the bottom of the plugin settings page, you will be able to obtain the **Redirect URI**. Go to Authgear portal, add the uri to the **Authorized Redirect URIs**.


# Integration by SAML

Guides on how to use Authgear as a SAML Identity Provider for Single Sign-On

Security Assertion Markup Language or short [SAML](https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) is a standard for exchanging security information between businesses. In SAML, one party acts as the Identity Provider (IdP), and the other party is the Service Provider (SP).

SAML allows the Identity Provider and Service Provider to authenticate and authorize without exchanging a user's password.

* **The Service Provider (SP)**: In SAML, this is the service that trust the Identity Provider to handle the process of user authentication.
* **The Identity Provider (IdP)**: handles user authentication and notifies the Service Provider once the user is authenticated.

<figure><img src="/files/tcHJK3GW8tMwlUhUhHB6" alt=""><figcaption></figcaption></figure>

Authgear supports the SAML protocol. Hence, you can set up third-party services like Salesforce, Dropbox, Figma, etc. to trust Authgear with the user authentication process.

## Specific Instructions for Service Providers

See the following guides for some popular service providers:

* [Use Authgear as SAML Identity Provider for Salesforce](/get-started/single-sign-on-with-saml/use-authgear-as-saml-identity-provider-for-salesforce)
* [Use Authgear as SAML Identity Provider for Dropbox](/get-started/single-sign-on-with-saml/use-authgear-as-saml-identity-provider-for-dropbox)

## How to Set up SAML in Authgear

To set up SAML in Authgear, you need to create an Authgear client application with the Application Type: `OIDC/SAML Client`. Then use the configuration for the Authgear client application to configure a SAML IdP on the Service Provider's platform.

The following steps show more details on how to set up an OIDC/SAML Client Application in Authgear Portal.

### Step 1: Create Authgear Client Application

Log in to Authgear Portal, then click on Applications from the navigation menu.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Click on Add Application to create a new client application. Or select an existing client application with the `OIDC/SAML Client` type.

Enter a **Name** for the application and select OIDC/SAML Client Application as the **Application Type**.

<figure><img src="/files/JiCwxn1BlP2EYYRBvz9A" alt=""><figcaption></figcaption></figure>

Click **Save** to proceed.

### Step 2: Enable SAML 2.0

By default, the SAML 2.0 Configuration is disabled for the client application.

Click on the SAML 2.0 tab then toggle **SAML 2.0 Support** switch to enable SAML 2.0.

<figure><img src="/files/oJggfgski2rTFdV9SkE9" alt=""><figcaption></figcaption></figure>

You'll be required to enter at least one **Allowed Assertion Consumer Service URLs (ACS URLs)** before you can save your changes. Hence, get an **ACS URL** from the Service Provider you plan to use.

### Step 3: Configure Authgear as IdP on a Service Provider

Visit the portal for the Service Provider you plan to use and add Authgear as an Identity Provider using the SAML configuration from your Authgear client application.

Refer to the following instructions for a generic SP:

**Configuration on SP:**

* Enter the Identity Provider Metadata URL provided by Authgear if it's supported by the SP. e.g. `https://[AUTHGEAR_ENDPOINT]/saml2/metadata/[CLIENT_ID]`
* If the SP does not support uploading an IDP metadata file, you can manually enter the parameters into the SP. These values can be copied from the application settings page:
  * Issuer: `urn:[AUTHGEAT_ENDPOINT]`
  * Login URL: `https://[AUTHGEAR_ENDPOINT]/saml2/login/[CLIENT_ID]`
  * Logout URL: `https://[AUTHGEAR_ENDPOINT]/saml2/logout/[CLIENT_ID]`
  * Identity Provider Certificates in PEM format: Download from the application settings page

**Configuration on Authgear**

* Upload the Metadata XML file provided by your client application into the Authgear Portal
* You may also manually enter the parameters into the application settings page in the Portal:
  * NameID Format
    * `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified` , or
      * When the format is `unspecified`, you can choose to use the User ID, Email, Phone, or Username as the attribute value
    * `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`
  * Allowed Assertion Consumer Service URLs (ACS URLs)
  * Response Destination (Optional)
  * Subject Recipient (Optional)
  * Assertion Audience (Optional)
  * Assertion Valid Duration (seconds), Default: 1200
  * Enable/Disable Single Logout (SLO)
    * SLO Callback URL
    * Callback Binding
      * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect`, or
      * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST`
  * Enable/Disable message signature verification
    * Upload the SP's certificate in PEM format

You may find more detailed guides for adding IdP on the Service Provider's documentation.

## Tools for troubleshooting

* Online Base64 decode/encode: <https://www.authgear.com/tools/base64-decode-encode>


# Use Authgear as SAML Identity Provider for Salesforce

A guide on how to use Authgear as a SAML Identity Provider IdP in Salesforce

Security Assertion Markup Language or short [SAML](https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) is a standard for exchanging security information between businesses. In SAML, one party acts as the Identity Provider (IdP) and the other party is the Service Provider (SP).

The Service Provider (SP) trust the Identity Provider to handle the process of user authentication. The Identity Provider handles user authentication and notifies the Service Provider once the user is authenticated.

In this guide, you'll learn how to set up SAML with Authgear as an Identity Provider (IdP) and Salesforce as the Service Provider (SP) in SAML.

### Prerequisites

* An Authgear account. [Sign up](https://authgear.com) for free.
* A Salesforce account.

### Step 1: Create an Authgear Client Application

An Authgear Client application is required to set up Authgear as a SAML Identity Provider.\
To create an Authgear application, login to the Authgear Portal and navigate to the **Applications** in your project.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Click on **Add Application** to create a new application. Or, select an existing application that is of type `OIDC/SAML Client Application` and skip to step 2.

Now on the New Application page, enter a name for your application (e.g. My App) and set the Application Type to "OIDC/SAML Client Application".

<figure><img src="/files/JiCwxn1BlP2EYYRBvz9A" alt=""><figcaption></figcaption></figure>

Click **Save** to continue.

If prompted to view a tutorial, click **Next** to proceed to the application configuration page.

### Step 2: Enable SAML 2.0 in Client Application

On the configuration page of your Authgear client application, switch to the **SAML 2.0** tab. Toggle the **SAML 2.0 Support** switch on to enable SAML for the application.

<figure><img src="/files/oJggfgski2rTFdV9SkE9" alt=""><figcaption></figcaption></figure>

Next, change **NameID Format** to `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`.

Enter your Salesforce domain in the **Allowed Assertion Consumer Service URLs (ACS URLs)** field under SSO Settings. You can get value for your Salesforce domain from the My Domain page on the Salesforce Setup page.

<figure><img src="/files/phl8lW7mSBR4wyDUdDGo" alt=""><figcaption></figcaption></figure>

Click on **Save** to keep your changes.

### Step 3: Get SAML Identity Provider (IdP) Configuration and Download Certificate

Still, on the SAML 2.0 tab, scroll to the **Configuration Parameters** section and click on the **Download Metadata** button to download the Identity Provider Metadata XML file for your Authgear application to your computer.

Next, scroll to the Identity Provider Certificates section and click **Download Certificate** to download the certificate to your computer.

You will use the downloaded metadata file and certificate in later steps.

### Step 4: Enable SAML in Salesforce

To enable SAML in Salesforce, login to your Salesforce account, click on the Settings icon on the top right corner then select Setup. This will open the Salesforce Setup page.

<figure><img src="/files/BcZdDFJtUjoB5e2Of7qi" alt=""><figcaption></figcaption></figure>

On the Setup page, type "single sign-on" in the **Quick find** search box on the left. Select **Single Sign-On** from the result to open the **Single Sign-On Settings** page.

<figure><img src="/files/3ocXktjmnmrDGoNGGLgl" alt=""><figcaption></figcaption></figure>

Next, click on the **Edit** button under Select Single Sign-On Settings then check the **SAML Enabled** box under **Federated Single Sign-On Using SAML**. Click **Save** to keep your changes.

<figure><img src="/files/WQ8gXd0OykmicCK5eW22" alt=""><figcaption></figcaption></figure>

### Step 5: Add Authgear as SAML IdP in Salesforce

To add Authgear as a SAML Identity Provider, return to the main page of Single Sign-On Settings.

<figure><img src="/files/0c7ABN1Jg3TOXhdK9kCG" alt=""><figcaption></figcaption></figure>

Next, click on the **New from Metadata File** button. Then, click Choose file, and select the Metadata XML file you downloaded in [Step 3](#step-3-get-saml-identity-provider-idp-configuration-and-download-certificate). Click on Create to continue.

On the next screen, you should see configurations for your new SAML IdP, including the details from the metadata file. Edit the **Name** field to Authgear SAML. The value for the Name field will be visible on your Salesforce login page.

Next, click on the Choose file button next to Identity Provider Certificate then select the SAML IdP Certificate file you downloaded in [Step 3](#step-3-get-saml-identity-provider-idp-configuration-and-download-certificate) from your computer.

<figure><img src="/files/Mqi6Q4uF1EWifM09jCUl" alt=""><figcaption></figcaption></figure>

Once you're done with the above configurations, click **Save** to finish.

### Step 6: Enable Login with SAML in Salesforce

In order to log in with a specific SAML IdP, you must first enable the provider in Salesforce.

To enable your new SAML IdP (Authgear SAML) search for "my domain" in Quick find. Click on My Domain from the result. On the My Domain page, scroll to **Authentication Configuration** then click on the Edit button.

<figure><img src="/files/3BqyQGHuUd7dGjrw6eqt" alt=""><figcaption></figcaption></figure>

You should find your Authgear SAMP IdP under **Authentication Service**. Check the box next to Authgear SAML to enable it. Click **Save** to keep changes.

Once you have enabled your SAML Identity provider in this step, it will be visible on your Salesforce login page the next time you attempt login.

### Step 7: Log in to Salesforce using Authgear SAML

Force you can log in to your Salesforce project using Authgear SAML, you need to create a user using an email address that is linked to a user account in your Authgear project.

To create a user in Salesforce, use type "users" in the Quick find search box, then select **Users** > **Users** from the result. This will take you to the All Users page.

From the All Users page, click on **New User**. Create a new user with an email address that's linked to an account on your Authgear project.

<figure><img src="/files/QCRIW7EFX2yri6Wsjyol" alt=""><figcaption></figcaption></figure>

Now, to test your SAML implementation, log out of Salesforce and attempt logging in again. This time, you should see a Login with Authgear SAML button.

When you click on Login with Authgear SAML, you should be redirected to Authgear Login page. Login to the account on your Authgear project that has the same email address as the new user you created earlier in this step. You should be successfully logged in to your Salesforce project.

<figure><img src="/files/FpmU1ctJOsAnJ7EY7sXt" alt=""><figcaption></figcaption></figure>


# Use Authgear as SAML Identity Provider for Dropbox

Guide on how to use Authgear as a SAML IdP for Dropbox

## Use Authgear as SAML Identity Provider for Dropbox

Security Assertion Markup Language (SAML) is a standard that allows an Identity Provider (IdP) and a Service Provider (SP) to perform user authentication and authorization without exchanging a user's password.

In this post, you'll learn how to set up Authgear as an Identity Provider and Dropbox as a Service Provider.

### Prerequisites

* An Authgear account. Sign up for free.
* A Dropbox Business Advanced account.

### Step 1: Create an Authgear Client Application

You need an Authgear client application of type `OIDC/SAML Client Application` to use Authgear as a SAML identity provider.

To create a new client application, log in to Authgear Portal, select your project then click on the **Applications** link from the navigation menu.

<figure><img src="/files/2QrIMBEwyQcmJljmA76b" alt=""><figcaption></figcaption></figure>

Next, click on **Add Application** to create a new client application. Alternatively, select an existing application of type `OIDC/SAML Client Application` and skip to step 2.

On the New Application page, enter **Name** and select `OIDC/SAML Client Application` as the **Application Type**.

<figure><img src="/files/JiCwxn1BlP2EYYRBvz9A" alt=""><figcaption></figcaption></figure>

Click **Save** to proceed.

### Step 2: Enable SAML 2.0 in Client Application

By default, SAML 2.0 is not enabled for the client application.

To enable SAML for your client application, click on the **SAML 2.0** tab then toggle the **SAML 2.0 Support** switch on.

<figure><img src="/files/oJggfgski2rTFdV9SkE9" alt=""><figcaption></figcaption></figure>

Next, change **NameID Format** to `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`.

Add the following URL (Dropbox post-back URL) in **Allowed Assertion Consumer Service URLs (ACS URLs)** field:

```
https://www.dropbox.com/saml_login
```

Click on **Save** to keep your changes.

### Step 3: Get SAML IdP Configuration and Download Certificate

Scroll down to the Configuration Parameters section of your Authgear client application's SAML 2.0 tab. Note the value for the login URL.

Also, download the **Identity Provider Certificate** for the client application to your computer.

You'll use the Login URL and certificate later in the Dropbox Admin console.

<figure><img src="/files/F8yohfKSnzNcUF5lpIYi" alt=""><figcaption></figcaption></figure>

### Step 4: Add Authgear SAML IdP in Dropbox

Log in to your Dropbox Business Advanced account then click on **Admin console** in the navigation menu.

<figure><img src="/files/oaG15kRLwdw1tTAjPYvp" alt=""><figcaption></figcaption></figure>

In the Dropbox Admin console navigate to **Settings** > **Single sign-on**.

<figure><img src="/files/w3LC0q0s4IbrWjcDCRVT" alt=""><figcaption></figcaption></figure>

Now in the Dropbox Single sign-on settings page, set Single sign-on to **Required**. Then, configure the following:

* Paste the value for your Authgear client application's `Login URL` in the `Identity provider sign-in URL` field.
* Under **X.509 certificate**, click on the Certificate upload button, then upload the Identity Provider Certificate you downloaded from your Authgear client application in the previous step.

<figure><img src="/files/r3x88hWE9DCVU3VLL40c" alt=""><figcaption></figcaption></figure>

### Step 5: Login to Dropbox Using Authgear SAML

To test your SAML implementation, you need to add a new user to your Dropbox with an email address that is associated with a user account in your Authgear project.

To add a new user to your Dropbox, navigate to **Admin console** > **People** > **Members** > **Invite member**.

Accept the invite for the new user, and try to log in to Dropbox using the registered email address for the new user. You should be redirected to the Authgear SAML login page. On successful login to the Authgear account, you should be redirected and signed in to Dropbox.


# SAML Attribute Mapping

By default, Authgear includes only the `sub` field in the SAML assertion. SAML Attribute Mapping lets you add other UserInfo fields, such as email, phone number, or name, so your SAML service provider receives the user data it needs.

You can map fields directly from the user profile, or transform them with a Go text template before they go into the assertion.

### How attribute mapping works

Each mapping has two parts:

* **Definition**: declares a SAML attribute by name. This becomes the `Name` of the `<Attribute>` element in the assertion.
* **Mapping**: sets the value of that attribute, either from a UserInfo field or from a text template.

### Enable SAML Attribute Mapping

#### Prerequisites

* A client application with **SAML 2.0 support** enabled. See [Integration by SAML](/get-started/single-sign-on-with-saml) to set one up.

#### Steps

1. Open the Authgear Portal and go to **Advanced > Edit Config**.
2. Find your application under `saml.service_providers` by matching its `client_id`.
3. Add an `attributes` block under that service provider with `definitions` and `mappings`. Use the example below as a starting point.
4. Save the config.

#### Example

Add the `attributes` block under the service provider matching your `client_id`. Leave the existing fields (`acs_urls`, `audience`, `nameid_format`, etc.) untouched, they were set when the SAML application was created.

```yaml
saml:
  service_providers:
  - client_id: YOUR_CLIENT_ID
    # ...existing fields, leave as-is...
    attributes:
      definitions:
      - name: family_name
      - name: given_name
      - name: placeholder_email
      mappings:
      - from:
          user_profile:
            pointer: /given_name
        to:
          saml_attribute: given_name
      - from:
          user_profile:
            pointer: /family_name
        to:
          saml_attribute: family_name
      - from:
          text_template:
            template: '{{.preferred_username}}@example.com'
        to:
          saml_attribute: placeholder_email
```

The `attributes` block adds three attributes: `family_name`, `given_name`, and `placeholder_email` to every SAML assertion issued to this service provider.

### Configuration reference

#### `definitions`

Each entry declares one SAML attribute.

| Field           | Type   | Required | Description                                                                                                                    |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `name`          | string | Yes      | The attribute name. Used as the `Name` of the `<Attribute>` element in the assertion.                                          |
| `name_format`   | string | No       | Sets the `NameFormat` attribute. See [SAML Core 2.7.3.1](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf). |
| `friendly_name` | string | No       | Sets the `FriendlyName` attribute.                                                                                             |

#### `mappings`

Each mapping sets the value of one declared attribute. Two source types are supported.

**Map from a user profile field**

Use a JSON pointer to reference any field in the UserInfo object.

```yaml
- from:
    user_profile:
      pointer: /email
  to:
    saml_attribute: email
```

| Field                       | Description                                                                                      |
| --------------------------- | ------------------------------------------------------------------------------------------------ |
| `from.user_profile.pointer` | JSON pointer to a UserInfo field, e.g. `/sub`, `/email`, `/phone_number`, `/preferred_username`. |
| `to.saml_attribute`         | Name of the target SAML attribute. Must match one declared in `definitions`.                     |

The SAML value type is derived from the JSON type: strings become `xs:string`, numbers become `xs:decimal`, booleans become `xs:boolean`, and arrays produce multiple `<AttributeValue>` elements.

{% hint style="info" %}
A field referenced by `pointer` must exist in the UserInfo. Missing fields produce an attribute with no `<AttributeValue>`.
{% endhint %}

**Map from a text template**

Use a [Go text template](https://pkg.go.dev/text/template) to build the value. The template receives the UserInfo object as its context.

```yaml
- from:
    text_template:
      template: '{{.preferred_username}}@example.com'
  to:
    saml_attribute: placeholder_email
```

The output is always rendered as `xs:string`. Missing fields render as empty strings.

{% hint style="warning" %}
Mapping order matters. If two mappings write to the same `saml_attribute`, the later one overrides the earlier one.
{% endhint %}


# Authentication

Implement Authgear to control access to your applications

Authgear simplifies the use of open industry standards like OAuth 2.0, and OIDC. Users can log into your applications with a variety of user login options. This set of how-to guides provides you with detailed instructions, code snippets, and configuration examples for each type of login method.


# Passwords

Configure settings for both Primary and Secondary passwords

## Primary and Secondary Passwords

In Authgear, a password can be Primary or Secondary. Secondary passwords can be enabled as part of Two Factor Authentication (2FA).

The password policies for Primary and Secondary passwords are **not** configured independently. Password policy settings apply to both types.

To configure your project's password policy, simply navigate to the password settings, and follow the corresponding guides.

## Primary password settings <a href="#pw-settings" id="pw-settings"></a>

1. Navigate to **Authentication** > **Login Methods**.
2. Select your **Login Method**. The login method selected determines the options available to customize.
3. Click on the **Passwords** tab to reveal the settings.

<figure><img src="/files/IVBTmFuRWUwa4EdKwepj" alt=""><figcaption></figcaption></figure>

**Note**: Make sure the login method you select has password enabled (you can not view the password settings screen if you only enable Passwordless login).

## Secondary password settings

Navigate to **Authentication > 2FA.** You will be able to view the password settings if you enable "Additional Password" as a 2nd-factor authentication.

Please note that it is not recommended to use a password for both primary and secondary authentication at the same time.

<figure><img src="/files/g9E0F8IR0ZvqxCadQlUu" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
In Multi Factor Authentication, users should provide multiple different forms of identification for extra security.
{% endhint %}


# Change Forgot/Reset Password settings

Configure password reset/account recovery processes.

The Forgot/Reset Password settings tab allows you to configure the behaviour of the account recovery process for your Authgear project.

## Enable Reset Password

Select a login method and navigate to the password settings tab. The login method chosen controls how the user can reset their password:

* Phone (OTP only)
* Email (link or OTP)
* Both

If both options are available, the user will be able to choose between using phone or email for account recovery.

### Recovery via phone

You can choose how you want the OTP to be delivered. In the following example, the user will receive an OTP via "WhatsApp or SMS". This means the user can select their preferred method during reset.

<figure><img src="/files/JUinRSYYI1OkJ0opYrN4" alt=""><figcaption></figcaption></figure>

The valid duration (in seconds) for the OTP or reset link can also be customized. This duration does **not** determine the minimum delay before resending OTPs or links.

As an example, if OTP is enabled as the recovery method, users will see the following screen to verify their identity during password reset:

<figure><img src="/files/vpWftpJSK4pAzwoBfQT5" alt=""><figcaption></figcaption></figure>

Once you're done, save your changes to enable the new configuration.


# Password Policy

Setting format and strength requirements for passwords

Authgear allows you to set a password policy for your project. This page walks through setting password requirements, password strength, keywords to exclude, and password expiry from the Authgear Portal.

You can configure your password policy in password settings.

## 1. Password Requirements

Choose a minimum character length, and use the checkboxes provided to include one or more requirements for a valid password.

<figure><img src="/files/VFCrV4vDaX43h3TDilGu" alt=""><figcaption></figcaption></figure>

To ensure your updated password policy applies to both existing and new users, toggle on "Force password change on next login". All users will be required to update their passwords if their current passwords do not meet the newly configured policy.

## 2. Password Strength

#### What is password strength?

Password strength is simply a measure of how difficult it is to guess or crack a password.

Authgear currently uses the [zxcvbn](https://github.com/dropbox/zxcvbn) password strength estimator library, which goes beyond basic requirements (like length or character variety) and uses pattern matching to recognize common insecure passwords.

<details>

<summary>How password strength is calculated in Authgear</summary>

A password is scored for how uncommon and guessable it is using the zxcvbn algorithm.

The following table shows the scores for the various minimum password strength levels in Authgear.

| Password Strength Level | Score | Description                                                                                                         |
| ----------------------- | ----- | ------------------------------------------------------------------------------------------------------------------- |
| N/A                     | -     | Totally ignore the Advance password strength score and use the Basic password policy. E.g. Minimum password length. |
| Extremely guessable     | 0     | Too guessable: risky password. (guesses < 10^3)                                                                     |
| Very guessable          | 1     | Very guessable: protection from throttled online attacks. (guesses < 10^6)                                          |
| Fair                    | 2     | Somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)                                    |
| Very unguessable        | 3     | Safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)                          |
| Extremely unguessable   | 4     | Very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)                             |

</details>

### How to set password strength for your Authgear project

Scroll down to the Advanced sub-section of the Password tab, then click select your preferred option from the **Min. password strength level** dropdown.

<figure><img src="/files/Vhol5Qma4yHxRW7iPz4u" alt=""><figcaption></figcaption></figure>

## 3. Prevent Password Reuse

Toggle on **Prevent Password Reuse** to ensure a new, unique password is set during password changes.

In the following example, the new password cannot match any password used within the 90 days, or any last 3 previously used passwords.

<figure><img src="/files/NygACbJxKxRFSO5a0dmc" alt=""><figcaption></figcaption></figure>

## 4. Keywords to be Excluded from Password

You can also disallow specific keywords in the user's password. Simply add them to the "Keywords to be excluded" field, and the admin or user will not be able to set a password containing the listed keywords.

<figure><img src="/files/5EKt99H2DABMbXwLXSI1" alt=""><figcaption></figcaption></figure>

## 5. Password Expiry

See [Password Expiry](/authentication-and-access/authentication/passwords/password-expiry)

Once you're done, remember to hit **Save** to keep your changes.


# Password Expiry

Requiring users to reset their password if they haven't logged in after specific number of days

You can set up your Authgear project such that a user's password expires after a specific number of days. When a user logs in after the password expiry date, they'll see a prompt to change their password before they're redirected back to your app.

{% hint style="info" %}
By default, password expiry is turned **off** for your Authgear project. [Recent security research](https://www.ncsc.gov.uk/blog-post/problems-forcing-regular-password-expiry) shows that forcing users to change their passwords after some time can do more harm than good.
{% endhint %}

## Enable Password Expiry

Navigate to the password settings tab and scroll to the **Password Expiry** section. Toggle the "**Force password change on next login if it has expired"** button to enable password expiry.

<figure><img src="/files/wzedJUpALtKpWjD2fzUX" alt=""><figcaption></figcaption></figure>

## Set Expiry Date

You can use the field **Force change since last update (days)** to specify the number of days after which a user's password should expire.

For example, setting the value to 90 means the user's password will expire 90 days after the day they set or updated their password.

Once you're done, hit the **Save** button to keep your changes.


# How to Handle Password While Creating Accounts for Users

Recommended practices for setting password for a user account created on the Authgear Portal

For creating accounts and sending users their passwords using Admin API, see `createUser` in the [Admin API documentation](https://docs.authgear.com/authentication-and-access/authentication/passwords/pages/k9OsUIA7hmhGI2gB4Ilq#id-2.3.-createuser).

In this post, we'll cover a few options and describe examples of automating the process of sending passwords to new users using the Authgear Portal or webhooks.

At this moment, Authgear only sends passwords to email accounts.

{% hint style="info" %}
For security reasons, it is not recommended to send users their passwords via text. SMS is unencrypted and insecure for transmitting passwords.
{% endhint %}

## How to create a user and send the password via email

{% stepper %}
{% step %}
Navigate to **User Management** > **Users**. From the Users page, click on the **Create User** button in the top right corner.

<figure><img src="/files/3DQrAoaSOmg7Dr7Tu9cC" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
Enter the user's credentials and input a secure password you wish to set for the user, or click on **Generate** to have Authgear automatically create a new password.

<figure><img src="/files/eAyoFmCpNWjykXLRQnA5" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
Check the "Send the password to user's email" box to enable Authgear to send the new password to the user.

If you wish to force users to change their password upon logging in with the password you set for them, enable "Ask user to change password on login" as well. Otherwise, users can continue using the password you set for them.
{% endstep %}
{% endstepper %}

## What happens if the user loses the preset password?

Users can still log in to their new account if they lose the password you set for them. They can click **Forgot Password** during login, and an OTP or link will be sent to the user. The user can then set a new password.


# Reset Password for Users

Guide on resetting a user's password via the Authgear Portal or AdminAPI

## How to reset a user's password via Authgear Portal <a href="#via-portal" id="via-portal"></a>

{% stepper %}
{% step %}
In your project, navigate to **User Management** > **Users**. Then click on the user whose password you would like to reset.

<figure><img src="/files/w4dn7s2aNQoTrxAcd4MF" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
Go to the user's **Account Security** tab, and click on Change Password. You can also require the user to change their password on login by enabling the **Ask to change password on login** toggle.

<figure><img src="/files/wKt2PVc32gSO0Euk6cNX" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
You can either enter a secure password you wish to set for the user in the **Password** field, or click on **Generate** to have Authgear automatically create a new password for the user.

Check the "**Send the password to user's email**" box to enable Authgear to send the password entered in the **Password** field to the user.

If you wish to force users to change their password upon logging in with the password you set for them, enable "Ask user to change password on login". Otherwise, users can continue using the password you set for them.

<figure><img src="/files/bX5u9R4QCZSgP9iOHpyg" alt=""><figcaption></figcaption></figure>

Finally, remember to click **Change** to save your changes.
{% endstep %}
{% endstepper %}

## How to reset a user's password via AdminAPI <a href="#via-portal" id="via-portal"></a>

See [resetPassword](https://docs.authgear.com/authentication-and-access/authentication/passwords/pages/k9OsUIA7hmhGI2gB4Ilq#id-2.9.-resetpassword) under Reference > APIs > Admin API > API Queries and Mutations.

## What happens if the user loses the preset password?

Users can still log in to their new account if they lose the password you set for them. They can click **Forgot Password** during login, and an OTP or link will be sent to the user. The user can then set a new password.


# Add Passkeys Login

Passkeys give users a simple and secure way to sign in to your apps and websites across platforms without passwords.

Passkeys replace passwords and other passwordless login methods. It is built on the WebAuthn standard (also known as FIDO Sign-in), which uses public key cryptography to authenticate the user. With 1 click, Authgear upgrades your app to support this cutting-edge auth technology.

#### Platform support and Multi-devices

The passkey standard is supported on the latest versions of Chrome, Safari, and Firefox browsers. On iOS 16 and macOS 13 (Ventura), [Apple has added passkey support](https://developer.apple.com/passkeys/) to the iCloud Keychain service. Passkeys are also [supported on Android 9](https://developers.google.com/identity/passkeys/supported-environments#android-passkey-support) (API level 28) or higher. A passkey is synchronized and relayed with an iCloud account and can be used across a user's devices.

Users can log in to their accounts using their biometrics easily. On Apple devices, Touch ID and Face ID authorize the use of the passkey which then authenticates the user on the app or website.

#### Hardware security keys

Besides the built-in support of all major desktop and mobile platforms, passkeys can also be stored in hardware security keys such as [YubiKeys](https://www.yubico.com/blog/passkeys-and-the-future-of-modern-authentication/), which provide the highest security against attacks.

## Add Passkeys to your apps with Authgear

Authgear adds a passkey feature to your apps and websites instantly. To enable it:

1. In your project portal, go to **Authentication > Login Methods**.
2. In the **Select Login Methods** section, turn on the **Enable passkey support for compatible devices.** toggle.
3. Press "Save" and your app now supports passkey login!

{% hint style="info" %}
It will take time for the passkey technology to be available on everyone's devices. In the transition stage, it is recommended to enable "Password" or "Passwordless via Email/Phone" in your project so users with non-compatible devices can access your app.

If you want to use ONLY passkeys in your app, it's perfectly supported too! Select **Custom** and deactivate all authenticators in the **Custom Login Methods** tab. Remember to keep **Enable Passkey support for compatible devices.** turned on.
{% endhint %}

## Support on different platforms

See the list of Passkey support via Authgear on different platforms.

* macOS 12: Passkey is supported on major browsers. However, the credentials are deleted when clearing browser data.
* iOS 15.5: Passkey is supported on Safari and stored locally a the device. Credential will be deleted by "Settings > Safari > Clear History and Website Data"
* iOS 16 Beta 3: Passkey is synced with iCloud Keychain. The individual credentials can be viewed and managed in "Settings > Passwords"
* Android 9 (API level 28) or higher: Supported.


# Add WhatsApp OTP Login

Allow users to log into your app via OTP with WhatsApp, as a secure alternative to SMS

## Enable WhatsApp OTP in your project

Authgear let your users login passwordlessly with WhatsApp OTP.

To enable this feature from the Portal:

1. Go to **Authentication > Login Methods**, we are going make few changes on this page.
2. In the top section of **Select Login Methods**, select **Mobile**.
3. In **Authentication** of **Select Login Methods**, select **Passwordless**.
4. In the tabs section below, switch to the tab **Verification and OTP**.
5. In the dropdown **Verify phone number by**, select either **WhatsApp or SMS** or **WhatsApp only**.
6. Press **Save** on the top left corner.

When the user login with their phone number, a WhatsApp message with an OTP will be received. They can copy the code by tapping on the "Copy code" button and log in by the code.

If "**Verify phone number by WhatsApp or SMS**" is enabled, the user can switch to receive the OTP via SMS instead in the login page.

## Set up your own WhatsApp Business account

By default, WhatsApp OTP are sent via a shared Authgear sender. If you want them to be sent with your own WhatsApp numbers, you need a WhatsApp Business account and a Meta App. This guide shows you how to set up a WhatsApp Business number for Authgear OTP authentication.

You will need an unused phone number, and a Facebook account.

### Step 1: Create a business portfolio

{% hint style="warning" %}
Skip this if you already have an existing business portfolio
{% endhint %}

1. Go to [business.facebook.com](https://business.facebook.com/) in a desktop browser. You will also use this address to log into Meta Business Suite. If you already have access to Meta Business Suite, you can also click the dropdown menu located at the top of the left menu. Then skip to Step 4.
2. Click **Create account**.
3. Log into your personal Facebook account. If you don’t have an account, click **Create account** to sign up for one.
4. Click **Create a business portfolio**.
5. Enter your business details.
   * **Business portfolio name**. It should match the public name of your business or organization, since it will be visible across Meta. It can't contain special characters.
   * **Your name**.
   * **Business email**. Meta will use this email to contact you about your business. It won't be visible to your customers.
6. Click **Submit** or **Create** to create your portfolio. You’ll get an email asking to confirm your business email address.

### Step 2: Register Phone number as a new WhatsApp Business Account

1. Go to “Settings” in Meta Businss Suite
2. Under “Accounts”, go to “WhatsApp accounts” and then “Create a new WhatsApp Business account”<br>

   <figure><img src="/files/x8vIMehjW71PLKaJZ6yc" alt=""><figcaption></figcaption></figure>
3. Enter the display name and set up the profile image, which will be shown to your users
4. Use a unregistered phone number for the account, Follow the on-screen instructions to verify the phone number
5. Set up payment method in this business portfolio and link the WhatsApp account with the payment method.

### Step 3: Set up Message template

1. Go to “WhatsApp Manager” in Meta Business Suite, if the item is not available in the nav bar, search WhatsApp in “All tools”
2. Go to “Manage templates” and “Create Template”, make sure the correct WhatsApp business account is selected on the top right dropdown.<br>

   <figure><img src="/files/HsqreYqePrXJOQljM8au" alt=""><figcaption></figcaption></figure>
3. Select “Authentication” type<br>

   <figure><img src="/files/QTV81r45nSj3aqYpFTh3" alt=""><figcaption></figcaption></figure>
4. For each language you wish to support, create a template named `one_time_password`
   1. Select “Copy Code” under **Code delivery setup**
   2. Select “Add security recommendation” under **Content**
   3. Set Validity period to 10 minutes<br>

      <figure><img src="/files/ooeG4SwOG3XdUCeCls2l" alt=""><figcaption></figcaption></figure>
5. The template page should look like this after adding the required languages<br>

   <figure><img src="/files/CjxUWtTKcxJgpw8v6zoI" alt=""><figcaption></figcaption></figure>

### Step 4: Set up Application

In this step, we will create a “Meta App”

1. Go to <https://developers.facebook.com/apps/> and create a new app
2. Enter an identifier for the meta app and your contact email
3. In User cases, select “**Other**”
4. App type, select “Business”
5. Link to the business portolio created in step 1.
6. After creating the app, select “WhatsApp” in “Add product to your app”<br>

   <figure><img src="/files/iZMWoo9em1BtOr6Tuvp9" alt=""><figcaption></figcaption></figure>
7. In “API Setup”, tap “Generate access token” and select the WhatsApp account we created in Step 2. Then click continue.\
   ![](/files/K0Hyqkk269Nx8mdOOypv)
8. Under “From” dropdown, select the phone number we registered in Step 2. Note the “**Phone number ID**” and “**WhatsApp Business Account ID**”. Here if your enter a valid “To” number, you should be able to receive a testing “Hello World” message.<br>

   <figure><img src="/files/nn9pqtECnBTuox6rNj35" alt=""><figcaption></figcaption></figure>
9. Go to “App Settings”, add “Privacy Policy” link; and note the **"App ID"**, and **"App secret"** on this page.<br>

   <figure><img src="/files/2Y5BQV3ZHjmz0djs5bt0" alt=""><figcaption></figcaption></figure>
10. Switch App mode to “**Live**” (You may be asked to enter business details for review.)

### Step 5: Set up “System User”

1. Go back to **Meta Business Suite** [business.facebook.com](http://business.facebook.com)
2. Go to “Settings” > “Users” > “System users”
3. Add a new system user
4. Assign the App we created in Step 4 with “Develop app” access
5. Assign the WhatsApp account created in Step 2 with "Message templates (view only)” and “Phone Numbers (view and manage)” access
6. Click “**Generate token**” and note the token created.

{% hint style="success" %}
Now, give Authgear team the following items:

* **Phone number ID**
* **WhatsApp Business Account ID**
* **Access Token of the System user**
* **App ID**
* **App Secret**

And we will help you set up the connection in the backend
{% endhint %}

### Step 6: Set up Automatic SMS Fallback

To support automatic fallback to SMS when the WhatsApp delivery failed, we will need to set up a webhook between Meta and the Authgear server to check the delivery status of messages.

{% hint style="info" %}
You can share app access with the Authgear Team for assistance with this step.
{% endhint %}

1. Go back to <https://developers.facebook.com/apps/> and manage the app we created
2. Under WhatsApp, go to “Configuration”
3. Fill in the
   1. Callback URL: `https://{authgear_endpoint}/whatsapp/webhook`
   2. Verify token: A random string
      1. e.g. Use `openssl rand -hex 16` command to generate a 32-character sequence
4. Share the **“Verify token”** to the Authgear Team for backend configuration
5. Press “Verify and save”<br>

   <figure><img src="/files/5ofF5UWEs70dPeDbbdqf" alt=""><figcaption></figcaption></figure>
6. In The “Webhooks” page, select “WhatsApp Business Account” under Product, and then enable the `messages` webhook subscription.<br>

   <figure><img src="/files/Z26IJaXrsUrA0ZQpLQhu" alt=""><figcaption></figcaption></figure>
7. Subscribe to the WhatsApp Business Account
   1. This step is required to let your facebook app to receive notifications from your whatsapp business account.\
      Note, you must be logged in to an account with access to both your Facebook app and Whatsapp Business account to perform these steps:
      1. Go to <https://developers.facebook.com/tools/explorer>
      2. In "Meta App", select your facebook app
      3. In User or Page, select "User Token"
      4. In "Add a permission", select:
         * whatsapp\_business\_management
         * whatsapp\_business\_messaging
      5. Press Generate Access Token
      6. Then, type `{WABA_ID}/subscribed_apps` as the request path. Replace `{WABA_ID}` with your whatsapp business account ID.
      7. Change the request method to `POST`.
      8. Press "Submit"
      9. Change the request method to `GET`, submit and confirm a new item is created. You should an item like this:

```json
{
  "whatsapp_business_api_data": {
    "link": "https://www.facebook.com/games/?app_id=1234567890987654",
    "name": "YOUR APP NAME",
    "id": "1234567890987654"
  }
},
```

{% hint style="success" %}
Now, give Authgear team the following items:

* **Phone number ID**
* **WhatsApp Business Account ID**
* **Access Token of the System user**
* **App ID**
* **App Secret**
* **Verify Token**

And we will help you set up the connection in the backend
{% endhint %}


# Add Email Magic Link Login

Passwordless login with email links

Email Login Links, also known as "**magic link**", is a passwordless authentication method that allows users to log into a website or application without using a traditional password. Instead, it relies on a unique link sent to the user's email address.

Here's how it works:

1. User initiates the login process by entering their email address on the login page.
2. Authgear generates a unique, time-limited login link associated with the user's email address.
3. The link is sent to the user's email inbox, with a button prompting them to click on it to log in.
4. The user clicks on the link, and approve the login
5. The user is securely logged in to the app or website.

Magic link login offers several advantages. It eliminates the need for users to remember and manage passwords, reducing the risk of weak or reused passwords. It also simplifies the login process and reduces friction, as users only need to access their email to authenticate.

To enable Email Login Links:

1. In the Authgear Portal, go to "**Authentication**" > "**Login Methods**"
2. Select "**Email**" or "**Mobile/Email**" as login methods
3. Go to the "**Verification and OTP**" tab
4. Under "**Email**", in the "**Verify email by**" field, select "**Login Link**"

<figure><img src="/files/shEWoUodv2ai0G9vc579" alt=""><figcaption></figcaption></figure>


# Add Biometric Login

## Overview

{% hint style="info" %}
Biometric login is supported for the following operating systems:

* iOS 11.3 or higher
* Android 6.0 (API 23) or higher
  {% endhint %}

Authgear supports enabling biometric login in the native mobile application. You will need to

1. Enable biometric login in your application via the portal.
2. In the mobile app, use the mobile SDK to enable biometric login for your users.

A pair of cryptographic keys will be generated upon registering biometric login. The private key will be stored securely in the device (using Keystore in Android and Keychain in iOS), while the public key is stored in the Authgear server. To authenticate the user, fingerprint or face is presented to unlock the private key, and a digital signed message is sent to the server to proof the authenticity of the user.

**Fig 1.0. The following figure shows the sequence for enabling Biometric Login on a supported device:**

<figure><img src="/files/pC1TdhiyLjl1yPZchS3w" alt=""><figcaption><p>Enable Biometric Login</p></figcaption></figure>

The Client App that is already logged in to a user's account will check if biometrics is supported by the user's device. If the device supports biometric login, it is then enabled. The public key is sent to Authgear server and associated with the logged-in user's account.

The flow is then completed and biometric login is enabled for the user on the Client App.

**Fig 2.0. The following figure shows the sequence for a user logging in with Biometric:**

<figure><img src="/files/FrkeoalFaQH1jDI27Wi0" alt=""><figcaption><p>Logging in with Biometric</p></figcaption></figure>

With biometric login already enabled for the user, the next time they need to log in they can initiate a biometric authentication flow which will follow the sequence shown in Fig 2.0 above. Once the biometric login is successful, Authgear server will return an access token and a refresh token. The client application can then use the access token to make authenticated requests.

Sounds overwhelming? Authgear's magic handles all these for you. Follow this guide to enable biometric login with a few lines of code in your app.

## Enable biometric authentication for your project

1. In the portal, go to **Authentication > Biometric**.
2. Turn on **Enable biometric authentication**.
3. **Save** the settings.

## Set reasonably short token lifetimes for client applications

Biometric login is usually used when you want the user to re-login after a relatively short period of time. For sensitive applications such as financial apps, it's ***recommended*** to use a short refresh token lifetime and a short idle timeout.

1. In the Authgear Portal, go to **Applications**
2. Select the client application that represent the integration with the mobile app
3. Set a short **Refresh Token Lifetime** to say 3,600 seconds (1 hour)
4. Enable **Expire after idling**
5. Set a short **Idle Timeout**, to say 1,800 seconds (30 minutes)

By doing so, the end-user's session will be expired 1 hour after their login, or after 30 minutes of inactivity. The end-user will need to authenticate themself again with biometric, even if the app process has not yet been killed.

## Configure SDK so users must re-login after app closed

Apart from the short token lifetimes, it's also common for sensitive apps to ask the user to re-login by biometric after the app process is killed and relaunched.

The SDK should be configured to use TransientTokenStorage so the tokens are stored in memory, and will be cleared when the app is closed. So the end-users must authenticate with biometrics again.

{% tabs %}
{% tab title="iOS" %}
{% code fullWidth="true" %}

```swift
let authgear = Authgear(
    clientId: "{your_client_id}", 
    endpoint: "{your_app_endpoint}",
    tokenStorage: TransientTokenStorage())
authgear.configure() { result in
    switch result {
    case .success():
        // configured successfully
    case let .failure(error):
        // failed to configured
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Android" %}

```kotlin
public class MyAwesomeApplication extends Application {
    // The client ID of the oauth client.
    private static final String CLIENT_ID = "a_random_generated_string"
    // Deployed authgear's endpoint
    private static final String AUTHGEAR_ENDPOINT = "http://<myapp>.authgear.cloud/"
    private Authgear mAuthgear;
    public void onCreate() {
        super.onCreate();
        mAuthgear = new Authgear(this, CLIENT_ID, AUTHGEAR_ENDPOINT, new TransientTokenStorage());
        mAuthgear.configure(new OnConfigureListener() {
            @Override
            public void onConfigured() {
                // Authgear can be used.
            }

            @Override
            public void onConfigurationFailed(@NonNull Throwable throwable) {
                Log.d(TAG, throwable.toString());
                // Something went wrong, check the client ID or endpoint.
            }
        });
    }

    public Authgear getAuthgear() {
        return mAuthgear;
    }
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import React, { useCallback } from "react";
import { View, Button } from "react-native";
import authgear, { TransientTokenStorage } from "@authgear/react-native";

function LoginScreen() {
  const onPress = useCallback(() => {
    // Normally you should only configure once when the app launches.
    authgear
      .configure({
        clientID: "client_id",
        endpoint: "http://<myapp>.authgear.cloud",
        tokenStorage: new TransientTokenStorage()
      })
      .then(() => {
        authgear
          .authenticate({
            redirectURI: "com.myapp.example://host/path",
          })
          .then(({ userInfo }) => {
            console.log(userInfo);
          });
      });
  }, []);

  return (
    <View>
      <Button onPress={onPress} title="Authenticate" />
    </View>
  );
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
Future<void> _init() async {
    _authgear = Authgear(
        endpoint: "ENDPOINT", 
        clientID: "CLIENT_ID", 
        tokenStorage: TransientTokenStorage()
    );
    await _authgear.configure();
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
import authgearCapacitor, { TransientTokenStorage, CancelError as CapacitorCancelError } from "@authgear/capacitor";
import authgearWeb, { SessionState, UserInfo, CancelError as WebCancelError } from "@authgear/web";
import { Capacitor } from "@capacitor/core";
import { useCallback, useState } from "react";

function isPlatformWeb(): boolean {
    return Capacitor.getPlatform() === "web";
}

const CLIENT_ID = "client_id";
const ENDPOINT = "http://<myapp>.authgear.cloud";

function AuthenticationScreen() {

    const [isAlertOpen, setIsAlertOpen] = useState(false);
    const [alertHeader, setAlertHeader] = useState("");
    const [alertMessage, setAlertMessage] = useState("");
    const [loading, setLoading] = useState(false);
    const [initialized, setInitialized] = useState(false);

    const [sessionState, setSessionState] = useState<SessionState | null>(() => {
        if (isPlatformWeb()) {
            return authgearWeb.sessionState;
        }
        return authgearCapacitor.sessionState;
    });

    const showError = useCallback((e: any) => {
        const json = JSON.parse(JSON.stringify(e));
        json["constructor.name"] = e?.constructor?.name;
        json["message"] = e?.message;
        let message = JSON.stringify(json);

        if (e instanceof WebCancelError || e instanceof CapacitorCancelError) {
            // Cancel is not an error actually.
            return;
        }

        setIsAlertOpen(true);
        setAlertHeader("Error");
        setAlertMessage(message);
    }, []);

    const postConfigure = useCallback(async () => {
        const sessionState = isPlatformWeb()
            ? authgearWeb.sessionState
            : authgearCapacitor.sessionState;
        if (sessionState !== "AUTHENTICATED") {
            setInitialized(true);
            return;
        }

        if (isPlatformWeb()) {
            await authgearWeb.fetchUserInfo();
        } else {
            await authgearCapacitor.fetchUserInfo();
        }

        setInitialized(true);
    }, []);

    const configure = useCallback(async () => {
        setLoading(true);
        try {

            if (isPlatformWeb()) {
                await authgearWeb.configure({
                    clientID: CLIENT_ID,
                    endpoint: ENDPOINT,
                    sessionType: "refresh_token",
                    isSSOEnabled: false,
                });
            } else {
                await authgearCapacitor.configure({
                    clientID: CLIENT_ID,
                    endpoint: ENDPOINT,
                    tokenStorage: new TransientTokenStorage()
                });

            }
            await postConfigure();
        } catch (e) {
            showError(e);
        } finally {
            setLoading(false);
        }
    }, [
        CLIENT_ID,
        ENDPOINT
    ]);
}
```

{% endtab %}
{% endtabs %}

## Enable biometric login in mobile SDK

In the following section, we will show you how to use biometric login in the SDK. In the SDK code snippet, `authgear` is referring to the configured Authgear container.

### Biometric options

Customize the biometric options to achieve the expected user experience.

#### iOS

There are 4 options on iOS:

* `localizedReason` is the custom message to explain to the user why TouchID or FaceID is required.
* `localizedCancelTitle` (optional) customizes the cancel button label.
* `policy` constraints how the user is authenticated locally.
  * [`deviceOwnerAuthenticationWithBiometrics`](https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthenticationwithbiometrics): The user MUST use TouchID or FaceID. This also implies the device must have TouchID or FaceID already set up. See also [#error-handling](#error-handling "mention")
  * [`deviceOwnerAuthentication`](https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthentication): If the device has TouchID or FaceID set up, it is used first. Otherwise, the device passcode is used. This also implies the device must have a passcode. See also [#error-handling](#error-handling "mention")
  * It refers to the `LAPolicy` enum on iOS, see [reference in Apple Developers Doc on these options](https://developer.apple.com/documentation/localauthentication/lapolicy).
* `constraint` is an enum that constraint the access of key stored under different conditions:
  * [`biometryCurrentSet`](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/biometrycurrentset): The biometric login will be invalidated if the device has any changes to TouchID or FaceID. Changes include adding, or removing, re-enrolling any fingerprints or faces.
  * [`biometryAny`](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/biometryany): The biometric login stays valid even if the device has any changes to TouchID or FaceID.
  * [`userPresence`](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/userpresence): Either biometry or device passcode/PIN can be used to access the private key.
  * See [reference in Apple Developers Doc on these options](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags).

In summary, based on the desired behavior and business requirements, set the policy and constraint options as below.

<table><thead><tr><th width="195.256103515625">Requirement</th><th width="359.66845703125">Policy</th><th>Constraint</th></tr></thead><tbody><tr><td>Sign in with any currently set biometry. Re-enrollment causes reset.</td><td><code>deviceOwnerAuthenticationWithBiometrics</code></td><td><code>BiometryCurrentSet</code></td></tr><tr><td>Sign in with any biometry. Re-enrollment does not cause reset.</td><td><code>deviceOwnerAuthenticationWithBiometrics</code></td><td><code>biometryAny</code></td></tr><tr><td>Sign in with either biometry, or device passcode</td><td><code>deviceOwnerAuthentication</code><br></td><td><code>userPresence</code></td></tr></tbody></table>

#### Android

There are 6 options on Android:

* `title` is the Title of the biometric dialog presented to the users
* `subtitle` is the subtitle of the biometric dialog presented to the users
* `description` is the description of the biometric dialog presented to the users
* `negativeButtonText` is what the dismiss button says in the biometric dialog
* `allowedAuthenticatorsOnEnable` is an array that defines the requirement of security level when the user enable biometric, which can be [`BIOMETRIC_STRONG`](https://developer.android.com/reference/android/hardware/biometrics/BiometricManager.Authenticators#BIOMETRIC_STRONG) or [`DEVICE_CREDENTIAL`](https://developer.android.com/reference/android/hardware/biometrics/BiometricManager.Authenticators#DEVICE_CREDENTIAL).
* `allowedAuthenticatosOnAuthenticate` is an array that defines the requirement of security level when the user authenticates with biometric.
* [`invalidatedByBiometricEnrollment`](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment\(boolean\)) is a boolean that controls if the key pair will be invalidated if a new biometric is enrolled, or when all existing biometrics are deleted.

In summary, based on the desired behavior and business requirements, set `allowedAuthenticatorsOnEnable` and `allowedAuthenticatosOnAuthenticate` as below.

| Requirement                                                                                           | `allowedAuthenticatorsOnEnable`         | `allowedAuthenticatosOnAuthenticate`    |
| ----------------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------- |
| Biometric is required during enable and subsequent biometric authentication.                          | `[BIOMETRIC_STRONG]`                    | `[BIOMETRIC_STRONG]`                    |
| Biometric is required during enable. Passcode is also allowed in subsequent biometric authentication. | `[BIOMETRIC_STRONG]`                    | `[BIOMETRIC_STRONG, DEVICE_CREDENTIAL]` |
| Passcode is allowed during enable and subsequent biometric authentication.                            | `[BIOMETRIC_STRONG, DEVICE_CREDENTIAL]` | `[BIOMETRIC_STRONG, DEVICE_CREDENTIAL]` |

### Code examples

#### Check support

Always check if the current device supports biometric login before calling any biometric API, including before enabling biometric login and before using biometrics to login.

{% tabs %}
{% tab title="iOS" %}

```swift
// check if current device supports biometric login
var supported = false
do {
    try authgear.checkBiometricSupported(policy: .deviceOwnerAuthenticationWithBiometrics)
    supported = true
} catch {}

if supported {
    // biometric login is supported
}
```

{% endtab %}

{% tab title="Android" %}

```java
boolean supported = false;
try {
    // biometric login is supported SDK_INT >= 23 (Marshmallow)
    if (Build.VERSION.SDK_INT >= 23) {
        // check if current device supports biometric login
        authgear.checkBiometricSupported(
                this.getApplication(),
                List.of(BiometricAuthenticator.BIOMETRIC_STRONG)
        );
        supported = true;
    }
} catch (Exception e) {}
if (supported) {
    // biometric login is supported
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
const biometricOptions = {
  ios: {
    localizedReason: 'Use biometric to authenticate',
    localizedCancelTitle: 'Cancel',
    constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
    policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
  },
  android: {
    title: 'Biometric Authentication',
    subtitle: 'Biometric authentication',
    description: 'Use biometric to authenticate',
    negativeButtonText: 'Cancel',
    allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.BiometricStrong],
    allowedAuthenticatosOnAuthenticate: [BiometricAuthenticatorAndroid.BiometricStrong],
    invalidatedByBiometricEnrollment: true,
  },
};
// check if current device supports biometric login
authgear
    .checkBiometricSupported(biometricOptions)
    .then(() => {
        // biometric login is supported
    })
    .catch(() => {
        // biometric login is not supported
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
// We will need the options for the other biometric api
final ios = BiometricOptionsIOS(
    localizedReason: "Use biometric to authenticate",
    localizedCancelTitle: "Cancel",
    constraint: BiometricAccessConstraintIOS.biometryAny,
    policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
);
final android = BiometricOptionsAndroid(
    title: "Biometric Authentication",
    subtitle: "Biometric authentication",
    description: "Use biometric to authenticate",
    negativeButtonText: "Cancel",
    allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.biometricStrong],
    allowedAuthenticatorsOnAuthenticate: [BiometricAuthenticatorAndroid.biometricStrong],
    invalidatedByBiometricEnrollment: true,
);

try {
    // check if current device supports biometric login
    await authgear.checkBiometricSupported(ios: ios, android: android);
    // biometric login is supported
} catch (e) {
    // biometric login is not supported
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
const biometricOptions: BiometricOptions = {
  ios: {
    localizedReason: "Use biometric to authenticate",
    localizedCancelTitle: "Cancel",
    constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
    policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
  },
  android: {
    title: "Biometric Authentication",
    subtitle: "Biometric authentication",
    description: "Use biometric to authenticate",
    negativeButtonText: "Cancel",
    allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.BiometricStrong],
    allowedAuthenticatosOnAuthenticate: [BiometricAuthenticatorAndroid.BiometricStrong],
    invalidatedByBiometricEnrollment: true,
  },
};

const updateBiometricState = useCallback(async () => {
    if (isPlatformWeb()) {
      return;
    }

    try {
      await authgearCapacitor.checkBiometricSupported(biometricOptions);
     //enable biometric...
    } catch (e) {
      console.error(e);
    }
  }, []);


```

{% endtab %}
{% endtabs %}

#### Enable biometric login

Enable biometric login for logged in user

{% tabs %}
{% tab title="iOS" %}

```swift
// provide localizedReason for requesting authentication
// which displays in the authentication dialog presented to the user
authgear.enableBiometric(
    localizedReason: "REPLACE_WITH_LOCALIZED_REASON",
    constraint: .biometryCurrentSet,
    policy: .deviceOwnerAuthenticationWithBiometrics,
    localizedCancelTitle: "Cancel",
) { result in
    if case let .failure(error) = result {
        // failed to enable biometric with error
    } else {
        // enabled biometric successfully
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
// We will need the options for the other biometric api
BiometricOptions biometricOptions = new BiometricOptions(
    activity, // FragmentActivity
    "Biometric authentication", // title
    "Biometric authentication", // subtitle
    "Use biometric to authenticate", // description
    "Cancel", // negativeButtonText
    List.of(BiometricAuthenticator.BIOMETRIC_STRONG), // allowedAuthenticatorsOnEnable
    List.of(BiometricAuthenticator.BIOMETRIC_STRONG), // allowedAuthenticatorsOnAuthenticate
    true // invalidatedByBiometricEnrollment
);
authgear.enableBiometric(
    biometricOptions,
    new OnEnableBiometricListener() {
        @Override
        public void onEnabled() {
            // enabled biometric login successfully
        }

        @Override
        public void onFailed(Throwable throwable) {
            // failed to enable biometric with error
        }
    }
);
```

{% endtab %}

{% tab title="React Native" %}

```javascript
authgear
    .enableBiometric(biometricOptions)
    .then(() => {
        // enabled biometric login successfully
    })
    .catch((err) => {
        // failed to enable biometric with error
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    await authgear.enableBiometric(ios: ios, android: android);
    // enabled biometric login successfully
} catch (e) {
    // failed to enable biometric with error
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
const enableBiometric = useCallback(async () => {
  setLoading(true);
  try {
    await authgearCapacitor.enableBiometric(biometricOptions);
  } catch (e: unknown) {
    showError(e);
  } finally {
    setLoading(false);
    await updateBiometricState();
  }
}, [showError, updateBiometricState]);

const onClickEnableBiometric = useCallback(
  (e: MouseEvent<HTMLIonButtonElement>) => {
    e.preventDefault();
    e.stopPropagation();

    enableBiometric();
  },
  [enableBiometric]
);
```

{% endtab %}
{% endtabs %}

#### Check if biometric has been enabled before

Before asking the user to log in with biometric, Check if biometric login has been enabled on the current device. I.e. Is the key pair exist on the device (Keystore in Android and Keychain in iOS).

This method will still return true even if all the fingerprint and facial data has been removed from the device. Before this method, you should use the "checkBiometricSupported" to check if biometry is supported in the device level.

{% tabs %}
{% tab title="iOS" %}

```swift
var enabled = (try? authgear.isBiometricEnabled()) ?? false
```

{% endtab %}

{% tab title="Android" %}

```java
boolean enabled = false;
try {
    enabled = authgear.isBiometricEnabled();
} catch (Exception e) {}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
authgear
    .isBiometricEnabled()
    .then((enabled) => {
        // show if biometric login is enabled
    })
    .catch(() => {
        // failed to check the enabled status
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    final enabled = await authgear.isBiometricEnabled();
    // show if biometric login is enabled
} catch (e) {
    // failed to check the enabled status
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
try {
   const enabled = await authgearCapacitor.isBiometricEnabled();
} catch (e) {
      console.error(e);
}
```

{% endtab %}
{% endtabs %}

#### Login with biometric credentials

If biometric is supported and enabled, you can use the Authenticate Biometric method to log the user in. If the key pair is invalidated due to changes in the biometry settings, e.g added fingerprint or re-enrolled face data, the `biometricPrivateKeyNotFound` will be thrown. You should handle the error by the Disable Biometric method, and ask the user to register biometric login again.

{% tabs %}
{% tab title="iOS" %}

```swift
authgear.authenticateBiometric(
    localizedReason: "The reason",
    policy: .deviceOwnerAuthenticationWithBiometrics
) { result in
    switch result {
        case let .success(userInfo):
            let userInfo = userInfo
            // logged in successfully
        case let .failure(error):
            // failed to login
        }
}
```

{% endtab %}

{% tab title="Android" %}

```java
authgear.authenticateBiometric(
    biometricOptions,
    new OnAuthenticateBiometricListener() {
        @Override
        public void onAuthenticated(UserInfo userInfo) {
            // logged in successfully
        }

        @Override
        public void onAuthenticationFailed(Throwable throwable) {
            // failed to login
        }
    }
);
```

{% endtab %}

{% tab title="React Native" %}

```javascript
authgear
    .authenticateBiometric(biometricOptions)
    .then(({userInfo}) => {
        // logged in successfully
    })
    .catch((e) => {
        // failed to login
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    final userInfo = await authgear.authenticateBiometric(ios: ios, android: android);
    // logged in successfully
} catch (e) {
    // failed to login
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
      const showUserInfo = useCallback((userInfo: UserInfo) => {
        const message = JSON.stringify(userInfo, null, 2);
        setIsAlertOpen(true);
        setAlertHeader("UserInfo");
        setAlertMessage(message);
      }, []);
      
      const authenticateBiometric = useCallback(async () => {
        setLoading(true);
        try {
          const { userInfo } = await authgearCapacitor.authenticateBiometric(
            biometricOptions
          );
          showUserInfo(userInfo);
        } catch (e: unknown) {
          showError(e);
        } finally {
          setLoading(false);
          await updateBiometricState();
        }
      }, [showError, showUserInfo, updateBiometricState]);
```

{% endtab %}

{% tab title="Xamrin" %}

```csharp
try
{
    var userInfo = await authgear.AuthenticateBiometricAsync(biometricOptions);
    // logged in successfully
}
catch
{
    // failed to login
}
```

{% endtab %}
{% endtabs %}

#### Disable biometric login on the current device

{% tabs %}
{% tab title="iOS" %}

```swift
do {
    try authgear.disableBiometric()
    // disabled biometric login successfully
} catch {
    // failed to disable biometric login
}
```

{% endtab %}

{% tab title="Android" %}

```java
try {
    authgear.disableBiometric();
    // disabled biometric login successfully
} catch (Exception e) {
    // failed to disable biometric login
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
authgear
    .disableBiometric()
    .then(() => {
        // disabled biometric login successfully
    })
    .catch((err) => {
        // failed to disable biometric login
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    await authgear.disableBiometric();
    // disabled biometric login successfully
} catch (e) {
    // failed to disable biometric login
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
  const disableBiometric = useCallback(async () => {
    setLoading(true);
    try {
      await authgearCapacitor.disableBiometric();
    } catch (e: unknown) {
      showError(e);
    } finally {
      setLoading(false);
      await updateBiometricState();
    }
  }, [showError, updateBiometricState]);
```

{% endtab %}
{% endtabs %}

#### Error handling

In all methods related to biometric, the SDK may throw the following errors that describe the status of the biometry enrollment or the key pair stored on the device.

{% tabs %}
{% tab title="iOS" %}

```swift
if let authgearError = error as? AuthgearError {
    switch authgearError {
    case .cancel:
        // user cancel
    case .biometricPrivateKeyNotFound:
        // biometric info has changed. e.g. Touch ID or Face ID has changed.
        // user have to set up biometric authentication again
    case .biometricNotSupportedOrPermissionDenied:
        // user has denied the permission of using Face ID
    case .biometricNoPasscode:
        // device does not have passcode set up
    case .biometricNoEnrollment:
        // device does not have Face ID or Touch ID set up
    case .biometricLockout:
        // the biometric is locked out due to too many failed attempts
    default:
        // other error
        // you may consider showing a generic error message to the user
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
import com.oursky.authgear.BiometricLockoutException;
import com.oursky.authgear.BiometricNoEnrollmentException;
import com.oursky.authgear.BiometricNoPasscodeException;
import com.oursky.authgear.BiometricNotSupportedOrPermissionDeniedException;
import com.oursky.authgear.BiometricPrivateKeyNotFoundException;
import com.oursky.authgear.CancelException;


if (e instanceof CancelException) {
    // user cancel
} else if (e instanceof BiometricPrivateKeyNotFoundException) {
    // biometric info has changed
    // user have to set up biometric authentication again
} else if (e instanceof BiometricNoEnrollmentException) {
    // device does not have biometric set up
} else if (e instanceof BiometricNotSupportedOrPermissionDeniedException) {
    // biometric is not supported in the current device
    // or user has denied the permission of using biometric
} else if (e instanceof BiometricNoPasscodeException) {
    // device does not have unlock credential set up
} else if (e instanceof BiometricLockoutException) {
    // the biometric is locked out due to too many failed attempts
} else {
    // other error
    // you may consider showing a generic error message to the user
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import {
    CancelError,
    BiometricPrivateKeyNotFoundError,
    BiometricNotSupportedOrPermissionDeniedError,
    BiometricNoEnrollmentError,
    BiometricNoPasscodeError,
    BiometricLockoutError,
} from '@authgear/react-native'

if (e instanceof CancelError) {
    // user cancel
} else if (e instanceof BiometricPrivateKeyNotFoundError) {
    // biometric info has changed. e.g. Touch ID or Face ID has changed.
    // user have to set up biometric authentication again
} else if (e instanceof BiometricNoEnrollmentError) {
    // device does not have biometric set up
    // e.g. have not set up Face ID or Touch ID in the device
} else if (e instanceof BiometricNotSupportedOrPermissionDeniedError) {
    // biometric is not supported in the current device
    // or user has denied the permission of using Face ID
} else if (e instanceof BiometricNoPasscodeError) {
    // device does not have unlock credential or passcode set up
} else if (e instanceof BiometricLockoutError) {
    // the biometric is locked out due to too many failed attempts
} else {
    // other error
    // you may consider showing a generic error message to the user
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    // ...
} on CancelException catch (e) {
    // user cancel
} on BiometricPrivateKeyNotFoundException catch (e) {
    // biometric info has changed. e.g. Touch ID or Face ID has changed.
    // user have to set up biometric authentication again
} on BiometricNoEnrollmentException catch (e) {
    // device does not have biometric set up
    // e.g. have not set up Face ID or Touch ID in the device
} on BiometricNotSupportedOrPermissionDeniedException catch (e) {
    // biometric is not supported in the current device
    // or user has denied the permission of using Face ID
} on BiometricNoPasscodeException catch (e) {
    // device does not have unlock credential or passcode set up
} on BiometricLockoutException catch (e) {
    // the biometric is locked out due to too many failed attempts
} catch (e) {
    // other error
    // you may consider showing a generic error message to the user
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
import {
    CancelError,
    BiometricPrivateKeyNotFoundError,
    BiometricNotSupportedOrPermissionDeniedError,
    BiometricNoEnrollmentError,
    BiometricNoPasscodeError,
    BiometricLockoutError,
} from '@authgear/capacitor'

if (e instanceof CancelError) {
    // user cancel
} else if (e instanceof BiometricPrivateKeyNotFoundError) {
    // biometric info has changed. e.g. Touch ID or Face ID has changed.
    // user have to set up biometric authentication again
} else if (e instanceof BiometricNoEnrollmentError) {
    // device does not have biometric set up
    // e.g. have not set up Face ID or Touch ID in the device
} else if (e instanceof BiometricNotSupportedOrPermissionDeniedError) {
    // biometric is not supported in the current device
    // or user has denied the permission of using Face ID
} else if (e instanceof BiometricNoPasscodeError) {
    // device does not have unlock credential or passcode set up
} else if (e instanceof BiometricLockoutError) {
    // the biometric is locked out due to too many failed attempts
} else {
    // other error
    // you may consider showing a generic error message to the user
}
```

{% endtab %}
{% endtabs %}

## Fall back to Device PIN/Password when Biometric Verification fails

You may want to provide an alternative means for users to log in when biometric verification fails on their devices. For example, in a case where a user's finger is dirty (touch-based) or when the user's face is covered (Face ID).

On iOS, you use `deviceOwnerAuthenticationWithBiometrics` during enable, and use `deviceOwnerAuthentication` during subsequent authentication.

On Android, you set `allowedAuthenticatorsOnEnable` and `allowedAuthenticatosOnAuthenticate` according to the example below.

{% tabs %}
{% tab title="iOS" %}

```swift
// Enable biometric for the user.
authgear.enableBiometric(
    localizedReason: "The reason",
    constraint: .biometryCurrentSet,
    // The user must authenticate with biometric during enable.
    policy: .deviceOwnerAuthenticationWithBiometrics,
) { result in
    if case let .failure(error) = result {
        // Handle error
    } else {
        // Enabled successfully
    }
}

// Authenticate the user
authgear.authenticateBiometric(
    localizedReason: "The reason",
    // Allow passcode by using deviceOwnerAuthentication instead of deviceOwnerAuthenticationWithBiometrics
    policy: .deviceOwnerAuthentication
) { result in
    if case let .failure(error) = result {
        // Handle error
    } else {
        // Authenticated successfully, either with biometric or passcode.
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
BiometricOptions biometricOptions = new BiometricOptions(
    activity, // FragmentActivity
    "Biometric authentication", // title
    "Biometric authentication", // subtitle
    "Use biometric to authenticate", // description
    "Cancel", // negativeButtonText
    // Require biometric during enable
    List.of(BiometricAuthenticator.BIOMETRIC_STRONG), // allowedAuthenticatorsOnEnable
    // Allow passcode during authentication
    List.of(BiometricAuthenticator.BIOMETRIC_STRONG, BiometricAuthenticator.DEVICE_CREDENTIAL), // allowedAuthenticatorsOnAuthenticate
    true // invalidatedByBiometricEnrollment
);

authgear.enableBiometric(
    biometricOptions,
    new OnEnableBiometricListener() {
        @Override
        public void onEnabled() {
            // enabled biometric login successfully
        }

        @Override
        public void onFailed(Throwable throwable) {
            // failed to enable biometric with error
        }
    }
);

authgear.authenticateBiometric(
    biometricOptions,
    new OnAuthenticateBiometricListener() {
        @Override
        public void onAuthenticated(UserInfo userInfo) {
            // logged in successfully
        }

        @Override
        public void onAuthenticationFailed(Throwable throwable) {
            // failed to login
        }
    }
);
```

{% endtab %}

{% tab title="React Native" %}

```typescript
const androidOptions = {
  title: "Biometric Authentication",
  subtitle: "Biometric authentication",
  description: "Use biometric to authenticate",
  negativeButtonText: "Cancel",
  allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.BiometricStrong],
  allowedAuthenticatosOnAuthenticate: [BiometricAuthenticatorAndroid.BiometricStrong, BiometricAuthenticatorAndroid.DeviceCredential],
  invalidatedByBiometricEnrollment: true,
};

const iosOptionsForEnable = {
  localizedReason: "Use biometric to authenticate",
  localizedCancelTitle: "Cancel",
  constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
  policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
};

const iosOptionsForAuthenticate = {
  localizedReason: "Use biometric to authenticate",
  localizedCancelTitle: "Cancel",
  constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
  policy: BiometricLAPolicy.deviceOwnerAuthentication,
};

authgear
    .enableBiometric({ ios: iosOptionsForEnable, android: androidOptions })
    .then(() => {
        // enabled biometric login successfully
    })
    .catch((err) => {
        // failed to enable biometric with error
    });

authgear
    .authenticateBiometric({ ios: iosOptionsForAuthenticate, android: androidOptions})
    .then(({userInfo}) => {
        // logged in successfully
    })
    .catch((e) => {
        // failed to login
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
final androidOptions = BiometricOptionsAndroid(
    title: "Biometric Authentication",
    subtitle: "Biometric authentication",
    description: "Use biometric to authenticate",
    negativeButtonText: "Cancel",
    allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.biometricStrong],
    allowedAuthenticatorsOnAuthenticate: [BiometricAuthenticatorAndroid.biometricStrong, BiometricAuthenticatorAndroid.deviceCredential],
    invalidatedByBiometricEnrollment: true,
);

final iosOptionsForEnable = BiometricOptionsIOS(
    localizedReason: "Use biometric to authenticate",
    localizedCancelTitle: "Cancel",
    constraint: BiometricAccessConstraintIOS.biometryCurrentSet,
    policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
);

final iosOptionsForAuthenticate = BiometricOptionsIOS(
    localizedReason: "Use biometric to authenticate",
    localizedCancelTitle: "Cancel",
    constraint: BiometricAccessConstraintIOS.biometryCurrentSet,
    policy: BiometricLAPolicy.deviceOwnerAuthentication,
);

try {
    await authgear.enableBiometric(ios: iosOptionsForEnable, android: androidOptions);
    // enabled biometric login successfully
} catch (e) {
    // failed to enable biometric with error
}

try {
    final userInfo = await authgear.authenticateBiometric(ios: iosOptionsForAuthenticate, android: androidOptions);
    // logged in successfully
} catch (e) {
    // failed to login
}
```

{% endtab %}

{% tab title="Ionic" %}

```typescript
const androidOptions = {
  title: "Biometric Authentication",
  subtitle: "Biometric authentication",
  description: "Use biometric to authenticate",
  negativeButtonText: "Cancel",
  allowedAuthenticatorsOnEnable: [BiometricAuthenticatorAndroid.BiometricStrong],
  allowedAuthenticatosOnAuthenticate: [BiometricAuthenticatorAndroid.BiometricStrong, BiometricAuthenticatorAndroid.DeviceCredential],
  invalidatedByBiometricEnrollment: true,
};

const iosOptionsForEnable = {
  localizedReason: "Use biometric to authenticate",
  localizedCancelTitle: "Cancel",
  constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
  policy: BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics,
};

const iosOptionsForAuthenticate = {
  localizedReason: "Use biometric to authenticate",
  localizedCancelTitle: "Cancel",
  constraint: BiometricAccessConstraintIOS.BiometryCurrentSet,
  policy: BiometricLAPolicy.deviceOwnerAuthentication,
};

authgear
    .enableBiometric({ ios: iosOptionsForEnable, android: androidOptions })
    .then(() => {
        // enabled biometric login successfully
    })
    .catch((err) => {
        // failed to enable biometric with error
    });

authgear
    .authenticateBiometric({ ios: iosOptionsForAuthenticate, android: androidOptions})
    .then(({userInfo}) => {
        // logged in successfully
    })
    .catch((e) => {
        // failed to login
    });
```

{% endtab %}
{% endtabs %}

The biometric pop-up will look like this after a failed biometric log-in attempt with `BiometricLAPolicy.deviceOwnerAuthentication` policy on iOS:

<figure><img src="/files/ihKynAFVHHQhBvr6GUFD" alt="" width="315"><figcaption></figcaption></figure>

With the `BiometricLAPolicy.deviceOwnerAuthenticationWithBiometrics` policy, the biometric login pop-up will not include the **Enter Passcode** button. An example is shown below:

<figure><img src="/files/PkIjgjvnbLh5yhX2qu8U" alt="" width="315"><figcaption></figcaption></figure>


# Add Anonymous Users

Allow guest users to use your apps and website and promote to regular users later.

## Overview

You can use the Anonymous Users feature to create an anonymous user account for the guests in your apps so that they can carry out interactions just like a normal user. For example, guests can post comments and save preferences in your social platform before setting their email and password. The user session will persist even if the app has been closed.

This improves the app experience because the user does not need to set up authenticators until further down the user journey, while still enjoying most of the app features. For app developers, the ability to create and assign Anonymous Users also makes it easier to link the activities of an individual before and after sign-up.

{% hint style="info" %}
**Note**: The Anonymous Users feature will only work for Authgear Client Applications of type: Native App, Single Page Application, and Traditional Web Application.

**OIDC/SAML Client Application type does not** support Anonymous Users. Hence, your application using this client application type will get an error when you try to create anonymous users.
{% endhint %}

## Enable Anonymous Users for your project

1. In the portal, go to **Authentication > Anonymous Users**.
2. Turn on **Enable anonymous users.**
3. **Save** the settings.

## Using the SDK

### Sign up as an Anonymous User

This will create an Anonymous User for the session. Subsequent requests from the end-user in the session can be identified by the same `sub`

{% tabs %}
{% tab title="React Native" %}

```typescript
authgear
    .authenticateAnonymously()
    .then(({userInfo}) => {
        // Logged in as anonymous user successfully
    })
    .catch((err) => {
        // Handle the error
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    final userInfo = await authgear.authenticateAnonymously();
    // Logged in as anonymous user successfully
} catch (e) {
    // Handle the error
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear.authenticateAnonymously { result in
    switch result {
    case let .success(userInfo):
        // Logged in as anonymous user successfully
    case let .failure(error):
        // Handle the error
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
mAuthgear.authenticateAnonymously(new OnAuthenticateAnonymouslyListener() {
    @Override
    public void onAuthenticated(@NonNull UserInfo userInfo) {
        // Logged in as anonymous user successfully
    }

    @Override
    public void onAuthenticationFailed(@NonNull Throwable throwable) {
        // Handle the error
    }
});
```

{% endtab %}

{% tab title="Web" %}

```typescript
authgear
    .authenticateAnonymously()
    .then(({userInfo}) => {
        // Logged in as anonymous user successfully
    })
    .catch((err) => {
        // Handle the error
    });
```

{% endtab %}
{% endtabs %}

### Check the UserInfo object

After "signing up" as an anonymous user, you can [retrieve the "UserInfo" object](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo) and see the `sub` of the end-user.

**UserInfo**

```json
{
  "sub": "...",
  "isVerified": false,
  "isAnonymous": true
}
```

### Promotion of an Anonymous User

The `promoteAnonymousUser` function can be called to promote an anonymous user to a regular user. You'll call the function with a login ID (e.g., email, phone number) and authenticators (e.g., password). The end-user will be prompted with a sign-up page to complete the promotion. The `sub` of the end-user will remain the same after the promotion.

{% tabs %}
{% tab title="React Native" %}

```javascript
authgear
    .promoteAnonymousUser({
        redirectURI: THE_REDIRECT_URI,
    })
    .then(({userInfo}) => {
        // Promote anonymous user successfully
    })
    .catch((e) => {
        // Handle the error
    });
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    final userInfo = await authgear.promoteAnonymousUser(redirectURI: THE_REDIRECT_URI);
    // Promote anonymous user successfully
} catch (e) {
    // Handle the error
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear.promoteAnonymousUser(
    redirectURI: THE_REDIRECT_URI
) { result in
    switch result {
    case let .success(userInfo):
        // Promote anonymous user successfully
    case let .failure(error):
        // Handle the error
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
PromoteOptions options = new PromoteOptions(THE_REDIRECT_URI);
authgear.promoteAnonymousUser(options, new OnPromoteAnonymousUserListener() {
    @Override
    public void onPromoted(@NonNull UserInfo userInfo) {
        // Promote anonymous user successfully
    }
    @Override
    public void onPromotionFailed(@NonNull Throwable throwable) {
        // Handle the error
    }
});
```

{% endtab %}

{% tab title="Web" %}
**Step 1: Start the promotion flow**

When the user clicks promote on your website, make a **start promotion** call to redirect them to the promotion page.

```typescript
authgear
    .startPromoteAnonymousUser({
        // Configure redirectURI which users will be redirected to
        // after they have promoted with Authgear.
        // You can use any path in your website.
        // Make sure it is in the "Redirect URIs" list of the Application.
        // The redirect uri for anonymous user promotion should be
        // different from the one for normal user authentication.
        // e.g. "https://yourdomain.com/promote-redirect"
        redirectURI: THE_REDIRECT_URI,
    })
    .then(({userInfo}) => {
        // Started the promotion flow
    })
    .catch((err) => {
        // Failed to start the promotion flow
    });
```

**Step 2: Handle the promotion result**

After the user promotes on the promotion page, the user will be redirected to the `redirectURL` with a `code` parameter in the URL query. In the `redirectURI` of your application, make a **finish promotion** call to handle the promotion result.

```typescript
authgear
    .finishPromoteAnonymousUser()
    .then(({userInfo}) => {
        // Promoted successfully
        // You should redirect the user to another path
    })
    .catch((err) => {
        // Failed to finish promotion
    });
);
```

{% endtab %}
{% endtabs %}

## User Lifetime

### Mobile apps

On Mobile SDKs, creating an anonymous user will create a key-pair. The key-pair is stored in the native encrypted store on the mobile device. The end-user can always re-login to the same anonymous user with the key-pair. Such anonymous user will become inaccessible when the encrypted store is removed.

### Web apps and websites

On the Web SDK, there will be no key-pair created. Therefore, the end-user will not be able to login to the same Anonymous User after their session becomes invalid. For cookie-based authentication, it is controlled by the "idle timeout" and "session lifetime" of the **Cookie**. For token-based authentication, it is controlled by the "idle timeout" and "token lifetime" of the **Refresh Token**.

In other words, the anonymous user account lifetime is the same as the logged-in session lifetime.

To adjust the lifetime settings, change the timeouts and lifetimes in **Portal** > **Applications** accordingly.

#### Caution for high-traffic websites

You should create anonymous users only when necessary in the user journey to prevent creating excessive orphan accounts in your tenant.


# Enable Two-Factor Authentication (2FA)

Guide on how to add Two-Factor Authentication to your application.

Authgear supports Two-Factor Authentication (2FA) or Multi-Factor Authentication (MFA) for additional layers of security in your application.

When you enable MFA on your application, Authgear will require your users to present two or more factors in order to log in. These factors could be their password and a One-time Password (OTP) that is sent to their registered email address or phone number. As a result, an attacker can not gain access to a user's account with only a compromised password.

In this post, you'll learn how to enable MFA or 2FA for your Authgear project and how to configure 2FA grace period.

### Prerequisites

* An Authgear account. Create one for free [here](https://www.authgear.com/).
* An Authgear Project.
* And basic experience [getting started](https://docs.authgear.com/get-started/start-building) with Authgear.

## 1. How to Enable 2FA

### Step 1: Open 2FA Settings Page

You can enable 2FA and configure other settings from the 2-Factor Authentication page in Authgear Portal.

To open the 2-Factor Authentication page, log in to Authgear Portal, select your project, then navigate to **Authentication** > **2FA**.

<figure><img src="/files/HSEBS21iVWoNkdLdHghw" alt=""><figcaption></figcaption></figure>

### Step 2: Select a 2FA Requirements Policy

Next, use the **2FA Requirements** dropdown on the 2-Factor Authentication page to set when to require users to use 2-Factor Authentication to sign in.

The available options are:

* **Disabled**: When this is selected, 2FA will not be required to log in for any user, including users who already have 2FA set up for their account.
* **Optional**: This policy will only require 2FA to log in for users who already have 2FA set up for their account. Users who have not set up 2FA can continue to log in without it.
* **Mandatory**: Use the mandatory policy to require 2FA for all users. That means users who have not set up 2FA will not be able to log in if no grace period is set. To use this option, consider further actions like [setting up a grace period for rollout](#id-2.-2fa-grace-period-and-how-2fa-should-be-rolled-out).

Toggle the Show **"Do not ask again on this device" for 2FA** switch on if you wish to require 2FA only the first time a user logs in from a specific device.

### Step 3: Add Available 2-Factor Methods

The **Available 2-Factor** sub-section on the 2-Factor Authentication page shows a list of supported second-factor authentication methods. The supported methods include:

* Google Authenticator/Authy
* Additional Password
* OTP Code/Login Link via Email
* OTP Code via Phone

Check the box for each 2FA method you wish to enable for your project.

<figure><img src="/files/58oT5TJ3YegyEDqtY72B" alt=""><figcaption></figcaption></figure>

Use the **up** and **down** allows on the right of each method to order the priority of the 2FA methods.

Once you're done, click on the **Save** button at the top-left of the 2-Factor Authentication page to keep your new settings.

## 2. Grace Period in Mandatory 2FA

The 2FA Grace Period feature grants your users some time to set up 2FA for their accounts. This is very helpful for the Mandatory enforcement of 2FA.

The following are the two types of 2FA grace periods you can set for your Authgear project:

**Global Grace Period**: When this type of grace period is enabled, all users who do not have 2FA set up for their account will be asked to set up 2FA the next time they log in. When the Global Grace Period is disabled, users who have not set up 2FA for their account cannot log in. Instead, they'll get an error message requesting them to contact an admin.

**Individual Grace Period:** This is a type of grace period that is set per user. It grants a user 10 days to set up 2FA for their account. This is ideal for allowing individual users to set up 2FA when the Global Grace Period is disabled.

### How to roll out Mandatory 2FA

The following steps show how to roll out Mandatory 2FA using grace periods:

1. Change 2FA requirement policy to Mandatory
2. Enable Global Grace Period so that all users who haven't set up 2FA are required to do so the next time they login.
3. Use your own channel to notify user's about the duration of the global grace period you've decided.
4. Disable the Global Grace Period once the date you notified users of has passed. After you do this, users that still haven't set up 2FA will be unable to log in.
5. When users that could not set up their 2FA during the Global Grace Period contact you (the admin), enable individual grace period for them using the instructions in [step 2](#step-2-enable-individual-grace-period).

### Step 1: Enable Global Grace Period

To enable the Global Grace Period, navigate to **Authentication** > **2FA** in the Authgear Portal. Then, set **2FA Requirements** to **Mandatory** so that you can view the **Enable global grace period** switch.

<figure><img src="/files/o0kuWe7aK9fsplSPwQuW" alt=""><figcaption></figcaption></figure>

Toggle the **Enable global grace period** switch on so that your users without 2FA will be required to set up 2FA the next time they log in. Or set **Enable global grace period** off, if you do not want users without 2FA to log in, or set up 2FA without contacting an admin.

### Step 2: Enable Individual Grace Period

When you set the 2FA requirement for your project to Mandatory and Global Grace Period is turned off, you can still use the individual grace period to grant a specific user a grace period to set up 2FA for their account.

To set individual grace period, navigate to **User Management** > **User** in Authgear Portal. Then, select the user you wish to set individual grace period for by clicking on the row with their ID in your project's users' list.

From the selected user's details page, click on the **Account Security** tab, then the **Grant grace period to set up 2FA** button. You will see a prompt to confim your action, click **Confirm** to continue.

<figure><img src="/files/JF0Hk66uVas7PV0I9t4r" alt=""><figcaption></figcaption></figure>

The duration of the individual grace period is 10 days. However, you can extend or cancel it from the user's details page.


# Re-authentication

Authgear provides an easy method to reauthenticate the end-users. You can use this as a security measure to protect sensitive operations.

## Overview

Reauthentication in Authgear is built on top of the [OIDC ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken). The ID token is a JWT.

Your sensitive operation server endpoint **MUST** require the ID token. When you receive the ID token, you **MUST** verify the signature of it. If the signature is valid, you can trust the claims inside the ID token.

The `auth_time` claim in the ID token tells **when** was the end-user last authenticated. You should check the `auth_time` claim to see if the end-user was authenticated recently enough.

The `https://authgear.com/claims/user/can_reauthenticate` claim in the ID token tells whether the end-user can be reauthenticated. If the value of this claim is `false`, then depending on your business needs, you can either allow the end-user to proceed, or forbid the end-user to perform sensitive operations. The flows are illustrated by the following diagrams.

![Sequence diagram for end-user who CANNOT reauthenticate](/files/-MeN_cFxDbGIdqBxdm6n)

![Sequence diagram for end-user who CAN reauthenticate](/files/-MeN_cFyG0WUHGeRtFe2)

## SDK Integration

The following code snippets illustrate the interaction between the SDK and Authgear.

{% tabs %}
{% tab title="React Native" %}

```typescript
const biometricOptions = {
  ios: {
    localizedReason: 'Use biometric to authenticate',
    constraint: 'biometryCurrentSet' as const,
  },
  android: {
    title: 'Biometric Authentication',
    subtitle: 'Biometric authentication',
    description: 'Use biometric to authenticate',
    negativeButtonText: 'Cancel',
    constraint: ['BIOMETRIC_STRONG' as const],
    invalidatedByBiometricEnrollment: true,
  },
};

async function onClickPerformSensitiveOperation() {
  // Step 1: Refresh the ID token to ensure the claims are up-to-date.
  await authgear.refreshIDToken();

  // Step 2: Check if the end-user can be reauthenticated.
  const canReauthenticate = authgear.canReauthenticate();
  if (!canReauthenticate) {
    // Step 2.1: Depending on your business need, you may want to allow
    // the end-user to proceed.
    // Here we assume you want to proceed.

    const idTokenHint = authgear.getIDTokenHint();

    // Step 2.2: Call the sensitive endpoint with the ID token.
    // It is still required to pass the ID token to the endpoint so that
    // the endpoint can know the end-user CANNOT be reauthenticated.
    return callMySensitiveEndpoint(idTokenHint);
  }

  // Step 3: The end-user can be reauthenticated.
  // If your app supports biometric authentication, you can pass
  // the biometric options to reauthenticate.
  // If biometric is enabled for the current user, it will be used instead.
  await authgear.reauthenticate({
    redirectURI: THE_REDIRECT_URI,
  }, biometricOptions);

  // Step 4: If we reach here, the reauthentication was done.
  // The ID token have up-to-date auth_time claim.
  const idTokenHint = authgear.getIDTokenHint();

  return callMySensitiveEndpoint(idTokenHint);
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
final ios = BiometricOptionsIOS(
    localizedReason: "Use biometric to authenticate",
    constraint: BiometricAccessConstraintIOS.biometryAny,
);
final android = BiometricOptionsAndroid(
    title: "Biometric Authentication",
    subtitle: "Biometric authentication",
    description: "Use biometric to authenticate",
    negativeButtonText: "Cancel",
    constraint: [BiometricAccessConstraintAndroid.biometricStrong],
    invalidatedByBiometricEnrollment: false,
);

Future<void> onClickPerformSensitiveOperation() async {
    // Step 1: Refresh the ID token to ensure the claims are up-to-date.
    await authgear.refreshIDToken();

    // Step 2: Check if the end-user can be reauthenticated.
    final canReauthenticate = authgear.canReauthenticate;
    if (!canReauthenticate) {
        // Step 2.1: Depending on your business need, you may want to allow
        // the end-user to proceed.
        // Here we assume you want to proceed.
        final idTokenHint = authgear.idTokenHint;

        // Step 2.2: Call the sensitive endpoint with the ID token.
        // It is still required to pass the ID token to the endpoint so that
        // the endpoint can know the end-user CANNOT be reauthenticated.
        return callMySensitiveEndpoint(idTokenHint);
    }

    // Step 3: The end-user can be reauthenticated.
    // If your app supports biometric authentication, you can pass
    // the biometric options to reauthenticate.
    // If biometric is enabled for the current user, it will be used instead.
    await authgear.reauthenticate(
        redirectURI: THE_REDIRECT_URI,
        biometricIOS: ios,
        biometricAndroid: android,
    );

    // Step 4: If we reach here, the reauthentication was done.
    // The ID token have up-to-date auth_time claim.
    final idTokenHint = authgear.idTokenHint;

    return callMySensitiveEndpoint(idTokenHint);
}
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
var ios = new BiometricOptionsIos
{
    LocalizedReason = "Use biometric to authenticate",
    AccessConstraint = BiometricAccessConstraintIos.BiometricAny,
};
var android = new BiometricOptionsAndroid
{
    Title = "Biometric Authentication",
    Subtitle = "Biometric authentication",
    Description = "Use biometric to authenticate",
    NegativeButtonText = "Cancel",
    AccessConstraint = BiometricAccessConstraintAndroid.BiometricOnly,
    InvalidatedByBiometricEnrollment = false,
};

async void OnPerformSensitiveOperationClicked(object sender, EventArgs args)
{
    // Step 1: Refresh the ID token to ensure the claims are up-to-date.
    await authgear.RefreshIdTokenAsync();

    // Step 2: Check if the end-user can be reauthenticated.
    var canReauthenticate = authgear.CanReauthenticate;
    if (!canReauthenticate)
    {
        // Step 2.1: Depending on your business need, you may want to allow
        // the end-user to proceed.
        // Here we assume you want to proceed.
        var idTokenHint = authgear.IdTokenHint;

        // Step 2.2: Call the sensitive endpoint with the ID token.
        // It is still required to pass the ID token to the endpoint so that
        // the endpoint can know the end-user CANNOT be reauthenticated.
        await CallMySensitiveEndpointAsync(idTokenHint);
        return;
    }

    // Step 3: The end-user can be reauthenticated.
    // If your app supports biometric authentication, you can pass
    // the biometric options to reauthenticate.
    // If biometric is enabled for the current user, it will be used instead.
    await authgear.ReauthenticateAsync(new ReauthenticateOptions
    {
        RedirectURI: THE_REDIRECT_URI,
    }, new BiometricOptions
    {
        Ios = ios,
        Android = android,
    });

    // Step 4: If we reach here, the reauthentication was done.
    // The ID token have up-to-date auth_time claim.
    var idTokenHint = authgear.IdTokenHint;
    await CallMySensitiveEndpointAsync(idTokenHint);
}
```

{% endtab %}

{% tab title="Web" %}

```typescript
async function onClickPerformSensitiveOperation() {
  // Step 1: Refresh the ID token to ensure the claims are up-to-date.
  await authgear.refreshIDToken();

  // Step 2: Check if the end-user can be reauthenticated.
  const canReauthenticate = authgear.canReauthenticate();
  if (!canReauthenticate) {
    // Step 2.1: Depending on your business need, you may want to allow
    // the end-user to proceed.
    // Here we assume you want to proceed.

    const idTokenHint = authgear.getIDTokenHint();

    // Step 2.2: Call the sensitive endpoint with the ID token.
    // It is still required to pass the ID token to the endpoint so that
    // the endpoint can know the end-user CANNOT be reauthenticated.
    return callMySensitiveEndpoint(idTokenHint);
  }

  // Step 3: The end-user can be reauthenticated.
  // The end-user will be redirected to Authgear.
  // When the reauthentication finishes,
  // The end-user will be redirected back to the given redirect URI.
  await authgear.startReauthentication({
    redirectURI: THE_REDIRECT_URI
  });
}

// Suppose the following function is run when the end-user is redirected to
// the redirect URI
async function onRedirectAfterReauthentication() {
  // You HAVE to configure authgear again
  // because your website have been visited freshly.
  await authgear.finishReauthentication();
  await authgear.refreshIDToken();
  const idTokenHint = authgear.getIDTokenHint();
  return callMySensitiveEndpoint(idTokenHint);
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
func onClickPerformSensitiveOperation() {
    // Step 1: Refresh the ID token to ensure the claims are up-to-date.
    authgear.refreshIDToken() { result in
        switch result {
        case .success:
            // Step 2: Check if the end-user can be reauthenticated.
            let canReauthenticate = authgear.canReauthenticate
            if !canReauthenticate {
                // Step 2.1: Depending on your business need, you may want to allow
                // the end-user to proceed.
                // Here we assume you want to proceed.
                let idTokenHint = authgear.idTokenHint
                // Step 2.2: Call the sensitive endpoint with the ID token.
                // It is still required to pass the ID token to the endpoint
                // so that the endpoint can know the end-user CANNOT
                // be reauthenticated.
                callMySensitiveEndpoint(idTokenHint)
                return
            }

            // Step 3: The end-user can be reauthenticated.
            // By default biometric is used for reauthentication if it is enabled for the current user.
            // If you do not want biometric to be used, specify skipUsingBiometric: true
            authgear.reauthenticate(redirectURI: THE_REDIRECT_URI, skipUsingBiometric: false) { result in
                switch result {
                case .success:
                    // Step 4: If we reach here, the reauthentication was done.
                    // The ID token have up-to-date auth_time claim.
                    let idTokenHint = authgear.idTokenHint
                    callMySensitiveEndpoint(idTokenHint)
                    return
                case let .failure(error):
                    // Handle the error
                }
            }
        case let .failure(error):
            // Handle the error
        }
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
public void onClickPerformSensitiveOperation() {
    BiometricOptions biometricOptions = new BiometricOptions(
        activity, // FragmentActivity
        "Biometric authentication", // title
        "Biometric authentication", // subtitle
        "Use biometric to authenticate", // description
        "Cancel", // negativeButtonText
        ALLOWED, // allowedAuthenticators
        true // invalidatedByBiometricEnrollment
    );

    // Step 1: Refresh the ID token to ensure the claims are up-to-date.
    authgear.refreshIDToken(new OnRefreshIDTokenListener() {
        @Override
        public void onFailed(Throwable throwable) {
            // Handle error
        }
        @Override
        public void onFinished() {
            // Step 2: Check if the end-user can be reauthenticated.
            boolean canReauthenticate = authgear.getCanReauthenticate();
            if (!canReauthenticate) {
                // Step 2.1: Depending on your business need, you may want to allow
                // the end-user to proceed.
                // Here we assume you want to proceed.
                String idTokenHint = authgear.getIDTokenHint();
                // Step 2.2: Call the sensitive endpoint with the ID token.
                // It is still required to pass the ID token to the endpoint
                // so that the endpoint can know the end-user CANNOT
                // be reauthenticated.
                callMySensitiveEndpoint(idTokenHint);
                return;
            }

            // Step 3: The end-user can be reauthenticated.
            // If your app supports biometric authentication, you can pass
            // the biometric options to reauthenticate.
            // If biometric is enabled for the current user, it will be used.
            ReauthenticateOptions options =
                new ReauthenticateOptions(THE_REDIRECT_URI);
            authgear.reauthenticate(options, biometricOptions, new OnReauthenticateListener() {
                @Override
                public void onFailed(Throwable throwable) {
                    // Handle error
                }
                @Override
                public void onFinished(UserInfo userInfo) {
                    // Step 4: If we reach here, the reauthentication was done.
                    // The ID token have up-to-date auth_time claim.
                    String idTokenHint = authgear.getIDTokenHint();
                    callMySensitiveEndpoint(idTokenHint);
                    return;
                }
            });
        }
    });
}
```

{% endtab %}
{% endtabs %}

### Reauthenticate conditionally by the last authentication time

If the end-users in your application often perform a series of sensitive operation, it is annoying that they have to reauthenticate themselves repeatedly before every operation. To allow the end-users to skip reauthentication if they have just reauthenticated themselves recently, the SDK allows you to inspect the last authentication time of the end-user.

{% tabs %}
{% tab title="JavaScript" %}

```typescript
async function onClickPerformSensitiveOperation() {
  await authgear.refreshIDToken();
  // Before you trigger reauthentication, check authTime first.
  const authTime = authgear.getAuthTime();
  if (authTime != null) {
    const now = new Date();
    const timeDelta = now.getTime() - authTime.getTime();
    if (timeDelta < 5 * 60 * 1000 /* 5 minutes */) {
      const idTokenHint = authgear.getIDTokenHint();
      return callMySensitiveEndpoint(idTokenHint);
    }
  }

  // Otherwise trigger authentication.
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
func onClickPerformSensitiveOperation() {
    authgear.refreshIDToken() { result in
        switch result {
        case .success:
            // Before you trigger reauthentication, check authTime first.
            if let authTime = authgear.authTime {
                let now = Date()
                let timeDelta = now.timeIntervalSince(authTime)
                if timeDelta < 5 * 60 {
                    let idTokenHint = authgear.idTokenHint
                    callMySensitiveEndpoint(idTokenHint)
                    return
                }
            }
            // Otherwise trigger authentication.
        case let .failure(error):
            // Handle the error
        }
    }
}
```

{% endtab %}

{% tab title="Android" %}

```java
public void onClickPerformSensitiveOperation() {
    authgear.refreshIDToken(new OnRefreshIDTokenListener() {
        @Override
        public void onFailed(Throwable throwable) {
            // Handle error
        }
        @Override
        public void onFinished() {
            // Before you trigger reauthentication, check authTime first.
            Date authTime = authgear.getAuthTime();
            if (authTime != null) {
                Date now = new Date();
                long timedelta = now.getTime() - authTime.getTime();
                if (timedelta < 5 * 60 * 1000) {
                    String idTokenHint = authgear.getIDTokenHint();
                    callMySensitiveEndpoint(idTokenHint);
                    return;
                }
            }
            // Otherwise trigger authentication.
        }
    });
}
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
public async void OnPerformSensitiveOperationClicked(object sender, EventArgs args)
{
    await authgear.RefreshIdTokenAsync();
    var authTime = authgear.AuthTime;
    if (authTime != null)
    {
        var now = DateTimeOffset.UtcNow;
        var timedelta = now - authTime.Value;
        if (timedelta < TimeSpan.FromMinutes(5))
        {
            var idTokenHint = authgear.IdTokenHint;
            callMySensitiveEndpoint(idTokenHint);
            return;
        }
    }
}
```

{% endtab %}
{% endtabs %}

## Backend Integration

Finally in your backend, you have to verify the signature of the ID token, and then validate the claims inside.

{% tabs %}
{% tab title="Python" %}

```python
import json
from contextlib import closing
from urllib.request import urlopen
from datetime import datetime, timezone, timedelta

import jwt
from jwt import PyJWKClient

base_address = "https://<your_app_endpoint>"

def fetch_jwks_uri(base_address):
    doc_url = base_address + "/.well-known/openid-configuration"
    with closing(urlopen(doc_url)) as f:
        doc = json.load(f)
    jwks_uri = doc["jwks_uri"]
    if not jwks_uri:
        raise Exception('Failed to fetch jwks uri.')
    return jwks_uri

def my_endpoint():
    id_token = GET_ID_TOKEN_FROM_HTTP_REQUEST_SOMEHOW()
    try:
        jwks_uri = fetch_jwks_uri(base_address)
        # Reuse PyJWKClient for better performance
        jwks_client = PyJWKClient(jwks_uri)
        signing_key = jwks_client.get_signing_key_from_jwt(id_token)
        claims = jwt.decode(
            id_token,
            signing_key.key,
            algorithms=["RS256"],
            audience=base_address,
            options={"verify_exp": True},
        )
        auth_time = claims["auth_time"]
        dt = datetime.fromtimestamp(auth_time)
        now = datetime.utcnow()
        delta = now - dt
        if delta > timedelta(minutes=5):
            raise ValueError("auth_time is not recent enough")
    except:
        # Handle error
        raise
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "time"

    "github.com/lestrrat-go/jwx/v3/jwk"
    "github.com/lestrrat-go/jwx/v3/jwt"
)

var (
    baseAddress = "https://<your_app_endpoint>"
)

type OIDCDiscoveryDocument struct {
    JWKSURI string `json:"jwks_uri"`
}

func FetchOIDCDiscoveryDocument(endpoint string) (*OIDCDiscoveryDocument, error) {
    resp, err := http.DefaultClient.Get(endpoint)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf(
            "failed to fetch discovery document: unexpected status code: %d",
            resp.StatusCode,
        )
    }

    var document OIDCDiscoveryDocument
    err = json.NewDecoder(resp.Body).Decode(&document)
    if err != nil {
        return nil, err
    }
    return &document, nil
}

func FetchJWK(baseAddress string) (jwk.Set, error) {
    doc, err := FetchOIDCDiscoveryDocument(
        baseAddress + "/.well-known/openid-configuration",
    )
    if err != nil {
        return nil, err
    }

    set, err := jwk.Fetch(context.Background(), doc.JWKSURI)
    return set, err
}

func CheckIDToken(idToken string) error {
    // fetch jwks_uri from Authgear
    // you can cache the value of jwks to have better performance
    set, err := FetchJWK(baseAddress)
    if err != nil {
        return fmt.Errorf("failed to fetch JWK: %s", err)
    }

    // parse jwt token
    token, err := jwt.ParseString(
        idToken, 
        // This may not work out of the box depending on the jwk.Set.
        // Please read about requirements for "kid" and "alg" (and possibly
        // "WithDefaultKey") when using jwk.Set in the jwt.WithKeySet documentation.
        jwt.WithKeySet(set),
    )
    if err != nil {
        return fmt.Errorf("invalid token: %s", err)
    }

    // validate jwt token
    err = jwt.Validate(token,
        jwt.WithClock(jwt.ClockFunc(
            func() time.Time { return time.Now().UTC() },
        )),
        jwt.WithIssuer(baseAddress),
    )
    if err != nil {
        return fmt.Errorf("invalid token: %s", err)
    }

    var authTimeUnix float64
    if err := token.Get("auth_time", &authTimeUnix); err != nil {
        return fmt.Errorf("no auth_time: %w", err)
    }

    authTime := time.Unix(int64(authTimeUnix), 0)
    now := time.Now().UTC()

    diff := now.Sub(authTime)
    if diff > 5*time.Minute {
        return fmt.Errorf("auth_time is not recent enough")
    }

    return nil
}
```

{% endtab %}
{% endtabs %}


# Add authentication to any web page

Learn how to add authentication to any web page without using Authgear's SDKs with IIFE(Immediately-invoked Function Expression) bundle

In this guide, you'll make a simple website server to host the SPA app using [ExpressJS](https://expressjs.com/). We'll also use it to serve our HTML page and any assets it needs, like JavaScript, CSS, and so on. You can also view a [full-source code](https://github.com/authgear/authgear-example-spa-js) on the GitHub repo.

#### Prerequisites

* Before we start, ensure you have Node.js installed in your system. If not, download and install it from the [official website](https://nodejs.org/en/download/).
* **An Authgear account:** You need an Authgear account to follow this guide. If you don't have one, you can[ create it for free](https://accounts.portal.authgear.com/signup) on the Authgear website.
* **A Registered App:** You need a registered application type (Single Page Application) in Authgear. Follow the [setup application](/get-started/single-page-app/website#setup-application-in-authgear) guide and skip [installing the Authgear Web SDK](/get-started/single-page-app/website#step-4-install-authgear-web-javascript-sdk) part. You will retrieve the Authgear Web SDK from Authgear's CDN using IIFE(Immediately-invoked Function Expression) bundle and reference a script in our HTML directly.

### Create a basic web server

Start with making a **new folder** on your computer to keep the app’s source code (In the example, we call it `authgear-spa-js-login`). Then, initialize a new NPM project by running the following command:

```bash
npm init -y
```

Next, we install two required packages:

```jsx
npm install express
```

Also, install [nodemon](https://npmjs.org/package/nodemon) so that our server can be restarted automatically on any code changes in dev mode:

```jsx
npm install -D nodemon
```

Next, open the `package.json` file and edit scripts entry to have `start` and `dev` commands like the below:

```json
{
  // ...
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  // ...
}
```

Now you can run the app in two modes: *prod and dev*.

For example, `npm run dev` will run the application using `nodemon`, monitoring for changes as we modify files.

### Creating server.js

Create a new file `server.js` in the root of the project and populate it with the following code:

```json
const express = require("express");
const { join } = require("path");
const app = express();

// Serve static assets from the /public folder
app.use(express.static(join(__dirname, "public")));

// Endpoint to serve the configuration file
app.get("/authgear_config.json", (req, res) => {
  res.sendFile(join(__dirname, "authgear_config.json"));
});

// Serve the index page for all other requests
app.get("/*", (_, res) => {
  res.sendFile(join(__dirname, "index.html"));
});

// Listen on port 3000
app.listen(3000, () => console.log("Application running on port 3000"));
```

### Create a basic HTML page

Create a `index.html` file in the root of the project and add the following content to the created file:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Authgear SPA SDK Sample</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css" />
  </head>

  <body>
    <h2>SPA Authentication Sample</h2>
    <p>Welcome to our page!</p>
    <button id="btn-login" disabled="true" onclick="login()">Log in</button>
    <button id="btn-logout" disabled="true" onclick="logout()">Log out</button>
    <script src="js/app.js"></script>
    <script src="<https://unpkg.com/@authgear/web@2.2.0/dist/authgear-web.iife.js>"></script>
  </body>
</html>
```

We do not use a package manager such as [Webpack](https://webpack.js.org/), we will retrieve the Authgear Web SDK from Authgear's CDN using IIFE(Immediately-invoked Function Expression) bundle. We can reference a script in our HTML directly:

```html
<script src="<https://unpkg.com/@authgear/web@2.2.0/dist/authgear-web.iife.js>"></script>
```

> You can install the Authgear Web SDK as a dependency of your application, it is useful if you are building React or React Native apps. See how to [install the package](https://docs.authgear.com/get-started/single-page-app/website#install-the-authgear-web-sdk).

### Create a main.css file

Create a new folder called `public` folder in the project root folder and create another folder called `css` inside the `public` folder. Add a new file in there called `main.css`. This will be used to determine how the log-in and log-out button elements will be hidden on the main page depending on whether a user is authenticated or not.

Open the newly-created `public/css/main.css` file and add the following CSS:

```css
.hidden {
    display: none;
}
  
label {
    margin-bottom: 10px;
    display: block;
}
```

After creating an HTML file and applying CSS styles, see now how our page looks like by running `npm run dev` and accessing it at <http://localhost:3000>.

<figure><img src="/files/UgkHfjOtAGMhZyTaRzBh" alt=""><figcaption></figcaption></figure>

### Create an app.js file

To add some action to the page, we create a new directory in the `public` folder called `js`, and add a new file there called `app.js`. Copy and paste the following JS code that reads `authgear_config.json` file Authgear app-specific values (`endpoint` and `clientId`) from the endpoint using `fetchAuthConfig` function. Also, it configures a new Authgear client, and defines login and logout logic:

```jsx
let authgearClient = null;

const fetchAuthConfig = () => fetch("/authgear_config.json");

const configureClient = async () => {
    const response = await fetchAuthConfig();
    const config = await response.json();
    authgearClient = window.authgear.default;

    await authgearClient.configure({
        endpoint: config.endpoint,
        clientID: config.clientID,
        sessionType: "refresh_token",
    }).then(
        () => {
            console.log("Authgear client successfully configured!");
        },
        (err) => {
            console.log("Failed to configure Authgear");
        }
    );
};

const login = async () => {
    await authgearClient
        .startAuthentication({
            redirectURI: window.location.origin,
            prompt: "login",
        })
        .then(
            () => {
                console.log("Logged in!");
            },
            (err) => {
                console.log("Log in failed", err);
            }
        );
};

const logout = () => {
    authgearClient
    .logout({
      redirectURI: window.location.origin,
    })
    .then(
      () => {
        console.log("Logged out successfully");
      },
      (err) => {
        console.log("Failed to logout");
      }
    );
};

window.onload = async () => {
    await configureClient();
    updateUI();

    const query = window.location.search;
    if (query.includes("code=")) {

        updateUI();

        window.history.replaceState({}, document.title, "/");
    }
}

const updateUI = async () => {
    const isAuthenticated = authgearClient.sessionState === "AUTHENTICATED";

    document.getElementById("btn-logout").disabled = !isAuthenticated;
    document.getElementById("btn-login").disabled = isAuthenticated;
};
```

### Understanding the whole picture

Let’s breakdown down `app.js` code in the previous section and understand how authentication is achieved with Authgear:

**Configure the Authgear client**

`fetchAuthConfig`: Firstly, this function makes a request to the `/authgear_config.json` the endpoint we exposed in `server.js` to fetch Authgear app setting values from `authgear_config.json`file.

`configureClient`: Once we retrieve the configuration information for the Authgear client from the `authgear_config.json` file and we set up the Authgear client with these settings. It also logs a message to the console, informing whether the configuration was successful or not.

**Login flow**

`login`: The function is called by the **Login** button previously defined on the HTML page. It performs the login action by calling `authgearClient.startAuthentication` Authgear’s function. It redirects the user to the Auhthgear login page. After the user logs in successfully, they will be redirected back to the same page we set in `redirectURI`. Run the project and click the **Login** button. You should be taken to the **Authgear Login Page** configured for your application.

<figure><img src="/files/0CA4WbTl5eE1NKgdB91Q" alt=""><figcaption></figcaption></figure>

Go ahead and create a new user or log in using an email (we specified the Passwordless Email login method in the first part). When you try to log in with your email, you should receive a [magic link](https://docs.authgear.com/strategies/email-login-link) to your email box to confirm login operation.

<div><figure><img src="/files/wihFnZVDA8eBlzgtIRWb" alt=""><figcaption></figcaption></figure> <figure><img src="/files/OUd27R27B5NJMiw2ywy6" alt=""><figcaption></figcaption></figure></div>

After authenticating successfully, you will be redirected to the page you were before.

**Logout flow**

`logout`: This function logs the user out and redirects them back to the original page (at<http://localhost:3000>). It uses Authgear’s `logout` function and logs a message to the console indicating the result of the operation.

**Update the UI**

`window.onload`: This is a function that runs when the page loads. It configures the Authgear client and updates the UI. If the page's URL contains a "code=" it means the user is authenticated (`code` the query will be received from the Authgear server), it updates the UI again and removes the "code=" from the URL.

**Evaluate the authentication state**

`updateUI`: This function updates the status of the login and logout buttons based on whether the user is authenticated or not. In Authgear, you can check if the user has logged in or not with `sessionState` the attribute. If the user is authenticated, we disable the login button and enable the logout button, and vice versa if the user is not authenticated.


# Disable Public Signup

For many business applications, it's important to restrict who can register for your platform. By disabling public signups in Authgear, you can ensure that new users are only added through your internal processes, such as the Admin API or the Authgear Portal.

### When to Disable Public Signup <a href="#when-to-disable-public-signup" id="when-to-disable-public-signup"></a>

Disable public signup if you want to:

* Control user access and approval for your application.
* Provision users only via admin tools.
* Prevent the general public from self-registering.

### How to Disable Public Signup <a href="#how-to-disable-public-signup" id="how-to-disable-public-signup"></a>

To turn off public registration:

1. Navigate to **Portal** > **Advanced** > **Edit Config**.
2. Find or add the following configuration in your YAML file:

   ```yaml
   authentication:
     public_signup_disabled: true
   # other configurations...
   ```
3. Save your changes


# Custom Authentication Flow

Allow users to authenticate with a custom flow.

Authgear lets you define exactly how users sign up and log in. Instead of using the default UI, you can configure a custom authentication flow — a sequence of steps that controls which identifiers you accept, how you verify them, and how users authenticate.

Configure flows in the Authgear portal under **Portal > Advanced > Edit Config**, in the `authentication_flow` section of the YAML.

### Key concepts

Before reading the examples, here are the building blocks you'll see throughout the YAML:

| Term                         | What it does                                                                                                                  |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `signup_flows`               | Defines steps for registering a new user                                                                                      |
| `login_flows`                | Defines steps for authenticating a returning user                                                                             |
| `signup_login_flows`         | A single flow that detects whether the user is new or returning, then routes to the appropriate `signup_flow` or `login_flow` |
| `type: identify`             | A step that asks the user for an identifier (email, phone, etc.)                                                              |
| `type: verify`               | A step that confirms the user owns the identifier (e.g. via OTP)                                                              |
| `type: authenticate`         | A step that authenticates the user with a registered credential                                                               |
| `type: create_authenticator` | A step that registers a new authenticator for the user                                                                        |
| `one_of`                     | Lists the allowed options for a step. Required even when there is only one option.                                            |
| `target_step`                | Links a `verify` or `create_authenticator` step back to the `identify` step that collected the credential                     |

***

### Prerequisites

The examples on this page use OTP-based passwordless authentication over email and SMS. Before adding the YAML config, enable the right login method in the portal:

1. Go to **Authentication > Login Methods**
2. Select **Mobile / Email**
3. Under **Authentication**, select **Passwordless**

If you use a different login method (e.g. password-based), the `primary_oob_otp_email` and `primary_oob_otp_sms` authenticators in the examples will not work — adjust the `authentication` values to match your chosen method.

<figure><img src="/files/xkt15wB3TW1ofP5bIkrB" alt=""><figcaption></figcaption></figure>

### Example: Signup requiring both email and phone

By default, when both email and phone are enabled, users can choose one identifier to sign up with — not both. This example changes that: it requires users to provide and verify both an email address and a phone number during signup. After signup, they can log in with either.

{% code lineNumbers="true" %}

```yaml
authentication_flow:
  signup_flows:
  - name: default
    steps:
    - name: setup_email
      type: identify
      one_of:
      - identification: email
        steps:
        - type: verify
          target_step: setup_email
        - type: create_authenticator
          name: authenticate_primary_email
          one_of:
          - authentication: primary_oob_otp_email
            target_step: setup_email
    - name: setup_phone
      type: identify
      one_of:
      - identification: phone
        steps:
        - type: verify
          target_step: setup_phone
        - type: create_authenticator
          name: authenticate_primary_phone
          one_of:
          - authentication: primary_oob_otp_sms
            target_step: setup_phone  
```

{% endcode %}

The signup flow has two sequential steps: `setup_email`, then `setup_phone`.

Each step collects one identifier and immediately verifies it with a one-time passcode. The `create_authenticator` step then registers that identifier as a login method, so the user can use it to log in later.

`target_step` is required here because the `verify` and `create_authenticator` steps are nested inside an `identify` step — they need to explicitly reference the parent step that collected the credential.

{% hint style="warning" %}
This config only defines a `signup_flow`. It does not include login steps. Disable **"Automatically sign up a new user if a login ID is not found during login"** in the portal, or returning users may trigger a signup instead of a login.
{% endhint %}

***

### Example: Combined signup and login, with both identifiers collected on signup

`signup_login_flows` handles new and returning users in one flow. The user enters an identifier (email or phone), and Authgear checks whether an account exists:

* Account found → routes to `login_flow`
* No account found → routes to `signup_flow`

This means your UI only needs one screen — no separate "Log in" and "Sign up" buttons required.

The example below combines this with the two-identifier requirement from the previous example. Users log in with whichever identifier they have. New users must verify both a phone number and an email during signup, so they can use either to log in in the future.

```yaml
authentication_flow:
  signup_login_flows:
  - name: default
    steps:
    - type: identify
      one_of:
      - identification: phone
        login_flow: default
        signup_flow: default
      - identification: email
        login_flow: default
        signup_flow: default

  login_flows:
  - name: default
    steps:
    - type: identify
      one_of:
      - identification: phone
        steps:
        - type: authenticate
          one_of:
          - authentication: primary_oob_otp_sms
      - identification: email
        steps:
        - type: authenticate
          one_of:
          - authentication: primary_oob_otp_email

  signup_flows:
  - name: default
    steps:
    - type: identify
      name: setup_id1
      one_of:
      - identification: phone
        steps:
        - type: verify
          target_step: setup_id1
        - type: identify
          name: setup_id2
          one_of:
          - identification: email
            steps:
            - type: verify
              target_step: setup_id2
      - identification: email
        steps:
        - type: verify
          target_step: setup_id1
        - type: identify
          name: setup_id3
          one_of:
          - identification: phone
            steps:
            - type: verify
              target_step: setup_id3
    - type: create_authenticator
      one_of:
      - authentication: primary_oob_otp_sms
      - authentication: primary_oob_otp_email
```

#### How each part works

**`signup_login_flows`**

The user picks an identifier — phone or email. Both options point to the same `login_flow: default` and `signup_flow: default`. Authgear looks up the identifier and routes the user automatically.

**`login_flows`**

For returning users. After identifying themselves, the user receives a one-time passcode — by SMS if they entered a phone number, or by email if they entered an email address.

**`signup_flows`**

For new users. The flow collects and verifies both identifiers, regardless of which one the user entered first.

* **User starts with phone** (`setup_id1: phone`): Authgear verifies the phone number, then asks for an email address (`setup_id2`) and verifies that too.
* **User starts with email** (`setup_id1: email`): Authgear verifies the email address, then asks for a phone number (`setup_id3`) and verifies that too.

Once both identifiers are verified, the `create_authenticator` step registers the OTP method the user will use to authenticate on future logins.

{% hint style="info" %}
`setup_id2` and `setup_id3` are separate named steps because the second identifier collected depends on which one the user started with. Each path needs its own named step so `target_step` can reference the right one.
{% endhint %}

{% hint style="warning" %}
When using `signup_login_flows`, disable **"Automatically sign up a new user if a login ID is not found during login"** in the portal. That setting conflicts with the routing logic in `signup_login_flows`.
{% endhint %}

***

### Further reading

* [Overview of Authentication Flow API](/customization/custom-ui/authentication-flow-api) — use the API to build a fully custom login UI
* [Authentication Flow API specification](/reference/apis/authentication-flow-api) — full reference for all flow types, step types, and options


# Sessions

Control how long users stay signed in, and whether their session survives after the browser is closed.

When a user signs in through Authgear on the built-in UI or your [Custom UI](/customization/custom-ui), Authgear remembers the signed-in user with a session cookie on your Authgear domain (e.g. `myproject.authgear.cloud` or your custom domain). Every app in the same project shares this session and it powers Single Sign-On (SSO) across your apps.

Control the behavior and expiration of this session in **Authgear Portal** > **Advanced** > **Session**.

{% hint style="info" %}
Token-based apps (SPAs and native apps using the SDKs) keep their own sessions with refresh tokens. Their lifetimes are configured per application in **Portal** > **Applications**. See [Refresh Token](/reference/tokens/refresh-token) for details. An SDK configured with `isSSOEnabled: false` skips the shared session cookie entirely, see [SSO with mobile apps / websites](/authentication-and-access/single-sign-on/sso-with-mobile-app-web-spa).
{% endhint %}

## Session Behavior

Session Behavior controls whether a signed-in session survives after the user closes the browser.

* **Keep users signed in** (default): The session cookie is persistent. Users stay signed in across browser restarts, until the session expires according to the [Session Expiration](#session-expiration) settings below.
* **End session on browser close**: The session cookie is dropped when the user closes the browser, so the user must sign in again next time. Choose this when devices are shared among multiple users, such as kiosks or terminals.

{% hint style="warning" %}
**End session on browser close** relies on the browser discarding the session cookie. Browsers with session restore enabled (e.g. Chrome's "Continue where you left off") may keep session cookies across restarts. The server-side session still expires according to the Session Expiration settings.
{% endhint %}

## Session Expiration

Session Expiration controls how long a session can last before the user must sign in again. It applies regardless of the Session Behavior setting.

* **Maximum session lifetime**: The longest a session can last before it expires, even if the user stays active. The default is `31449600` seconds (52 weeks). When it expires, the user must sign in again to every app that shares the session.
* **Expire after idling** and **Idle timeout**: When enabled (default), the session expires after the user has been inactive for the idle timeout. The default is `2592000` seconds (30 days).

For example, with a maximum session lifetime of 52 weeks and an idle timeout of 30 days, a user who visits your apps at least once a month stays signed in for up to a year, while a user who is away for more than 30 days must sign in again.

## Configure with authgear.yaml

If you self-host Authgear, the same settings are available under the `session` key in `authgear.yaml`:

```yaml
session:
  # End session on browser close. Default is false (keep users signed in).
  use_session_cookie: false
  # Maximum session lifetime in seconds. Default is 31449600 (52 weeks).
  lifetime_seconds: 31449600
  # Whether the session expires after idling. Default is true.
  idle_timeout_enabled: true
  # Idle timeout in seconds. Default is 2592000 (30 days).
  idle_timeout_seconds: 2592000
```


# Single Sign-on Overview

Provide a seamless user experience across multiple applications with the single sign-on feature.

Single sign-on (SSO) is defined as login once, logged in all apps. If you have multiple mobile apps or websites that wants to streamline the user experiences. You can configure your apps to turn on the SSO feature, so the end-users only have to enter their authentication credentials once.

There are multiple ways to achieve Single Sign-on, with various pros-and-cons:

|                                                 | Related Feature                                                                                                                           | Technical Remarks                                                                                             |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| SSO between Websites with the same apex domain  | [Cookie-based Deployment](/get-started/backend-api#forward-cookie-in-http-header)                                                         | Requires all of the websites with the same "root domain" (e.g. app1.**example.com** and app2.**example.com**) |
| SSO between Mobile Apps and Browsers            | [SSO between Mobile App / Websites](/authentication-and-access/single-sign-on/sso-with-mobile-app-web-spa)                                | Requires the use of `ASWebAuthentication` and `Custom Tab` on iOS/Android respectively                        |
| SSO between two independent mobile apps         | [App2App Login](/authentication-and-access/single-sign-on/app2app-authorization)                                                          | Based on OIDC App2App                                                                                         |
| SSO from a Mobile App to Website                | [Pre-Authenticated URLs](/authentication-and-access/single-sign-on/pre-authenticated-urls)                                                | Open a URL from Mobile App and pass the user session along, based on OIDC Token Exchange                      |
| SSO between Mobile Apps from the same publisher | <p>Keychain Sharing / Android Account Manager<br><br>(<a href="https://www.authgear.com/schedule-demo">Contact us</a> if you need it)</p> | Requires both apps published by the same publisher from App Store.                                            |


# App2App Login

Perform faster authentication flow via another app installed on the same device.

This may be familiar for users from UK, which many neobanks are using the app2app mechanism to authorize the money transfer from 1 bank app to another.

The App2App mechanism allows one app to authenticate the user using another apps connected to the auth server installed on the same device. This is achieved by universal links and the apps do not need to share the session via the system browser or the refresh tokens via the token storage.

{% hint style="info" %}
This is an Enterprise feature, please contact us for using the App2App flow in your project at <https://www.authgear.com/talk-with-us>
{% endhint %}

Please note that this is not the Single Sign-on feature, if your are offering multiple apps under the same brand and wish the users to use a shared login session among their apps in the device, you may want to use [Single Sign-on](/authentication-and-access/single-sign-on) instead. App2app should be used when:

* The session cannot be shared via the browser cookies
* The session cannot be shared via a common token storage

{% embed url="<https://youtu.be/Q6FPiQm56xE>" %}
Demo of the App2App login flow
{% endembed %}

### Mechanism

An app can start the authentication flow by opening a link to another app, instead of using the authorization endpoint. The app which handles the link should validate the authentication request, then could return a valid authorization code. The valid code is then transferred to the original app using universal link. The initiating app can use that authorization code to perform code exchange for tokens with Authgear.

<figure><img src="/files/jS3h5g9f47bFglDmzNNS" alt=""><figcaption><p>App2App flow in iOS</p></figcaption></figure>

A detailed explanation on the technology can be found in [this specification](https://github.com/authgear/authgear-server/blob/main/docs/specs/app2app.md).

### Setting it up in Authgear

{% hint style="info" %}
This is an Enterprise feature, please contact us for enabling the App2App flow in your project at <https://www.authgear.com/talk-with-us>
{% endhint %}

#### Configuration in the authorizing app

1. Go to the Application detail page of the authorizing app, i.e. the app which handles the app2app authentication requests.
2. Scroll to the bottom and you will see the App2App config panel.
3. Select Enable App2App login for this Application"\ <img src="/files/Bku7yzzxrZLtH64FX3ct" alt="" data-size="original">
4. Migration mode offers a less secure mechanism which helps older user sessions to participate in App2App. DO NOT enable it unless there is migration problem.

#### Configuration in the initiating app

1. Go to the Application detail page of the initiating app, i.e. the app which initiates the app2app authentication requests.
2. In the redirect URIs, a universal link that's capable of opening this app should be set.

### Setting up the apps

1. Define and set up the universal links for both apps, for example:
   1. `https://a.example.com/authorize` should open the authorizing app (App A)
   2. `https://b.example.com/redirect` should open the initiating app (App B)
2. In App B, call `startApp2AppAuthentication(options: App2AppAuthenticateOptions)` to initiate the app2app login
   * `App2AppAuthenticateOptions.authorizationEndpoint` should be an url of an universal link pointing to App A, i.e. `https://a.example.com/authorize`
   * `App2AppAuthenticateOptions.redirectUri` should be an URI for the authorizing app to return the authentication result. It must be an universal link which opens the current app. i.e. `https://b.example.com/redirect`
3. In App A, upon receiving the app2app login request
   * Call `parseApp2AppAuthenticationRequest(url: URL): App2AppAuthenticateRequest?`
   * The result will be `null` if the url is not a valid app2app request.
4. You can approve or reject the app2app request in App A
   * Approve: `approveApp2AppAuthenticationRequest(request: App2AppAuthenticateRequest)`
     * Approves an app2app request returning the result through the redirect URI.
     * `request` should be the return value of `parseApp2AppAuthenticationRequest`.
     * This method must be called when then SDK session state is `AUTHENTICATED`, and the current session supported app2app authentication by providing a `device_key`, or else an error will be thrown.
   * Reject: `rejectApp2AppAuthenticationRequest(request: App2AppAuthenticateRequest, error: Error)`
     * Rejects an app2app request, returning an error through the redirect URI.
     * `request` should be the return value of `parseApp2AppAuthenticationRequest`.
     * `error` is the reason to reject the request.
5. When it's back to App B, call `handleApp2AppAuthenticationResult(url: URL)`
   * This method should be called by the app which initiate the app2app authentication flow, and when received the result through the universal link, `url` should be the URL of the universal link received.


# App2Web (Pre-authenticated URLs)

Use the pre-authenticated URLs feature to open a website from a native app in an authenticated state.

Pre-authenticated URLs is a feature that enables single sign-on (SSO) from a mobile application to a website. It allows users who are authenticated on a mobile application to open a website in an authenticated state.

An example use case for a pre-authenticated URL is opening a web application in a WebView.

### Prerequisites

To use pre-authenticated URLs, you must have the following:

* A native app using Authgear as authentication
* A web application using the same Authgear project as authentication

## How to Implement Pre-authentication URLs in your application

### Step 1: Enable SSO & Pre-authenticated URLs in Native Client App

First, ensure your mobile application uses an Authgear application with the **Native App**. Enable "preAuthenticatedURL" to allow pre-authenticated URLs to work.

{% tabs %}
{% tab title="Ionic" %}

```javascript
authgear
  .configure({
    clientID: '<CLIENT_ID>',
    preAuthenticatedURLEnabled: true
  })
```

{% endtab %}
{% endtabs %}

### Step 2: Add Allowed Origin to Web App Client

Next, add an allowed origin to the web application client in Authgear.

Navigate to **Advanced > Edit Config** in the Authgear Portal, find the **web application client** and add the origins you wish to use for Pre-authentication URLs in the `x_pre_authenticated_url_allowed_origins` key. Also set `x_pre_authenticated_url_enabled` to true.

```yaml
oauth:
  clients:
  - client_id: <Client ID of the web app>
    x_pre_authenticated_url_enabled: true
    x_pre_authenticated_url_allowed_origins:
    - <origin 1>
    - <origin 2>
    - ...
    ...
```

Note that the origin should be of the format "protocol (scheme) + domain + port". For example, if the mobile application wants to open `https://www.mywebapp.com/home?key=value`, the origin must be `https://www.mywebapp.com`.

### Step 3: Generate Pre-Authenticated URL

The Pre-Authenticated URL is a link that the Authgear SDK can generate for a mobile client that has the Pre-Authenticated URLs feature enabled. Your mobile application can open the Pre-Authenticated URL in a web view for users to start browsing the origin in an authenticated state.

To generate the Pre-Authenticated URL, call the `makePreAuthenticatedURL()` method of the Authgear SDK as shown below:

{% tabs %}
{% tab title="Ionic" %}

```javascript
const url = await authgear.makePreAuthenticatedURL({
    webApplicationClientID: "YOUR_WEB_APP_CLIENT_ID", // Replace with you web app client id
    webApplicationURI: "YOUR_WEB_APP_URI", // Replace with you web app uri
  });
```

{% endtab %}
{% endtabs %}

The `makePreAuthenticatedURL()` method accepts an object as a parameter. Inside the object, you should provide your web application's client ID and web app URI.

### Step 4: Open Pre-Authenticated URL in a WebView

After the `makePreAuthenticatedURL()` return the URL, your mobile application should open the URL in a WebView. From there, users should be able to continue their current authenticated session (from the mobile app) on the web application.

{% tabs %}
{% tab title="Ionic" %}
The following code sample shows how to open the pre-authenticated URL using the `Browser.open()` method in Ionic.

<pre class="language-javascript"><code class="lang-javascript"><strong>Browser.open({ url: url }).catch(err =>
</strong>      console.error("Couldn't load page", err),
);
</code></pre>

{% endtab %}

{% tab title="React Native" %}
The following code sample shows how to open the pre-authenticated URL using the `Linking.openURL()` method of React Native.

<pre><code><strong>Linking.openURL(url).catch(err =>
</strong>      console.error("Couldn't load page", err),
);
</code></pre>

{% endtab %}
{% endtabs %}

### Step 5: Get authenticated state in the web application

The pre-authenticated URL is opened in the browser via the native app. In the web application, trigger authentication with the injected SSO session and get the authenticated state.

{% tabs %}
{% tab title="Web" %}
In the web application, enable SSO to allow pre-authenticated URLs to work. You can initialize the SDK as following

```typescript
import authgear, { PromptOption } from "@authgear/web";

authgear.configure({
    endpoint: "AUTHGEAR_ENDPOINT",
    clientID: "CLIENT_ID",
    sessionType: "refresh_token",
    isSSOEnabled: true
});
```

And in the web application URI, trigger authentication as following. Note here `prompt: PromptOption.None` is used to skip the SSO continue screen.

```typescript
import authgear, { PromptOption } from "@authgear/web";

authgear.startAuthentication({
  redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
  prompt: PromptOption.None // use "None" to skip the continue screen
})
.then(
  () => {
    // started authentication, user should be redirected to Authgear
   },
  (err) => {
    // failed to start authorization
  }
);
```

<div align="center"><figure><img src="/files/Kz96SOSselsI3RRWUUhv" alt="" width="188"><figcaption><p>Use prompt parameter to skip the SSO Continue Screen</p></figcaption></figure></div>

In a normal login flow, for example the user browses the web page in the browser rather than from a link in the native app, the prompt should not be used because it will hinder the user from opening the login page. Only use this prompt when an SSO session is surely set in the browser, for instance in conjunction with this Pre-authentication URL feature.
{% endtab %}
{% endtabs %}


# Web2App (From Mobile Apps to Browsers)

If you are building token-based websites or mobile apps, you can enable the SSO feature via the SDK.

When SSO-enabled is ON, the end-user will need to enter their authentication credentials when they login to the first app. Later on, when they login to the second app, they will see a **continue screen** so that they can log in with just a click, without authenticating themselves again.

{% hint style="info" %}
It is important that when the SSO feature is ON, don't set the `prompt` parameter when authenticating (e.g. `prompt=login`), it will force to show the login screen.
{% endhint %}

When the end-user logout the SSO-enabled app, all the apps will be logged out at the same time.

You can turn on this feature when you configure the SDK by setting the **is sso enabled** option to `true`.

{% tabs %}
{% tab title="Web" %}

```typescript
authgear.configure({
    clientID: CLIENT_ID,
    endpoint: ENDPOINT,
    sessionType: "refresh_token",
    isSSOEnabled: true,
});
```

{% endtab %}

{% tab title="React Native" %}

```typescript
authgear.configure({
    clientID: CLIENT_ID,
    endpoint: ENDPOINT,
    isSSOEnabled: true,
});
```

{% endtab %}

{% tab title="Flutter" %}

```dart
final authgear = Authgear(
    clientID: CLIENT_ID,
    endpoint: ENDPOINT,
    isSsoEnabled: true,
);
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
var authgearOptions = new AuthgearOptions
{
    ClientId = CLIENT_ID,
    AuthgearEndpoint = ENDPOINT,
    IsSsoEnabled = true,
};
// Android
#if __ANDROID__
var authgear = new AuthgearSdk(GetActivity().ApplicationContext, authgearOptions);
#else
#if __IOS__
var authgear = new AuthgearSdk(UIKit.UIApplication.SharedApplication, authgearOptions);
#endif
#endif
```

{% endtab %}

{% tab title="iOS" %}

```swift
Authgear(
    clientId: CLIENT_ID,
    endpoint: ENDPOINT,
    isSSOEnabled: true,
)
```

{% endtab %}

{% tab title="Android" %}

```java
new Authgear(
    getApplication(),
    CLIENT_ID,
    ENDPOINT,
    new PersistentTokenStorage(getApplication()),
    true // isSsoEnabled = true
);
```

{% endtab %}
{% endtabs %}

These type of SSO requires sharing the cookies between mobile apps and the system browsers on mobile, hence underlying it use `ASWebAuthenticationSession` on iOS and `Custom Tab` on Android, which will show a popup box like this:

<figure><img src="/files/2EyCVIy2rVKf99vA3mIE" alt="" width="188"><figcaption></figcaption></figure>

If you want to avoid the said popup box, you will need to use `WKWebView` on iOS and `WebKitWebView` on Android for `UIImplementation` instead; And use [App2App Login](/authentication-and-access/single-sign-on/app2app-authorization) for sharing login session between mobile apps, and [Pre-authenticated URLs](/authentication-and-access/single-sign-on/pre-authenticated-urls) between mobile and web instead.


# Force Authgear to Show Login Page

Force Authgear to always show login page even if the user have already logged in.

When user login / signup to Authgear, it usually starts with your application making a request to the authorization endpoint, which leads to a login or signup screen.

If the user is already signed in on the browser, the Single Sign On feature will show a "Continue Screen" instead as follows.

<figure><img src="/files/FfUaWu3iVeEQZuatzE1x" alt=""><figcaption></figcaption></figure>

If your application do not want to utilize the Single Sign On feature, and always show the login / sign up screen instead, you can force Authgear to show login page by using `prompt="login"` at the `authorize` endpoint.

### How to Force Authgear to Show Login Page

The `prompt="login"` parameter which is defined in the [OIDC spec](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) can force AuthUI to show the login page. Authgear SDKs have a `prompt` parameter that can be used to set prompt="login". Once the `prompt` parameter is set to `login` Authgear will always show the login screen when your application calls the SDK's authenticate method.

The following code shows how to set `prompt: "login"` in Authgear SDKs:

{% tabs %}
{% tab title="Next.js" %}

```typescript
import { PromptOption } from "@authgear/nextjs";

// via hook
const { signIn } = useAuthgear();
signIn({ prompt: PromptOption.Login });

// via component
<SignInButton signInOptions={{ prompt: PromptOption.Login }}>Sign In</SignInButton>
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
authgear
  .startAuthentication({
    redirectURI: "<AUTHGEAR_REDIRECT_URI>",
    prompt: PromptOption.Login,
  })
```

{% endtab %}

{% tab title="React Native" %}

```typescript
authgear
  .authenticate({
    redirectURI: 'com.reactnativeauth://host/path',
    prompt: PromptOption.Login,
  })
```

{% endtab %}

{% tab title="Android" %}

```java
AuthenticateOptions options = new AuthenticateOptions("<AUTHGEAR_REDIRECT_URI>");
List<PromptOption> promptOptions = Arrays.asList(PromptOption.LOGIN);
options.setPrompt(promptOptions);
mAuthgear.authenticate(options, new OnAuthenticateListener() {
    @Override
    public void onAuthenticated(@Nullable UserInfo userInfo) {
        
    }

    @Override
    public void onAuthenticationFailed(@NonNull Throwable throwable) {
        Log.d(TAG, throwable.toString());
    }
});
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear?.authenticate(
    redirectURI: "<AUTHGEAR_REDIRECT_URI>",
    prompt: "login"
)
```

{% endtab %}

{% tab title="Flutter" %}

```dart
_authgear.authenticate(
        redirectURI: "<AUTHGEAR_REDIRECT_URI>",
        prompt: "login",
      );
```

{% endtab %}
{% endtabs %}


# Social/Enterprise Login Providers Overview

Add third-party identity providers to enable frictionless sign in for your users

Authgear supports the following social and enterprise identity providers. Please click the link below for setup instructions.

* [Apple](/authentication-and-access/social-enterprise-login-providers/social-login-providers/apple)
* [Google](/authentication-and-access/social-enterprise-login-providers/social-login-providers/google)
* [Facebook](/authentication-and-access/social-enterprise-login-providers/social-login-providers/facebook)
* [GitHub](/authentication-and-access/social-enterprise-login-providers/social-login-providers/github)
* [Linkedin](/authentication-and-access/social-enterprise-login-providers/social-login-providers/linkedin)
* [Azure Active Directory](/authentication-and-access/social-enterprise-login-providers/enterprise-login-providers/azureadv2)
* [Azure AD B2C](/authentication-and-access/social-enterprise-login-providers/enterprise-login-providers/azureadb2c)
* [Microsoft AD FS](/authentication-and-access/social-enterprise-login-providers/enterprise-login-providers/adfs)
* [Mobile application with WeChat Login](/authentication-and-access/social-enterprise-login-providers/social-login-providers/wechat-mobile)
* [Website with WeChat Login](/authentication-and-access/social-enterprise-login-providers/social-login-providers/wechat-web)


# Social Login Providers


# Connect Apps to Apple

## Prerequisite

To configure "Sign in with Apple" for Authgear, you will need to fulfil the following:

1. Register an Apple Developer Account. Apple Enterprise Account does not support "Sign in with Apple"
2. Register your own domain.
3. Your domain must be able to send and receive emails.
4. Set up [Sender Policy Framework](https://en.wikipedia.org/wiki/Sender_Policy_Framework)(SPF) for your domain.
5. Set up [DomainKeys Identified Mail](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail)(DKIM) for your domain.
6. Create an "App ID" by adding a new "Identifier" [here](https://developer.apple.com/account/resources/identifiers/list), choose app IDs, enable "Sign in with Apple" enabled.
7. Create a "Services ID" by adding a new "Identifier" [here](https://developer.apple.com/account/resources/identifiers/list), choose service IDs, enable "Sign in with Apple".
8. Click "Configure" the Next to "Sign in with Apple". In "Primary App ID" field, select app ID created above.

   <figure><img src="/files/knuf37t1DtreSFFEGuCA" alt=""><figcaption><p>Select here to see Services IDs</p></figcaption></figure>
9. Fill in and verify the domain created above, add `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/apple` to **Return URLs**
10. Create a "Key" following [this guide](https://developer.apple.com/help/account/manage-keys/create-a-private-key) with "Sign in with Apple" enabled. Click "Configure" next to "Sign in with Apple" and select "Primary App ID" with app ID created above. Keep the private key safe, you need to provide this later.

{% hint style="info" %}
Redirect URI has the form of `/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal.
{% endhint %}

## Configure Sign in with Apple in Authgear Portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with Apple**.
3. In **Client ID**, add the identifier of the **Services ID** you created in Apple. Not the ID of the App ID or the Client ID of your Authgear Application.
4. In **Apple Developer Portal**, view key information of the "Key" created above.
5. Jot down the **Key ID** and download the key text file (`.p8` file).
6. Copy the content in the key text file to **Client Secret** text area in **Authgear Portal.**.
7. Fill in **Key ID** field using the **Key ID** obtained from step 5.
8. In **Apple Developer Portal**, click username on the top right corner, click **View Membership**.
9. Find the **Team ID** from **Membership Information**, fill in **Team ID** field in Authgear portal.
10. **Save** the settings.

🎉Done! You have just added Sign in with Apple to your apps!


# Connect Apps to Google

Add Google Sign in to your apps in less than 5 minutes.

## Set up OAuth client on Google Cloud Platform

To configure Google OAuth client for Authgear, you will need to create an OAuth client on Google Cloud Platform first.

### Create a new project

Create a project on Google Cloud Platform through [console](https://console.cloud.google.com/). If you are adding Authgear to your existing Google Cloud Platform projects, you may skip to the next step to create the OAuth client.

### Create OAuth Consent Screen

After creating a new project, you will need to configure the OAuth consent screen. Press the <img src="/files/-McJGR0q7IzSNkqQ_5F-" alt="" data-size="line"> button on the top-left and go to **APIs & Services** -> **OAuth consent screen** and follow the instruction to create the consent screen.

### Create OAuth client ID

1. Go to <img src="/files/-McJGR0q7IzSNkqQ_5F-" alt="" data-size="line"> -> **APIs & services** -> **Credentials**
2. Click **Create Credentials** -> **OAuth client ID**
3. Choose **Web application** in **Application type** and assign a name as reference. You should *always choose Web application* here regardless of the platform of the app you are creating. It is because this OAuth Client ID is used by your Authgear services, which is a web application in Google’s classification.
4. In **Authorized JavaScript origins**, add your Authgear endpoint, e.g. `https://myproject.authgear.cloud`
5. In **Authorized redirect URIs**, add `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/google`. For example, `https://myproject.authgear.cloud/sso/oauth2/callback/google`
6. After creating a client ID, you will see the client ID under the **OAuth 2.0 Client IDs** section of the **Credentials** page.

![OAuth Client ID in the Credentials page](/files/-McJOJ3udcqQVoWtkIC4)

{% hint style="info" %}
Redirect URI has the form of `/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal.
{% endhint %}

You can find more details in [official Google Cloud Platform doc](https://support.google.com/cloud/answer/6158849)

## Configure Sign in with Google in Authgear Portal

### Get your OAuth Client details

After creating an OAuth client, click the name of OAuth client to view the details.

![Get your OAuth Client ID and Secret in the details page](/files/-McJPyCf7dBJNuKM3wbr)

You will need the values of **Client ID**, **Client secret** to configure Google Sign In.

### Configure in Authgear Portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with Google**.
3. Fill in the **Client ID** and **Client Secret** with the values obtained from the previous step.
4. **Save** the settings.

🎉Done! You have just added Google Sign In to your apps!

Your end-users can now sign in with Google on Authgear pre-built Log In and Sign Up page. Existing end-users can connect their account to Google in the [User Settings](/customization/built-in-ui/user-settings) page.

!["Sign in with Google" in Log in and Sign up page](/files/-McJU398f7UHlO5uhNxO)

![Your end-users can connect to their Google account in User Settings page](/files/-McJU396Aj4hNZi3-9Tl)


# Connect Apps to Facebook

Add Facebook Sign in to your apps in less than 5 minutes.

This guide shows how to connect your Authgear application to Facebook so users can log in using the Login with Facebook feature.

<figure><img src="/files/PAUKldfFYf0PP01Uq9pY" alt=""><figcaption></figcaption></figure>

## Step 1: Create an App in Facebook for Developers

If you are using Authgear in your existing Facebook Apps, you may skip to the next step to set up the OAuth client.

### Prerequisite

You will need a Facebook developer Account. Register as one by clicking **Get Started** in the [Facebook for Developers](https://developers.facebook.com/) website.

### Create an App

To create a new app, go to the Facebook Developers [Apps](https://developers.facebook.com/apps) panel then click the Click **Create App** button.

<figure><img src="/files/Pe4wi6yMaz7w1RvKu1cx" alt=""><figcaption></figcaption></figure>

On the "**Use cases**" screen, select **Others** and "**Authenticate and request data from users with Facebook Login**" as your app **use case** then, click **Next**.

<figure><img src="/files/vWYBGNrVgtV0zQQ0SEma" alt=""><figcaption></figcaption></figure>

Follow the on-screen instructions to finish the app creation process.

## Step 2: Add "email" permission

1. Go to "Use Cases" configuration in your application just created.
2. Customize the "Authenticate and request data from users with Facebook Login" use case.
3. Enable "email" permission, make sure both "public\_profile" and "email" are enabled.

<figure><img src="/files/qyIFOgYG79qHS824FSWC" alt=""><figcaption></figcaption></figure>

## Step 3: Set up the OAuth Client

1. In the use case settings, make sure **Client OAuth Login** and **Web OAuth Login** are enabled.
2. Add `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/facebook` to **Valid OAuth Redirect URIs** and save the changes.

<figure><img src="/files/pTfISti0gXD78jzg9fq4" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Redirect URI has the form of `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal.

See [Your Authgear Endpoint](/reference/glossary#your-authgear-endpoint) for instructions on how to get the value for `YOUR_AUTHGEAR_ENDPOINT`.
{% endhint %}

## Step 4: Configure Login with Facebook in Authgear Portal

### Get your OAuth Client details

After setting up the OAuth Client, go to **App settings** > **Basic** in the sidebar.

<figure><img src="/files/cmdkqfQqAaMbgSkzVn7f" alt=""><figcaption></figcaption></figure>

You will need the **App ID** and **App Secret** to configure Facebook Login so, note them down.

App review submission maybe needed to complete this step. Please follow the on-screen instructions.

### Configure in Authgear Portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Login with Facebook**.
3. Fill in the **Client ID** with the **App ID** obtained from the Facebook Developers portal, and **Client Secret** with the **App Secret** in the previous step.
4. **Save** the settings.

<figure><img src="/files/UTY1UvQPfisf2QOwoDCW" alt=""><figcaption></figcaption></figure>

🎉 Done! You have just added Facebook Login to your apps!

Your end-users can now sign in with Facebook on Authgear's pre-built Log In and Sign Up page. Existing end-users can connect their account to Facebook in the [User Settings](/customization/built-in-ui/user-settings) page.

![Your end-users can connect to their Facebook account in User Settings page](/files/-McNqWHx3umxmzdNPuX2)


# Connect Apps to GitHub

## Prerequisite

1. Follow the [official guide](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app) to create a OAuth App.
2. In "Authorization callback URL", use `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/github`.
3. After the creation, click "Generate a new client secret". Remember the client secret.

## Configure Sign in with GitHub through the portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with GitHub**.
3. Fill in **Client ID**.
4. Fill in **Client Secret**.
5. **Save** the changes.

🎉 Done! You have just added GitHub integration to your apps!


# Connect Apps to LinkedIn

Guide on how to add LinkedIn as a social login provider

## Prerequisite

* A LinkedIn Developer profile. You can create one on the [LinkedIn Developers Portal](https://developer.linkedin.com/).
* LinkedIn App

### Step 1: Create a LinkedIn App

Log in to the [LinkedIn Developers Portal](https://developer.linkedin.com/) and create a new app or update the configuration for an existing one.

Go to the **Auth** tab of your LinkedIn app and take note of the "Client ID" and "Client Secret".

Also, add `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/linkedin` to "Authorized redirect URLs" under "OAuth 2.0 settings" section.

<figure><img src="/files/HU1il23HE6im8QqsFRqQ" alt=""><figcaption><p>LinkedIn app, "Auth" tab</p></figcaption></figure>

{% hint style="info" %}
The redirect URI has the form of `/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal
{% endhint %}

Next, open the **Products** tab of your LinkedIn app, and request access to "Sign In with LinkedIn using OpenID Connect."

<figure><img src="/files/Kf8GsNMlrgMZepZYKb6H" alt=""><figcaption><p>LinkedIn app product list</p></figcaption></figure>

### Step 2: Configure Sign in with LinkedIn through the portal

In the Authgear Portal, go to **Authentication** > **Social / Enterprise Login**.

Enable **Sign in with LinkedIn**.

Then fill in the **Client ID**. and **Client Secret** from your LinkedIn app.

**Save** the settings.

🎉 Done! You have just added LinkedIn Login to your apps!


# Connect Mobile Apps to WeChat

{% hint style="info" %}
**WeChat Open Platform account (微信开放平台账号)** is different from **WeChat Official account (微信公众平台账号)**. Authgear supports integrating WeChat Login with a WeChat Open Platform account.
{% endhint %}

## Prerequisite

* Register a [WeChat Open Platform account (微信开放平台账号)](https://open.weixin.qq.com/).
* Register a Mobile Application (移动应用).
  * For **iOS**, the **Associated Domains and Universal Links** supports are required in your app. See [the official documentation](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/) from Apple for details.
  * For **Android**, the **MD5 fingerprint of the signing keystore** is required

See [Appendix: Create mobile app on WeChat Open Platform](#appendix-create-mobile-app-on-wechat-open-platform) for more details.

{% hint style="warning" %}
**WeChat login is incompatible with Passkey**

Login with WeChat requires the use of `WebKitWebViewUIImplementation` which does not support the use of Passkey APIs. At the moment, either passkey or WeChat login can be enabled in your mobile applications.
{% endhint %}

## Get the information from WeChat Open Platform

Once your mobile app is approved on the platform, you will see the word ":white\_check\_mark:<mark style="color:green;">已通过</mark>" in the app details page.

* Get the `appid` (**Client ID**).

<figure><img src="/files/4cmsTOkPEvuaSqNroIIG" alt="where to find appid"><figcaption><p>where to find appid</p></figcaption></figure>

* Get the `appsecret` (**Client Secret**). It will only be shown once. You need to re-generate if you lose it.

<figure><img src="/files/eYZdJhtqoyat6Pkv9suV" alt="where to find appsecret"><figcaption><p>where to find appsecret</p></figcaption></figure>

* Get the `原始ID` (**Account ID**) of your WeChat Open Platform account.

<figure><img src="/files/okxaU54W9x0zb3WmY9fn" alt="where to find account ID"><figcaption><p>where to find account ID</p></figcaption></figure>

## Configure Sign in with WeChat in the Authgear portal

1. Sign in to the Authgear portal.
2. Select your project.
3. In the navigation menu, go to **Authentication > Social / Enterprise Login**.
4. Click **Add Connection**.
5. Select **WeChat Mobile / 移动应用**.
6. Fill in **Client ID** with the `appid`.
7. Fill in **Client Secret** with the `appsecret`.
8. Fill in **Account ID** with the `原始ID`.
9. Add a WeChat Redirect URI. This is typically a custom URI with the scheme being your iOS bundle identifier or your Android package name. For example, `com.myapp://authgear/open_wechat_app`.
10. Save.

## Integrate the WeChat SDK into your iOS app

{% hint style="info" %}
You can skip this section if you do not have an iOS app.
{% endhint %}

{% hint style="info" %}
This section assumes you install the WeChat SDK with [CocoaPods](https://cocoapods.org/).
{% endhint %}

To integrate the WeChat SDK into your iOS app, you have to read [the official integration guide](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html).

This section reminds you some of the important points that you may miss in the official integration guide.

### Install the latest version of the WeChat SDK for iOS

```
pod "WechatOpenSDK-XCFramework", "~> 2.0.4"
```

The latest version can be found at [this URL](https://cocoapods.org/pods/WechatOpenSDK) As of the time of writing, the latest version is `2.0.4`.

### Specify your WeChat `appid` in Info.plist

```
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>weixin</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>wxYOUR_WECHAT_APPID</string>
			</array>
		</dict>
	</array>
```

### Specify LSApplicationQueriesSchemes in Info.plist

```
	<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>weixin</string>
		<string>weixinULAPI</string>
		<string>weixinURLParamsAPI</string>
	</array>
```

In case you have other schemes, you need to make sure the WeChat ones are the first 50 items.

### Make sure `OTHER_LDFLAGS` contains `-ObjC`

{% hint style="info" %}
If you fail to do so, you will encounter [this error](https://developers.weixin.qq.com/community/develop/article/doc/000e8e316d4590c7ef92ec1a366c13).
{% endhint %}

The WeChat SDK for iOS depends on the Objective-C runtime. Therefore, your app has to link to the Objective-C runtime. To do so, you need to make sure `OTHER_LDFLAGS` contains `-ObjC`.

The `-ObjC` flag is so special in CocoaPods that you **CANNOT** add it with CocoaPods [post\_install](https://guides.cocoapods.org/syntax/podfile.html#post_install) hook.

If your iOS app is written in [React Native](https://reactnative.dev/), the app is likely to have `OTHER_LDFLAGS` including `-ObjC` already, as React Native depend on the Objective-C runtime.

In other case, you need to manually edit your Build Settings to include the `-ObjC` flag.

### Add the integration code for the WeChat SDK for iOS

{% tabs %}
{% tab title="Swift WeChat SDK" %}
In your `AppDelegate.swift`

```swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate {

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Insert this line to register your WeChat mobile application.
    WXApi.registerApp("wxYOUR_WECHAT_APPID", universalLink: "https://myapp.com/wechat/")
    return true
  }

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    // Forward the URL to the WeChat SDK.
    // If the URL is about WeChat integration, onResp will be called.
    WXApi.handleOpen(url, delegate: self)
    return true
  }

  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    // Forward the URL to the WeChat SDK.
    // If the URL is about WeChat integration, onResp will be called.
    WXApi.handleOpen(url, delegate: self)
    return true
  }

  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    // Forward the NSUserActivity to the WeChat SDK.
    // If the NSUserActivity is about WeChat integration, onResp will be called.
    WXApi.handleOpenUniversalLink(userActivity, delegate: self)
    return true
  }

  // Implements WXApiDelegate.
  // You need not do any thing special in this method.
  func onReq(_ req: BaseReq) {}

  // The WeChat SDK will call this method if the forwarded callbacks are about WeChat.
  func onResp(_ resp: BaseResp) {
    guard let authResp = resp as? SendAuthResp else {
      return
    }


    let errCode = WXErrCode(rawValue: authResp.errCode)
    if errCode == WXSuccess {
      let state = authResp.state
      let code = authResp.code
      // Forward code to Authgear SDK.
    } else {
      // Handle the error returned by the WeChat SDK properly to deliver good user experience.
    }
  }
}
```

In your `SceneDelegate` (if you have one)

```swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
  func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    // Assume your AppDelegate is the single point of handling WeChat callback,
    // you need to get it back.
    let appDelegate = UIApplication.shared.delegate as? AppDelegate

    // Forward the NSUserActivity to the WeChat SDK.
    // If the NSUserActivity is about WeChat integration, onResp on AppDelegate will be called.
    WXApi.handleOpenUniversalLink(userActivity, delegate: appDelegate)
  }
}
```

{% endtab %}

{% tab title="Objective-C WeChat SDK" %}
In your `AppDelegate.h`

```objc
// Import the WeChat SDK header file.
#import <WXApi.h>

// Declare your AppDelegate to be a WXApiDelegate.
@interface AppDelegate : UIResponder<UIApplicationDelegate, WXApiDelegate>

@end
```

In your `AppDelegate.m`

```objc
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // Insert this line to register your WeChat mobile application.
  [WXApi registerApp:@"wxYOUR_WECHAT_APPID" universalLink:@"https://myapp.com/wechat/"];
  return true;
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  // Forward the URL to the WeChat SDK.
  // If the URL is about WeChat integration, onResp will be called.
  [WXApi handleOpenURL:url delegate:self];
  return true;
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  // Forward the URL to the WeChat SDK.
  // If the URL is about WeChat integration, onResp will be called.
  [WXApi handleOpenURL:url delegate:self];
  return true;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
  // Forward the NSUserActivity to the WeChat SDK.
  // If the NSUserActivity is about WeChat integration, onResp will be called.
  [WXApi handleOpenUniversalLink:userActivity delegate:self];
  return true;
}

// Implements WXApiDelegate.
// You need not do any thing special in this method.
-(void) onReq:(BaseReq*)req
{
}

// The WeChat SDK will call this method if the forwarded callbacks are about WeChat.
-(void) onResp:(BaseResp*)resp
{
  if([resp isKindOfClass:[SendAuthResp class]]) {
    SendAuthResp *sendAuthResp = (SendAuthResp*)resp;
    if (sendAuthResp.errCode == WXSuccess) {
      NSString *state = sendAuthResp.state;
      NSString *code = sendAuthResp.code;
      // Forward code to Authgear SDK.
    } else {
      // Handle the error returned by the WeChat SDK properly to deliver good user experience.
    }
  }
}

@end
```

In your `SceneDelegate.m` (if you have one)

```objc
@implementation SceneDelegate

- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
  // Assume your AppDelegate is the single point of handling WeChat callback,
  // you need to get it back.
  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

  // Forward the NSUserActivity to the WeChat SDK.
  // If the NSUserActivity is about WeChat integration, onResp on AppDelegate will be called.
  [WXApi handleOpenUniversalLink:userActivity delegate:appDelegate];
}

@end
```

{% endtab %}
{% endtabs %}

## Integrate the WeChat SDK into your Android app

{% hint style="info" %}
You can skip this section if you do not have an Android app.
{% endhint %}

To integrate the WeChat SDK into your Android app, you have to read [the official integration guide](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/Android.html).

This section reminds you some of the important points that you may miss in the official integration guide.

### Install the latest version of the WeChat SDK for Android

```
implementation "com.tencent.mm.opensdk:wechat-sdk-android:6.8.34"
```

The latest version can be found at [this URL](https://central.sonatype.com/artifact/com.tencent.mm.opensdk/wechat-sdk-android) As of the time of writing, the latest version is `6.8.34`.

### Declare `<queries>` in your `AndroidManifest.xml`

You need to declare in your `AndroidManifest.xml` that your app is supposed to query the existence of the WeChat app at runtime.

Add this to your `AndroidManifest.xml`.

```xml
    <queries>
        <package android:name="com.tencent.mm" />
    </queries>
```

### Declare your callback Activity in your `AndroidManifest.xml`

The WeChat SDK opens the Activity `PACKAGE_NAME.wxapi.WXEntryActivity`. You need to implement that Activity and declare it in your `AndroidManifest.xml`.

```xml
  <activity
    <!-- android:name MUST match the package name you tell WeChat Open Platform -->
    android:name="com.myapp.wxapi.WXEntryActivity"
    <!-- It must be exported, otherwise the WeChat app will not be able to open it -->
    android:exported="true"
    <!-- The official guide says it should be singleTask -->
    android:launchMode="singleTask"
    <!-- android:taskAffinity MUST match the package name you tell WeChat Open Platform -->
    android:taskAffinity="com.myapp"
    android:theme="@android:style/Theme.Translucent.NoTitleBar">
  </activity>
```

### Implement the callback Activity

The full Java name of the Activity must be `PACKAGE_NAME.wxapi.WXEntryActivity`. If the package name you tell WeChat Open Platform is `com.myapp`, then the full Java name of the Activity must be `com.myapp.wxapi.WXEntryActivity`.

Here is an example implementation that is based on BroadcastReceiver:

```java
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.modelmsg.SendAuth;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
    private IWXAPI api;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        api = WXAPIFactory.createWXAPI(this, "wxYOUR_WECHAT_APPID", true);
        api.registerApp("wxYOUR_WECHAT_APPID");

        try {
            Intent intent = getIntent();
            api.handleIntent(intent, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onReq(BaseReq req) {
    }

    @Override
    public void onResp(BaseResp resp) {
        String error = null;

        switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_OK:
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                error = "errcode_cancel";
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                error = "errcode_deny";
                break;
            case BaseResp.ErrCode.ERR_UNSUPPORT:
                error = "errcode_unsupported";
                break;
            default:
                error = "errcode_unknown";
                break;
        }

        if (resp.getType() == ConstantsAPI.COMMAND_SENDAUTH) {
            SendAuth.Resp authResp = (SendAuth.Resp)resp;

            String state = authResp.state;
            Intent intent = new Intent(state);
            intent.putExtra("state", state);

            intent.setPackage(this.getApplicationContext().getPackageName());
            if (error != null) {
                intent.putExtra("error", error);
            } else {
                intent.putExtra("code", authResp.code);
            }

            this.getApplicationContext().sendBroadcast(intent);
        }
        finish();
    }
}
```

### Make sure your Android app is signed by the keystore you tell WeChat Open Platform

On WeChat Open Platform, you need to tell it the MD5 fingerprint of the keystore you use to sign your Android app.

If you use Google Play Console to distribute your Android app, it is likely that you are using [Play App Signing](https://support.google.com/googleplay/android-developer/answer/9842756?hl=en). With Play App Signing, you tell Google the fingerprint of the keystore you originally sign your app. Google removes the signature and uses its managed keystore to sign the app before distributing the app to Play Store. The final app running on the device of course bears the signature of the managed keystore. This is also the signature the WeChat SDK sees when it validates the signature.

To get the MD5 fingerprint of the managed keystore, refer to the following screenshot:

<figure><img src="/files/UNQx6aDcFRkK7As4sfIP" alt="where to find the MD5 fingerprint of the managed keystore"><figcaption><p>where to find the MD5 fingerprint of the managed keystore</p></figcaption></figure>

Note that the MD5 fingerprint you see on Google Play Console is a **colon-delimited uppercase hex string**, while WeChat Open Platform expects you to provide a **plain lowercase hex string**.

For example, suppose the MD5 fingerprint you get from Google Play Console is `D4:1D:8C:D9:8F:00:B2:04:E9:80:09:98:EC:F8:42:7E`, you can use the following shell command to do the conversion.

```sh
$ echo "D4:1D:8C:D9:8F:00:B2:04:E9:80:09:98:EC:F8:42:7E" | sed 's/://g' | tr 'A-Z' 'a-z'
d41d8cd98f00b204e9800998ecf8427e
```

The output `d41d8cd98f00b204e9800998ecf8427e` is in the expected WeChat Open Platform format.

For those of you who produce the final signature of your app using your own keystore, you can use the following shell command to output the MD5 fingerprint.

```sh
keytool -exportcert -keystore "$STORE_FILE" -storepass "$STORE_PASSWORD" -alias "$KEY_ALIAS" -rfc | openssl x509 -noout -fingerprint -md5
```

Where

* `STORE_FILE` is an environment variable pointing to the keystore file on your machine, for example, `./mykeystore.jks`.
* `STORE_PASSWORD` is an environment variable containing the password of the keystore.
* `KEY_ALIAS` is an environment variable indicating which key in the keystore to use.

## Configure Authgear SDK for WeChat

You have 2 actions to take:

1. Switch to `WebKitWebViewUIImplementation` when you initialize the Authgear SDK.
2. Implement the callback / delegate of `WebKitWebViewUIImplementation`.

### Switch to `WebKitWebViewUIImplementation`

{% tabs %}
{% tab title="iOS WebKitWebViewUIImplementation" %}

```swift
import Authgear

let uiImplementation = WKWebViewUIImplementation()
// Put the WeChat redirect URI you added in the Authgear portal here.
uiImplementation.wechatRedirectURI = URL(string: "com.myapp://authgear/open_wechat_app")!
// You need to implement the delegate to bridge between the WeChat SDK and the Authgear SDK.
uiImplementation.authgearDelegate = self

self.authgear = Authgear(
  clientId: "AUTHGEAR_CLIENT_ID",
  endpoint: "https://myapp.authgear.cloud",
  uiImplementation: uiImplementation
)
```

{% endtab %}

{% tab title="Android WebKitWebViewUIImplementation" %}

```java
WebKitWebViewUIImplementation impl = new WebKitWebViewUIImplementation();
impl.setWechatRedirectURI(Uri.parse("com.myapp://authgear/open_wechat_app"));
impl.setAuthgearDelegate(this);
boolean isSsoEnabled = false;
boolean isPreAuthenticatedURLEnabled = false;

this.mAuthgear = new Authgear(
  this.getApplicationContext(),
  "AUTHGEAR_CLIENT_ID",
  "https://myapp.authgear.cloud",
  new TransientTokenStorage(),
  impl,
  isSsoEnabled,
  isPreAuthenticatedURLEnabled,
  null,
  null
);
```

{% endtab %}

{% tab title="React Native WebKitWebViewUIImplementation" %}

```typescript
import authgear, {
  WebKitWebViewUIImplementation,
} from '@authgear/react-native';

authgear.configure({
  clientID: "AUTHGEAR_CLIENT_ID",
  endpoint: "https://myapp.authgear.cloud",
  uiImplementation: new WebKitWebViewUIImplementation({
    ios: {
      wechatRedirectURI: "com.myapp://authgear/open_wechat_app",
    },
    android: {
      wechatRedirectURI: "com.myapp://authgear/open_wechat_app",
    },
    // You need to implement this callback to bridge between the WeChat SDK and the Authgear SDK.
    sendWechatAuthRequest,
  }),
})
```

{% endtab %}

{% tab title="Flutter WebKitWebViewUIImplementation" %}

```dart
import 'package:flutter_authgear/flutter_authgear.dart';

var authgear = Authgear(
  clientID: "AUTHGEAR_CLIENT_ID",
  endpoint: "https://myapp.authgear.cloud",
  uiImplementation: WebKitWebViewUIImplementation(
    options: WebKitWebViewUIImplementationOptions(
      ios: WebKitWebViewUIImplementationOptionsIOS(
        wechatRedirectURI: "com.myapp://authgear/open_wechat_app",
      ),
      android: WebKitWebViewUIImplementationOptionsAndroid(
        wechatRedirectURI: "com.myapp://authgear/open_wechat_app",
      ),
      // You need to implement this callback to bridge between the WeChat SDK and the Authgear SDK.
      sendWechatAuthRequest: sendWechatAuthRequest,
    ),
  ),
)
```

{% endtab %}
{% endtabs %}

### Implement the callback / delegate of `WebKitWebViewUIImplementation`

{% hint style="info" %}
This section assume you have integrated the WeChat SDK according to

* [Add the integration code for the WeChat SDK for iOS](#add-the-integration-code-for-the-wechat-sdk-for-ios)
* [Implement the callback Activity](#implement-the-callback-activity)

In particular,

* On iOS, you implement `WXApiDelegate` on your `AppDelegate`.
* On Android, your `WXEntryActivity` sends a broadcast with `state` being the Intent action.
  {% endhint %}

{% tabs %}
{% tab title="iOS Authgear WeChat" %}
In your `AppDelegate.swift`

```swift
import Authgear

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate, AuthgearDelegate {
  // authgear is assumed to be initialised somewhere else.
  var authgear: Authgear!

  // Implements AuthgearDelegate
  func sendWechatAuthRequest(_ state: String) {
    // Open WeChat using the WeChat SDK.
    let req = SendAuthReq()
    req.scope = "snsapi_userinfo"
    req.state = state
    WXApi.send(req)
  }

  // Implements WXApiDelegate
  func onResp(_ resp: BaseResp) {
    guard let authResp = resp as? SendAuthResp else {
      return
    }

    let errCode = WXErrCode(rawValue: authResp.errCode)
    if errCode == WXSuccess {
      let state = authResp.state
      let code = authResp.code
      // Forward code to Authgear SDK.
      self.authgear?.wechatAuthCallback(code: code, state: state) { result in
        switch result {
        case .success():
          // The code was sent to Authgear successfully.
          // The end-user will proceed the login in the WebView.
          // You do not need to do anything here.
          break
        case let .failure(error):
          // Handle the error returned by the Authgear SDK properly to deliver good user experience.
        }
      }
    } else {
      // Handle the error returned by the WeChat SDK properly to deliver good user experience.
    }
  }
}
```

{% endtab %}

{% tab title="Android Authgear WeChat" %}
In your `MainActivity.java`

```java
public class MainActivity extends AppCompatActivity implements AuthgearDelegate {
  private IWXAPI mWeChatAPI;
  // mAuthgear is assumed to be initialised somewhere else.
  private Authgear mAuthgear;

  @Override
  public void sendWechatAuthRequest(String state) {
      if (!this.mWeChatAPI.isWXAppInstalled()) {
        // Handle WeChat not installed.
        return;
      }

      Context ctx = this.getApplicationContext();

      IntentFilter intentFilter = new IntentFilter(state);
      BroadcastReceiver br;
      br = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
          String state_ = intent.getStringExtra("state");
          if (state_ != null && state_.equal(state)) {
            ctx.unregisterReceiver(br);

            String code = intent.getStringExtra("code");
            String error = intent.getStringExtra("error");
            if (code != null) {
              mAuthgear.wechatAuthCallback(code, state, new OnWechatAuthCallbackListener() {
                @Override
                public void onWechatAuthCallback() {
                  // No need to do anything here.
                }

                @Override
                public void onWechatAuthCallbackFailed(Throwable throwable) {
                  // Handle error.
                }
              });
            } else {
              // Handle error.
            }
          }
        }
      }
      ContextCompat.registerReceiver(ctx, br, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED);

      SendAuth.Req req = new SendAuth.Req();
      req.scope = "snsapi_userinfo";
      req.state = state;
      this.mWeChatAPI.sendReq(req);
  }
}
```

{% endtab %}

{% tab title="React Native Authgear WeChat" %}
In your React Native TypeScript code

```typescript
import {
  NativeModules,
} from "react-native";
import authgear from '@authgear/react-native';

// This is the callback you pass to WebKitWebViewUIImplementation.
async function sendWechatAuthRequest(state: string) {
  // MyWeChatModule is the Native Module you need to implement yourselves to bridge
  // between the WeChat SDK and the Authgear SDK.
  const { code } = await NativeModules.MyWeChatModule.sendWechatAuthRequest(state);
  await authgear.wechatAuthCallback(code, state);
}
```

In your iOS Native Module `MyWeChatModule.h`

```objc
#import <React/RCTBridgeModule.h>

static NSString *const kMyWeChatModuleNotification = @"MyWeChatModuleNotification";

@interface MyWeChatModule : NSObject <RCTBridgeModule>
@end
```

In your iOS Native Module `MyWeChatModule.m`

```objc
#import "MyWeChatModule.h"
#import <React/RCTUtils.h>
#import <WXApi.h>

@interface MyWeChatModule()
@property (nonatomic, strong) RCTPromiseResolveBlock sendWechatAuthResolve;
@property (nonatomic, strong) RCTPromiseRejectBlock sendWechatAuthReject;
@end

@implementation MyWeChatModule

RCT_EXPORT_MODULE(MyWeChatModule);

- (instancetype)init
{
  if ((self = [super init])) {
    // Listen to the notification posted by your WXApiDelegate.
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleWechatAuthResult:)
                                                 name:kMyWeChatModuleNotification
                                               object:nil];
  }
  return self;
}

RCT_EXPORT_METHOD(sendWechatAuthRequest:(NSString *)state
                                resolve:(RCTPromiseResolveBlock)resolve
                                 reject:(RCTPromiseRejectBlock)reject)
{
  self.sendWechatAuthResolve = resolve;
  self.sendWechatAuthReject = reject;

  dispatch_async(dispatch_get_main_queue(), ^{
    SendAuthReq* req = [[SendAuthReq alloc] init];
    req.scope = @"snsapi_userinfo";
    req.state = state;
    [WXApi sendReq:req completion:nil];
  });
}

- (void)handleWechatAuthResult:(NSNotification *)notification
{
  if (notification.userInfo[@"code"]) {
    self.sendWechatAuthResolve(notification.userInfo);
  } else {
    NSString *error = notification.userInfo[@"error"];
    self.sendWechatAuthReject(RCTErrorUnspecified, error, error);
  }
  [self cleanup];
}

- (void)cleanup
{
    self.sendWechatAuthResolve = nil;
    self.sendWechatAuthReject = nil;
}

@end
```

In your `WXApiDelegate`

```objc
-(void) onResp:(BaseResp*)resp
{
  if ([resp isKindOfClass:[SendAuthResp class]]) {
    SendAuthResp *sendAuthResp = (SendAuthResp*)resp;
    NSDictionary<NSString *, id> *payload;
    if (sendAuthResp.errCode == WXSuccess) {
      payload = @{
        @"code": sendAuthResp.code,
        @"state": sendAuthResp.state,
      };
    } else {
      NSString *error;
      switch (resp.errCode) {
        case WXErrCodeUserCancel:
          error = @"errcode_cancel";
          break;
        case WXErrCodeAuthDeny:
          error = @"errcode_deny";
          break;
        case WXErrCodeUnsupport:
          error = @"errcode_unsupported";
          break;
        default:
          error = @"errcode_unknown";
          break;
      }
      payload = @{
        @"error": error,
      };
    }
    // Send the notification to your MyWeChatModule.
    [[NSNotificationCenter defaultCenter] postNotificationName:kMyWeChatModuleNotification
                                                        object:nil
                                                      userInfo:payload];
  }
}
```

In your Android Native Module `MyWeChatModule`

```java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import androidx.core.content.ContextCompat;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

public class MyWeChatModule extends ReactContextBaseJavaModule {
    private IWXAPI wechatAPI;

    WechatAuthModule(ReactApplicationContext context) {
        super(context);

        wechatAPI = WXAPIFactory.createWXAPI(context, "wxYOUR_WECHAT_APPID", true);
        wechatAPI.registerApp("wxYOUR_WECHAT_APPID");
    }

    @Override
    public String getName() {
        return "MyWeChatModule";
    }

    @ReactMethod
    public void sendWechatAuthRequest(String state, Promise promise) {
        if (!wechatAPI.isWXAppInstalled()) {
            promise.reject(new Exception("You have not installed the WeChat client app"));
            return;
        }

        Context ctx = this.getReactApplicationContext();

        IntentFilter intentFilter = new IntentFilter(state);
        BroadcastReceiver br;
        br = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String state_ = intent.getStringExtra("state");
                if (state_ != null && state_.equal(state)) {
                    ctx.unregisterReceiver(br);

                    String code = intent.getStringExtra("code");
                    String error = intent.getStringExtra("error");
                    if (code != null) {
                        WritableMap result = new WritableNativeMap();
                        result.putString("code", code);
                        result.putString("state", state);
                        promise.resolve(result);
                    } else {
                        promise.reject(error, error);
                    }
                }
            }
        }
        ContextCompat.registerReceiver(ctx, br, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED);

        SendAuth.Req req = new SendAuth.Req();
        req.scope = "snsapi_userinfo";
        req.state = state;
        wechatAPI.sendReq(req);
    }
}
```

{% endtab %}

{% tab title="Flutter Authgear WeChat" %}
In your Flutter code

```dart
import 'package:flutter/services.dart';
import 'package:flutter_authgear/flutter_authgear.dart';

// authgear is assumed to be initialised somewhere else.
var authgear;

const _nativeMethodChannel = MethodChannel("MyWeChatMethodChannel");

// This is the callback you pass to WebKitWebViewUIImplementation.
Future<void> sendWechatAuthRequest(String state) async {
  try {
    final code = await _nativeMethodChannel.invokeMethod("sendWechatAuthRequest", {
      "state": state,
    });
    await authgear.wechatAuthCallback(state: state, code: code);
  } on PlatformException catch (e) {
    print("exception: $e");
  }
}
```

In your `AppDelegate.swift`

```swift
import Flutter

@main
@objc class AppDelegate: FlutterAppDelegate, WXApiDelegate {

  private var wechat = [String: FlutterResult]()

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "MyWeChatMethodChannel", binaryMessenger: controller.binaryMessenger)
    channel.setMethodCallHandler {
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      let arguments = call.arguments as! Dictionary<String, AnyObject>
      let state = arguments["state"] as! String
      self.wechat[state] = result
      let req = SendAuthReq()
      req.scope = "snsapi_userinfo"
      req.state = state
      WXApi.send(req)
    }

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
      WXApi.handleOpen(url, delegate: self)
      return super.application(app, open: url, options: options)
  }

  override func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
      WXApi.handleOpen(url, delegate: self)
      return super.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
  }

  override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    WXApi.handleOpenUniversalLink(userActivity, delegate: self)
    return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
  }

  @objc
  func onReq(_ req: BaseReq) {
  }

  @objc
  func onResp(_ resp: BaseResp) {
    guard let authResp = resp as? SendAuthResp else {
      return
    }

    guard let state = authResp.state else {
      return
    }

    guard let result = wechat.removeValue(forKey: state) else {
      return
    }

    var error: String
    switch WXErrCode(rawValue: authResp.errCode) {
    case WXErrCodeUserCancel:
      error = "errcode_cancel"
    case WXErrCodeAuthDeny:
      error = "errcode_deny"
    case WXErrCodeUnsupport:
      error = "errcode_unsupported"
    default:
      error = "errcode_unknown"
    }

    switch WXErrCode(rawValue: authResp.errCode) {
    case WXSuccess:
      let code = authResp.code
      result(code)
    default:
      result(FlutterError(code: error, message: error, details: nil))
    }
  }
}
```

In your `MainActivity.kt`

```kotlin
class MainActivity: FlutterFragmentActivity() {

  private var api: IWXAPI? = null

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)

    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "MyWeChatMethodChannel").setMethodCallHandler { call, result ->
        handleMethodCall(call, result)
    }
  }

  private fun handleMethodCall(call: MethodCall, result: MethodChannel.Result) {
    val api = api!!
    if (!api.isWXAppInstalled) {
        result.error("errcode_not_installed", "errcode_not_installed", null)
        return
    }

    val state = call.argument<String>("state")!!
    val req = SendAuth.Req()
    req.scope = "snsapi_userinfo"
    req.state = state

    var broadcastReceiver: BroadcastReceiver?
    broadcastReceiver = object : BroadcastReceiver() {
      override fun onReceive(context: Context?, intent: Intent?) {
        val state_ = intent.getStringExtra("state")
        if (state_ == state) {
          this@MainActivity.applicationContext.unregisterReceiver(broadcastReceiver)

          val code = intent.getStringExtra("code")
          val error = intent.getStringExtra("error")
          if (code != null) {
            result.success(code)
          } else {
            result.error(error, error, nil)
          }
        }
      }
    }
    val intentFilter = IntentFilter(state)
    ContextCompat.registerReceiver(this.applicationContext, broadcastReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED)
    api.sendReq(req)
  }
}
```

{% endtab %}
{% endtabs %}

## Appendix: Create mobile app on WeChat Open Platform

**Prerequisite**: A WeChat account is required for registering the app.

These information are required for creating the application on the platform, prepare them before creating the application:

### Basic Information

| Field     | Meaning in English                                       | Usage                                                                                                                                                                                                         |
| --------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 移动应用名称    | Mobile app name                                          | The app name shown to the end-user when they login                                                                                                                                                            |
| 英文名称      | Mobile app name in English                               | The app name shown to an English end-user when they login                                                                                                                                                     |
| 移动应用简介    | Mobile app description                                   | A description for the approver to understand the app                                                                                                                                                          |
| 英文简介      | Mobile app description in English                        | Optional field                                                                                                                                                                                                |
| 应用官网      | Official website of the mobile app                       | The webpage should show the description of the app and offer links to download it on app stores                                                                                                               |
| 移动应用图片    | App Icon                                                 | A 28x28 and a 108x108 icon                                                                                                                                                                                    |
| 应用市场      | Is the app released on any app stores for Chinese users? | <p>Choose between:<br>- <strong>Unreleased</strong>: the login is rate limited to 100 per day<br>- <strong>Released:</strong> A Chinese ICP license is required (App备案号), for example: 粤B2-00000000-0000A</p> |
| 应用类目      | App category                                             | Choose from the list of categories defined by the platform that best describe your app                                                                                                                        |
| 应用运行流程图   | Images of app flow                                       | Any flowchart or screenshots showing how the app works                                                                                                                                                        |
| 申请/修改应用说明 | Application details                                      | Reasons for application, any testing account and testing instruction.                                                                                                                                         |
| 管理员身份设置   | Set Admin                                                | Scan the QR code from the WeChat mobile app to prove the admin identity of the app.                                                                                                                           |

### App configuration

#### iOS

* Bundle ID
* 测试 Bundle (Testing Bundle ID)
* Universal Links
* 备用 Universal Links (Backup Universal Links)

#### Android

* 应用包名 (Package name)
* 应用签名 (App Signature)


# Connect Websites to WeChat

{% hint style="info" %}
**WeChat Open Platform account (微信开放平台账号)** is different from **WeChat Official account (微信公众平台账号)**. Authgear supports integrating WeChat Login with a WeChat Open Platform account.
{% endhint %}

## Prerequisite

* Register a [WeChat Open Platform account (微信开放平台账号)](https://open.weixin.qq.com/).
* Register a Web Application (网站应用).

See [Appendix: Create web app on WeChat Open Platform](#appendix-create-web-app-on-wechat-open-platform) for more details.

## Get the information from WeChat Open Platform

Once your mobile app is approved on the platform, you will see the word ":white\_check\_mark: <mark style="color:green;">已通过</mark>" in the app details page.

* Get the `appid` (**Client ID**)

<figure><img src="/files/kISLGn5feBHYO1scZxmf" alt="where to find appid"><figcaption><p>where to find appid</p></figcaption></figure>

* Get the `appsecret` (**Client Secret**). It will only be shown once. You need to re-generate if you lose it.

<figure><img src="/files/u6BnYZWeuEjtQIngDauV" alt="where to find appid"><figcaption><p>where to find appid</p></figcaption></figure>

* Get the `原始ID` (**Account ID**) of your WeChat Open Platform account.

<figure><img src="/files/okxaU54W9x0zb3WmY9fn" alt="where to find account ID"><figcaption><p>where to find account ID</p></figcaption></figure>

## Configure Sign in with WeChat in the Authgear portal

1. Sign in to the Authgear portal.
2. Select your project.
3. In the navigation menu, go to **Authentication > Social / Enterprise Login**.
4. Click **Add Connection**.
5. Select **WeChat Web / 网站应用**.
6. Fill in **Client ID** with the `appid`.
7. Fill in **Client Secret** with the `appsecret`.
8. Fill in **Account ID** with the `原始ID`.
9. Save.

## Done!

No further changes are needed. The Sign in with WeChat button should be shown in the signup / login page now.

## Appendix: Create web app on WeChat Open Platform

After logging into the Open Platform, go to the "网站应用" (Web App) and click "创建网站应用" to create a new web app

The following information are needed:

| Field      | Meaning in English                            | Usage                                                                                  |
| ---------- | --------------------------------------------- | -------------------------------------------------------------------------------------- |
| 网站应用名称     | Web app name                                  | The app name shown to the end-user when they login                                     |
| 英文名称       | Web app name in English                       | The app name shown to an English end-user when they login                              |
| 网站应用简介     | Web app description                           | A description for the approver to understand the app                                   |
| 英文简介       | Mobile app description in English             | Optional field                                                                         |
| 应用官网       | Official website of the web app               | The webpage should show the description of the app                                     |
| 网站应用图片     | App Icon                                      | A 28x28 and a 108x108 icon                                                             |
| 网站信息登记表扫描件 | Scanned copy of the Website Registration Form | A signed registration form about the web app and the business nature and company info. |
| 授权回调域      | Authorized redirect domain                    | Domain of your Authgear endpoint for example: `*.authgear.cloud`                       |


# Enterprise Login Providers


# Connect Apps to Microsoft Entra ID (Azure AD)

{% hint style="info" %}
**Microsoft Entra ID** is the new name for **Azure Active Directory (Azure AD)**. Microsoft renamed the product in 2023; existing Azure AD configurations and integrations continue to work without change. This page uses the current name, but you may still see "Azure AD" in older portals or documentation.
{% endhint %}

## Prerequisite

1. Create a Microsoft Entra ID account [here](https://azure.microsoft.com/free)
2. Setup a tenant by completing [Quickstart: Set up a tenant](https://learn.microsoft.com/en-us/entra/fundamentals/create-new-tenant)
3. Register an application by completing [Quickstart: Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)
4. Choose "Supported account types", the following options are supported:

   * Single tenant only - \<your tenant> (Accounts in this organizational directory only)
   * Multiple Entra ID tenants (Accounts in any organizational directory - Multitenant)
   * Any Entra ID tenant + Personal Microsoft accounts (Multitenant and personal Microsoft accounts, e.g. Skype, Xbox)

   "Personal accounts only" is not supported yet. Remember the account type chosen as this affects the configuration on Authgear portal
5. Configure "Redirect URI" with `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/azureadv2`. See [How to add a redirect URI to your application](https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-redirect-uri).
6. Follow [this](https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials?tabs=client-secret) section to add a client secret. Remember to record the secret value when you add the client secret, as it will not be displayed again. This will be needed for configure OAuth client in Authgear.

{% hint style="info" %}
Redirect URI has the form of `/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal.
{% endhint %}

## Configure Sign in with Microsoft through the portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with Microsoft**
3. Fill in **Client ID** with **Application (client) ID** of your just created Microsoft Entra ID application.
4. Fill in **Client Secret**" with the secret you get after creating a client secret for your Microsoft Entra ID application.
5. For **Tenant** field:
   * If **single tenant (first option)** is chosen, fill in the **Directory (tenant) ID** of your Microsoft Entra ID application.
   * If **multi tenant (second option)** is chosen, fill in the string literal `organizations`.
   * If **multi tenant and personal account (third option)** is chosen, fill in the string literal `common`.
6. **Save** the settings.

🎉 Done! You have just added Microsoft Entra ID (Azure AD) Login to your apps!

### Force Login page

Microsoft Entra ID automatically logs in to the same account without requiring a username and password. To prevent this behaviour, you can use the `prompt=login` parameter to force Microsoft Entra ID to show the login page. See our [guide on using the prompt=login parameter](/authentication-and-access/social-enterprise-login-providers/force-social-enterprise-login-providers-to-show-login-screen) in Authgear SDKs to learn more.


# Connect Apps to Microsoft AD FS

## Prerequisite

1. Setup your own AD FS server
2. Create an application in your AD FS Server, obtain "Client ID", "Client Secret" and "Discovery Document Endpoint". Discovery Document Endpoint typically ends with `/.well-known/openid-configuration`. Configure your application with redirect uri `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/adfs`.

{% hint style="info" %}
Redirect URI has the form of `/sso/oauth2/callback/:oauth_provider_alias`. The `oauth_provider_alias` is the OAuth Provider Alias configured for this provider in Authgear Portal.
{% endhint %}

## Configure Sign in with Microsoft AD FS through the portal

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with Microsoft AD FS**.
3. Fill in **Client ID**, **Client Secret** and **Discovery Document Endpoint**.
4. **Save** the settings.

🎉 Done! You have just added Microsoft AD FS Login to your apps!

### Force Users to Re-authenticate

Microsoft AD FS supports the `prompt=login` parameter. You can include this parameter in your request when you want users to re-authenticate. See our [guide on using the prompt=login parameter](/authentication-and-access/social-enterprise-login-providers/force-social-enterprise-login-providers-to-show-login-screen) in Authgear SDKs to learn more.


# Connect Apps to Azure AD B2C

## Prerequisite

1. Sign in [Microsoft Azure](https://portal.azure.com/).
2. Create a B2C tenant by following [this tutorial](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant).
3. Enable self-service sign-up for the tenant by following [this doc](https://docs.microsoft.com/en-us/azure/active-directory/external-identities/self-service-sign-up-user-flow#enable-self-service-sign-up-for-your-tenant)
4. Go back the main page of [Microsoft Azure](https://portal.azure.com/) and search for "Azure AD B2C"
5. Create a app registration for Authgear by following [this guide](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga).
6. Configure "Redirect URI" with `https://<YOUR_AUTHGEAR_ENDPOINT>/sso/oauth2/callback/azureadb2c`.
7. Follow [this guide](https://docs.microsoft.com/en-us/azure/active-directory-b2c/add-sign-up-and-sign-in-policy?pivots=b2c-user-flow) to create a sign-up and sign-in user flow.
8. After creating the user flow, configure it

* Open "Application Claims".
* Make sure "Email Addresses" is checked.

## Configure Sign in with Azure AD B2c through the portal

If you have finished the above prerequisite, you should have the following information:

1. The **Tenant Name**, obtained in Step 2
2. The **Application (Client) ID**, obtained in Step 5
3. The **Policy (User flow) Name**, obtained in Step 7

Then in Authgear portal, do the following:

1. In the portal, go to **Authentication > Social / Enterprise Login**.
2. Enable **Sign in with Microsoft Azure AD B2C**.
3. Fill in **Client ID** with the **Application (Client) ID** above.
4. Fill in **Client secret** with the client secret you get when you create the app registration.
5. Fill in **Tenant** with the Azure AD B2C **Tenant Name**.
6. Fill in **Policy** with the **Policy (User Flow) Name**. Normally it starts with `b2c_`.
7. **Save** the changes

🎉 Done! You have just added Azure AD B2C Login to your apps!

### Force Login page

Azure AD B2C automatically logs in to the same account without requiring a username and password. To prevent this behaviour, you can use the `prompt=login` parameter to force Azure AD B2C to show the login page. See our [guide on using the prompt=login parameter](/authentication-and-access/social-enterprise-login-providers/force-social-enterprise-login-providers-to-show-login-screen) in Authgear SDKs to learn more.


# Account Linking

Connect a social or enterprise login to an existing account that shares the same email, instead of creating a duplicate user or rejecting the signup.

When you add social login to an app that already has users, some of them will click **Continue with Google** even though they originally signed up with an email and password. Both identities carry the same email address, but by default Authgear treats the Google signup as an attempt to create a second account and rejects it with an "account already exists" error.

Account linking changes what happens next. Instead of an error, Authgear detects that the incoming Google account matches an existing user, asks the user to log in to that account once to prove they own it, then attaches the Google identity to it. The user ends up with one account and two ways to sign in.

{% hint style="info" %}
Account linking runs automatically **during signup**. To let an already signed-in user connect a provider from your own UI, see [Link and Unlink Social/Enterprise Provider with the SDK](/authentication-and-access/social-enterprise-login-providers/link-and-unlink-social-enterprise-provider-with-the-sdk). Users can also manage their connections on the [User Settings](/customization/built-in-ui/user-settings) page.
{% endhint %}

## When to use it

Enable account linking when the same person can reach your app through more than one signup method and you want them to keep a single account. Typical situations:

* **You added Google login to an app with existing password users.** A user who signed up with `alice@example.com` and a password clicks **Continue with Google**. Their Google account uses the same email. With account linking they log in with their password once, and from then on can sign in with either method.
* **You offer several social providers.** A user signs up with Google today and taps **Continue with Apple** on their phone next month. Both accounts share one email; linking keeps them on one Authgear account instead of two.
* **You migrated users into Authgear and later enabled an enterprise provider.** Imported users match their Microsoft Entra ID or ADFS accounts by email, so their first enterprise login attaches to the account you imported rather than creating a fresh one.

## How it works

When a user signs up with a social or enterprise provider, Authgear reads a claim from the provider's user profile (the email, by default) and compares it against the identities of existing users. What happens on a match depends on the `action` you configure:

| Action           | Behavior                                                                                                                                                           |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `error`          | Reject the signup with a "duplicated identity" error. This is the default when account linking is not configured.                                                  |
| `login_and_link` | Show the user their existing account and ask them to log in to it. After they authenticate, add the new provider identity to that account and continue the signup. |

{% hint style="warning" %}
The login step in `login_and_link` prevents account takeover. Some identity providers let anyone register with an unverified email, so an email match alone doesn't prove ownership. Without the login step, an attacker could register `alice@example.com` at such a provider and hijack Alice's account.
{% endhint %}

## Enable account linking

This walkthrough uses Google, but the same configuration works for any provider you have set up.

### Prerequisites

* A Google login provider configured in the Portal under **Authentication > Social / Enterprise Login**, with an alias. This guide assumes the alias `google`. See [Connect Apps to Google](/authentication-and-access/social-enterprise-login-providers/social-login-providers/google) if you haven't set this up.

### Add the configuration

Configure account linking in your project's YAML config. In the Portal, go to **Advanced > Edit Config** and add an `account_linking` section:

```yaml
account_linking:
  oauth:
    - alias: google
      oauth_claim:
        pointer: "/email"
      user_profile:
        pointer: "/email"
      action: login_and_link
```

Save the config. The change takes effect immediately.

Each entry under `account_linking.oauth` defines one linking rule:

| Field          | Meaning                                                                                                                                                     |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alias`        | Which provider this rule applies to. Must match the alias of a provider under **Social / Enterprise Login**.                                                |
| `oauth_claim`  | A JSON pointer to the claim to read from the incoming provider profile. `/email` reads the email Google reports.                                            |
| `user_profile` | A JSON pointer to the attribute to match against existing users' identities. `/email` matches users whose email login ID or standard attribute is the same. |
| `action`       | What to do on a match: `error` or `login_and_link`.                                                                                                         |

{% hint style="info" %}
`oauth_claim` and `user_profile` accept only `/email`, `/phone_number`, and `/preferred_username`. Matching by email covers Google and most social providers; `/preferred_username` is useful for enterprise providers such as ADFS that identify users by username.
{% endhint %}

### What the user experiences

With the config above in place, here is the full flow for a user who already has a password account under `alice@example.com`:

1. On the signup page, the user clicks **Continue with Google** and completes Google's consent screen.
2. Authgear finds the existing account and shows an **Existing account found** page listing it with a masked identifier, such as `al***@example.com`.
3. The user selects the account and logs in with their existing method, in this case their password.
4. Authgear links the Google identity to the account. If your signup flow requires steps the account doesn't satisfy yet (for example, setting up two-factor authentication), the user completes them now; steps the account already satisfies are skipped.
5. The user is signed in. From now on, both the password and Google sign the user in to the same account.

## Linking email, phone, or username signups

Account linking also covers the reverse direction: a user who signed up with Google first, then tries to sign up with the same email address and a password. Configure it under `account_linking.login_id`:

```yaml
account_linking:
  login_id:
    - key: email
      user_profile:
        pointer: "/email"
      action: login_and_link
```

`key` names the login ID type (`email`, `phone`, or `username` by default). When a signup with that login ID matches an existing user, the user logs in to the existing account (in this case, through Google) and the new email login ID is added to it. Without this config, the conflicting signup fails with an error, as before.

## Advanced: different rules per signup flow

If you define [custom authentication flows](/authentication-and-access/authentication/custom-authentication-flow), you can override a linking rule inside a specific signup flow step. Give the rule a `name` in `account_linking`, then reference that name in the step to override its `action` or pick which login flow handles the login:

```yaml
account_linking:
  oauth:
    - name: google_linking
      alias: google
      oauth_claim:
        pointer: "/email"
      user_profile:
        pointer: "/email"
      action: error
authentication_flow:
  signup_flows:
    - name: customer_signup
      steps:
        - name: identify
          type: identify
          one_of:
            - identification: oauth
              account_linking:
                oauth:
                  - name: google_linking
                    action: login_and_link
                    login_flow: customer_login
```

Here Google linking fails with an error everywhere except the `customer_signup` flow, which allows it and runs the `customer_login` flow for the login step. When `login_flow` is not set, Authgear uses the login flow with the same name as the current signup flow.

## Account linking in Custom UI

If you build your own signup UI with the [Authentication Flow API](/customization/custom-ui/authentication-flow-api), account linking appears as an `identify` action whose `data.type` is `account_linking_identification_data`. The data contains the list of matched accounts with masked display names; your UI submits the index of the account the user picks, and the flow continues into the login steps. See the [Authentication Flow API reference](https://github.com/authgear/authgear-server/blob/main/docs/specs/authentication-flow-api-reference.md) for the exact schema.


# Force Social/Enterprise Login Providers to Show Login Screen

Use OIDC prompt parameter to force OAuth providers to show login screen.

The `prompt="login"` parameter which is defined in the [OIDC spec](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) can prompt Social/Enterprise Login Providers to always show their login screen. As a result, you can use the `prompt="login"` parameter to allow users to switch accounts when their previous authentication session on the provider is still stored.

In this post, you'll learn how to use `prompt: "login"` in Authgear SDKs to force OIDC providers to always show your users their login screen. You can also use `prompt: "login"` to allow users to switch accounts with a Social/Enterprise Login provider on the same device (or browser).

### How to Force Social/Enterprise Login Providers to Show Login Page

Authgear SDKs have a `prompt` parameter that you can set in your application. The value of the prompt parameter will be passed to the Social/Enterprise Login provider. Hence, if you set `prompt: "login"` in the SDK, your Social/Enterprise Login provider will receive a `prompt="login"` parameter. The following code examples show how to use the prompt parameter in Authgear SDKs.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
authgear
  .startAuthentication({
    redirectURI: "<AUTHGEAR_REDIRECT_URI>",
    prompt: PromptOption.Login,
  })
```

{% endtab %}

{% tab title="React Native" %}

```typescript
authgear
  .authenticate({
    redirectURI: 'com.reactnativeauth://host/path',
    prompt: PromptOption.Login,
  })
```

{% endtab %}

{% tab title="Android" %}

```java
AuthenticateOptions options = new AuthenticateOptions("<AUTHGEAR_REDIRECT_URI>");
List<PromptOption> promptOptions = Arrays.asList(PromptOption.LOGIN);
options.setPrompt(promptOptions);
mAuthgear.authenticate(options, new OnAuthenticateListener() {
    @Override
    public void onAuthenticated(@Nullable UserInfo userInfo) {
        
    }

    @Override
    public void onAuthenticationFailed(@NonNull Throwable throwable) {
        Log.d(TAG, throwable.toString());
    }
});
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear?.authenticate(
    redirectURI: "<AUTHGEAR_REDIRECT_URI>",
    prompt: "login"
)
```

{% endtab %}

{% tab title="Flutter" %}

```dart
_authgear.authenticate(
        redirectURI: "<AUTHGEAR_REDIRECT_URI>",
        prompt: "login",
      );
```

{% endtab %}
{% endtabs %}


# Link and Unlink Social/Enterprise Provider with the SDK

Let signed-in users connect a social or enterprise login provider to their account, and disconnect it later, from your own UI. This guide uses Google as the example provider, but the same code works for any OAuth provider you have configured.

{% hint style="info" %}
These SDK methods are currently available in the **web SDK (`@authgear/web`) only**. For other SDKs, use the [User Settings](/customization/built-in-ui/user-settings) page and users can manage their Social/Enterprise connection there.
{% endhint %}

### What you will build

A page in your app, such as an account details page, that:

1. Reads the signed-in user's linked identities.
2. Shows a **Link** button when the provider is not connected, or an **Unlink** button when it is.
3. Links the provider by redirecting to Google's consent screen, then back to your app.
4. Unlinks the provider by redirecting to an Authgear-hosted confirmation page, then back to your app.

Both actions use a browser redirect. The user leaves your app, completes the step on Google's or Authgear's page, and returns to a redirect URI you control. Your callback code calls a `finish` method to complete the action.

### Prerequisites

* The `@authgear/web` SDK installed and configured in your app. See [Getting Started](/get-started/start-building).
* The user is **already signed in**. Linking and unlinking require an authenticated session. Both methods throw if no valid session exists.
* The OAuth provider is configured in the Authgear Portal under **Social / Enterprise Login**, with an alias. This guide uses the alias `google`.
* The redirect URI you pass to each method is registered under your application's **Redirect URIs** in the Portal.

The examples assume your app configures the SDK with `sessionType: "refresh_token"`.

### Step 1: Check whether the provider is linked

Call `fetchUserInfo()` to get the user's info, then inspect the `identities` array. Each OAuth identity has `type === IdentityType.OAuth` and an `oauthProviderAlias` that matches the alias you configured in the Portal.

```typescript
import authgear, { IdentityType } from "@authgear/web";

const PROVIDER_ALIAS = "google";

async function isProviderLinked() {
  const userInfo = await authgear.fetchUserInfo();
  return (userInfo.identities ?? []).some(
    (identity) =>
      identity.type === IdentityType.OAuth &&
      identity.oauthProviderAlias === PROVIDER_ALIAS
  );
}
```

The raw `identities` claim from the [UserInfo endpoint](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo) looks like this:

```json
{
  "https://authgear.com/claims/user/identities": [
    {
      "type": "login_id",
      "login_id_key": "email",
      "login_id_type": "email"
    },
    {
      "type": "oauth",
      "oauth_provider_type": "google",
      "oauth_provider_alias": "google"
    }
  ]
}
```

### Step 2: Show the Link or Unlink button

Render the button that matches the current state.

{% tabs %}
{% tab title="React" %}

```tsx
import { useEffect, useState } from "react";
import authgear, { IdentityType } from "@authgear/web";

const PROVIDER_ALIAS = "google";

function ProviderLinkButton() {
  const [linked, setLinked] = useState<boolean | null>(null);

  useEffect(() => {
    authgear.fetchUserInfo().then((userInfo) => {
      const isLinked = (userInfo.identities ?? []).some(
        (i) =>
          i.type === IdentityType.OAuth &&
          i.oauthProviderAlias === PROVIDER_ALIAS
      );
      setLinked(isLinked);
    });
  }, []);

  if (linked === null) {
    return <p>Loading…</p>;
  }

  return linked ? (
    <button onClick={startUnlink}>Unlink Google</button>
  ) : (
    <button onClick={startLink}>Link Google</button>
  );
}
```

{% endtab %}

{% tab title="JavaScript" %}

```html
<div id="provider-container"></div>

<script type="module">
  import authgear, { IdentityType } from "@authgear/web";

  const PROVIDER_ALIAS = "google";
  const container = document.getElementById("provider-container");

  const userInfo = await authgear.fetchUserInfo();
  const linked = (userInfo.identities ?? []).some(
    (i) =>
      i.type === IdentityType.OAuth &&
      i.oauthProviderAlias === PROVIDER_ALIAS
  );

  const button = document.createElement("button");
  if (linked) {
    button.textContent = "Unlink Google";
    button.onclick = startUnlink;
  } else {
    button.textContent = "Link Google";
    button.onclick = startLink;
  }
  container.appendChild(button);
</script>
```

{% endtab %}
{% endtabs %}

### Step 3: Link the provider

Call `startLinkOAuth()`. The SDK redirects the browser to Google's consent screen, where the user signs in and approves the link. After approval, Google redirects back to your `redirectURI`.

```typescript
function startLink() {
  authgear
    .startLinkOAuth({
      oauthProviderAlias: PROVIDER_ALIAS,
      redirectURI: "https://myapp.com/oauth-callback",
      state: "link_oauth",
    })
    .catch((err) => {
      console.error(err);
    });
}
```

| Option               | Type   | Required | Description                                                                                                       |
| -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `oauthProviderAlias` | string | Yes      | The provider alias configured in the Portal, e.g. `google`.                                                       |
| `redirectURI`        | string | Yes      | Where the browser returns after the flow. Must be registered in the Portal.                                       |
| `state`              | string | No       | An OAuth state value returned to your callback. You can use it to tell link, unlink, and sign-in callbacks apart. |

### Step 4: Unlink the provider

Call `startUnlinkOAuth()`. The SDK redirects the browser to an Authgear-hosted page with an unlink button. The user confirms there, and Authgear redirects back to your `redirectURI`.

```typescript
function startUnlink() {
  authgear
    .startUnlinkOAuth({
      oauthProviderAlias: PROVIDER_ALIAS,
      redirectURI: "https://myapp.com/oauth-callback",
      state: "unlink_oauth",
    })
    .catch((err) => {
      console.error(err);
    });
}
```

The options are the same as `startLinkOAuth()`. Use a different `state` value so your callback knows which action to finish.

### Step 5: Handle the callback

When the user returns to your `redirectURI`, finish the action. Read the `state` query parameter to choose between `finishLinkOAuth()` and `finishUnlinkOAuth()`, then send the user back to your account details page.

{% tabs %}
{% tab title="React" %}

```tsx
import { useEffect } from "react";
import authgear from "@authgear/web";

function OAuthCallback() {
  useEffect(() => {
    async function finish() {
      // configure() must run before any finish method.
      await authgear.configure({
        clientID: "your-client-id",
        endpoint: "https://myapp.authgear.cloud",
        sessionType: "refresh_token",
      });

      const state = new URL(window.location.href).searchParams.get("state");

      try {
        if (state === "link_oauth") {
          await authgear.finishLinkOAuth();
        } else if (state === "unlink_oauth") {
          await authgear.finishUnlinkOAuth();
        }
        // Both actions are done. Return to the account details page.
        window.location.replace("/account");
      } catch (err) {
        console.error(err);
      }
    }
    finish();
  }, []);

  return <p>Finishing…</p>;
}
```

{% endtab %}

{% tab title="JavaScript" %}

```html
<script type="module">
  import authgear from "@authgear/web";

  // configure() must run before any finish method.
  await authgear.configure({
    clientID: "your-client-id",
    endpoint: "https://myapp.authgear.cloud",
    sessionType: "refresh_token",
  });

  const state = new URL(window.location.href).searchParams.get("state");

  try {
    if (state === "link_oauth") {
      await authgear.finishLinkOAuth();
    } else if (state === "unlink_oauth") {
      await authgear.finishUnlinkOAuth();
    }
    // Both actions are done. Return to the account details page.
    window.location.replace("/account");
  } catch (err) {
    console.error(err);
  }
</script>
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
`finishLinkOAuth()` and `finishUnlinkOAuth()` reject with an `OAuthError` if the user cancels or the flow fails. Catch the error and show the user a message before redirecting.
{% endhint %}

### Related pages

* [UserInfo endpoint reference](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo): the full `identities` claim.


# FAQ for Authentication


# OTP Test Mode

Suppress message delivery and fix OTP for testing purposes

Test mode allows for control over how OTPs are handled during development and testing.

Common use cases include:

* Internal testing (e.g. Allowing specific accounts to authenticate with fixed codes for ease of testing).
* Testing SMS, WhatsApp, or email login via OTP, but don't want to rely on the actual OTP delivery.
* App Store Reviewers. See [Tips for Apple App Store Review with Passwordless Login](/faq-for-authentication/passwordless-demo-user-for-apple-app-review).

## How to enable test mode

In your portal, navigate to Advanced > Edit Config

<figure><img src="/files/lvbzKyP5vn0T2V4i1Ew8" alt=""><figcaption></figcaption></figure>

Then add the following to the yaml:

```yaml
test_mode:
  oob_otp:          # rules defined below apply to all OOB channels (email, sms, whatsapp)
    enabled: true
    rules:
    - regex: .*
      fixed_code: "000000" 
  sms:              # channel specific rules defined here
    enabled: true
    rules:
    - regex: .*
      suppressed: true
```

How this works

* `regex`: Defines target emails/phone numbers the rule applies to using a regular expression
* `fixed_code`: A 6-digit code accepted as the OTP for targets
* `suppressed`: `false` by default. If `true`, messages will not be sent to the target
* `enabled`
  * Set to `true` to enable test mode
  * `false` by default. This means the OTP flow will work as expected, and fixed codes will not be accepted for the channel.

## Examples

### Case 1: Development

In development, you may want to use a fixed code for testing.

```yaml
test_mode:
  oob_otp:
    enabled: true
    rules:
    - regex: .*
      fixed_code: "000000"
  sms:
    enabled: true
    rules:
    - regex: ^\+85291231234$ # SMS not suppressed for +85291231234
    - regex: .*
      suppressed: true
  whatsapp:
    enabled: true
    rules:
    - regex: .*
      suppressed: true
  email:
    enabled: true
    rules:
    - regex: .*
      suppressed: true
```

In this case,

* The rule in `oob_otp` matches all target so a fixed OTP `000000` will be used.
* SMS is not suppressed for `+85291231234` only. They will receive `000000` via SMS so the SMS sending capability can be tested.
* No messages will be sent to WhatsApp or email channels

{% hint style="info" %}
Since `rules` are evaluated top to bottom, always order them from most to least specific.
{% endhint %}

If the rules in `sms` were reversed in the example above, the `.*` regex would match all accounts first, making more specific rules unreachable.

### Case 2: App review

During app review, you can enable "test mode" for a specific phone number or email address that allows the account to authenticate with a fixed OTP. So this account can be shared with the app reviewer to complete the passwordless login flow

```yaml
test_mode:
  oob_otp:
    enabled: true
    rules:
    - regex: ^\+85291231234$
      fixed_code: "000000"
  sms:
    enabled: true
    rules:
    - regex: ^\+85291231234$
      suppressed: true
  whatsapp:
    enabled: true
    rules:
    - regex: ^\+85291231234$
      suppressed: true
```

In this example,

* Only `+85291231234` can be passed with the fixed code `000000` and other accounts should use real OTP.
* SMS and WhatsApp are suppressed for `+85291231234`


# Tips for Apple App Store Review with Passwordless Login

How to pass the Apple Store review process if your app uses passwordless login.

When you try to publish a mobile app on the Apple AppStore, there will be an [App Review process](https://developer.apple.com/app-store/review/). You need provide a demo user account for the reviewers to access the features of the app.

However, passwordless login via email/phone OTP cannot be used in the review because the reviewer do not have access to the email inbox or phone number of that demo account.

To work around this, you can enable "test mode" for a specific phone number or email address that allows the account to authenticate with a fixed OTP (e.g. `000000`).

## How to enable "test mode"

1. Navigate to Advanced > Edit Config
2. paste the following to the yaml:

```yaml
test_mode:
  oob_otp:
    enabled: true
    rules:
    - fixed_code: "000000"
      regex: (\+85291231234$|me@example\.com$)
  sms:
    enabled: true
    rules:
    - suppressed: true
      regex: \+85291231234$
  whatsapp:
    enabled: true
    rules:
    - suppressed: true
      regex: \+85291231234$
  email: 
    enabled: true
    rules:
    - suppressed: true
      regex: me@example\.com$
```

In this example,

* `+85291231234` and `me@example.com` will not recieve any messages.
* They can log in using the fixed OTP `000000`.
* All other users will continue to receive OTPs normally.


# Phone Number Validation

Use the Phone Number Validation Settings to configure your app to check the phone number your users enter without depending on SMS or WhatsApp for validation and verification.

Under the hood, the Phone Number Validation Settings uses the [libphonenumber](https://github.com/google/libphonenumber) library. The library can parse, format, and validate phone numbers for all countries/regions in the world.

To access Phone Number Validation settings in your Authgear project, navigate to **Authentication** > **Login Methods** in the Authgear Portal.

Next, select a login method that uses mobile phone number as login ID (e.g., **Mobile**, **Mobile/Email,** or **Custom** with phone number enabled as login ID). Then, scroll down to the Phone Number Validation section. Select **validate phone number with libphonenumber** or validate only the country code and phone number length.

<figure><img src="/files/attVVmeCeAbJbdtAvV5Z" alt=""><figcaption><p>Enable phone number validation with libphonenumber</p></figcaption></figure>

{% hint style="info" %}
**Note**: The phone number validation logic may become outdated due to recent changes in the numbering style in certain countries/regions. In such a case, you can **disable** the Phone Number Validation feature to enable users to register new numbers that the logic is unable to recognize.
{% endhint %}


# Skip Login Screen and Direct Users to Enterprise Login

Learn how to take users directly to an external OAuth provider's authorization page without opening AuthUI login page.

You can use Authgear's `x_oauth_provider_alias` parameter to add social/enterprise login to your application without showing any AuthUI pages. To do this, you must enable the **Social/Enterprise only** login method for your project in Authgear Portal.

In this post, you'll learn how to use the `x_oauth_provider_alias` parameter to skip AuthUI and take users directly to a social/enterprise login provider's authorization page.

### Pre-requisite

* An Authgear account. Create one for free [here](https://authgear.com).

### What We Will Build

* In this post, we'll walk through the steps for adding only the social/enterprise login method to an Authgear application.
* We'll use the Authgear SDK for React Native to set the `x_oauth_provider_alias` parameter and show how to use `x_oauth_provider_alias` without the SDK in an example Express app.

<figure><img src="/files/desQJNNHvKlLZFNCuzlN" alt=""><figcaption></figcaption></figure>

The sequence diagram above demonstrates the flow for using `x_oauth_provider_alias` to skip AuthUI.

First, when the user clicks on the sign-in button, your application will call your Authgear project's `authorize` endpoint with the x\_oauth\_provider\_alias parameter appended.

Next, when Authgear server receives the call, it redirects to the third-party OAuth (Social/EnterpriseLogin) provider's authorization server. The user is then shown the OAuth provider's login/authorization page for them to grant authorization. Once that is done, the OAuth provider returns an authorization code to Authgear.

In the next step, Authgear exchanges the authorization code for an access token, refresh token, and ID token and then starts creating a new user or logging the existing user in.

The rest of the flow from there involves the usual sending of the authorization code to the client application and the client application exchanging the authorization code for an access token, refresh token, and ID token.

### Step 1: Configure a Social/Enterprise Login Provider

The first step is to add the Social/Enterprise login provider you wish to use to your Authgear project. For our example, we'll be adding Facebook.

To add a new provider, log in to Authgear Portal, select your project, then navigate to **Authentication** > **Social/Enterprise Login**.

Next, click on the **Edit** button, then enter the **Client ID** and **Client Secret** for the Social/Entreprise login provider then click **Save**. Also, note the value for **Alias** as you'll use it in a later step.

See our guide [here](/authentication-and-access/social-enterprise-login-providers/social-login-providers/facebook) for instructions on how to get a Client ID and Client Secret for Facebook Login.

<figure><img src="/files/cQ9ap6UekAsudQQzpyPr" alt=""><figcaption></figcaption></figure>

### Step 2: Enable Social/Enterprise Only Login Method

An important step for making Authgear to skip AuthUI is to enable the Social/Enterprise only login method. By doing this, Authgear will understand that the only login method your app will use is from a third-party OAuth provider. As a result, it's ok to skip showing AuthUI for login, registration, or login method selection and go to the OAuth provider's authorization page directly.

To enable Social/Enterprise only, navigate to **Authentication** > **Login Methods**. Next, select the **Social/Enterprise only** Login Method and click **Save**.

<figure><img src="/files/aUQjg8XHKgfCFNbLvBjW" alt=""><figcaption></figcaption></figure>

### Step 3: Set x\_oauth\_provider\_alias Parameter

Now that you've set up everything to allow your application to use only Social/Enterprise Login, you can open Authgear's `authorize` endpoint with the `x_oauth_provider_alias` parameter to start an authorization request that will skip AuthUI.

We will show 2 ways to do this. The first is using the SDK and the second is by passing `x_oauth_provider_alias` as a URL query parameter.

#### 1. Adding x\_oauth\_provider\_alias using Authgear SDK

The following example shows how to add `x_oauth_provider_alias` using the Authgear React Native SDK:

```javascript
authgear
  .configure({
    clientID: '<YOUR_CLIENT_ID>',
    endpoint: '<YOUR_AUTHGEAR_PROJECT_ENDPOINT>',
  })
  .then(() => {
    authgear
      .authenticate({
        redirectURI: 'com.reactnativeauth://host/path',
        oauthProviderAlias: 'facebook',
      })
      .then(({userInfo}) => {
        Alert.alert('Login successful, welcome ' + userInfo.email);
      });
  });
```

The key thing about using the above option is the presence of `oauthProviderAlias: 'facebook'` in the `authenticate()` method of the Authgear SDK. This parameter tells Authgear to redirect directly to an OAuth provider, given that the Social/Enterprise provider is configured properly as shown in [step 1](#step-1-configure-a-social-enterprise-login-provider). Also, the **Social/Enterprise only** Login method is enabled.

Note that the value for `oauthProviderAlias` must be the Alias for the social/enterprise provider you configured in step 1.

#### 2. Add x\_oauth\_provider\_alias to the Authorization URL

You can manually add `x_oauth_provider_alias` parameter to the Authgear authorization endpoint when you're not using the Authgear SDK.

The following example shows how to add `x_oauth_provider_alias` to the Authgear authorization endpoint:

```javascript
app.get("/login", (req, res) => {
  const url = new URL("<YOUR_AUTHGEAR_PROJECT_ENDPOINT>/oauth2/authorize");
  url.searchParams.set('client_id', config.client.id);
  url.searchParams.set('redirect_uri', "http://localhost:3000");
  url.searchParams.set('scope', "openid offline_access");
  url.searchParams.set('x_oauth_provider_alias', 'facebook')

  res.redirect(url);

});
```

The above code will append `&x_oauth_provider_alias=facebook` to the authorization URL.


# Use the OAuth 2.0 State Parameter

Reference on what the OAuth 2.0 parameter is and how to use it in Authgear SDK.

The [OAuth 2.0 framework](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1) includes an optional `state` parameter. The value of the `state` parameter can be any random string or number defined by a client application (e.g. a web or mobile that uses Authgear for user authentication) before making an authorization request. In fact, the `state` parameter is added to the authorization URL as a URL query.

The authorization server (Authgear) will include the value of the state parameter when redirecting the user-agent back to the client application. As a result, the client application can retrieve the value of `state` returned to verify that it is the origin of the authorization request.

In this post, we'll cover some possible usage of the `state` parameter and how to include the `state` parameter in an authorization request to the Authgear server.

## Use cases of the State Parameter

The following are some use cases of the OAuth 2.0 state parameter.

### 1. Customize Post Login/Sign up User Experience

Because the value for the state parameter passed at the beginning of an authorization request is returned unchanged after authorization, you can use this behavior to customize the post-login or sign-up user experience.

For example, you can show users some custom messages after they sign up or log in, using a special link that was sent to them via email or SMS. The "special" thing in the link would be the value of a query parameter that can be passed in the `state` parameter.

Then, a client application can read the value of the `state` parameter and based on that, determine when and how to display the custom message or user experience.

### 2. Analytics

Another possible use of the `state` parameter is analytics and tracking user behavior. You can use the state token to include a unique key that tracks your campaigns. This way, you can know the number of users who sign up or log in to your application from a particular campaign.

You can also use the value you specify in the `state` parameter in an analytic tool [Mixpanel](https://docs.mixpanel.com/docs/tracking-methods/id-management/identifying-users) (for example, as `id` in the `identify(id)` function) to track user's behavior pre-login and post-login.

To learn more about using the state parameter for tracking user behavior, see our detailed guide [here](/integration/faq-for-integration/track-user-before-and-after-signup).

### 3. Security: To Prevent Cross-site Request Forgery (CSRF)

Cross-site Request Forgery or short CSRF is a type of web security vulnerability where the attacker uses malicious means to trick a user into performing undesired actions on sites they use and trust. This type of attack usually targets users who are signed in and attempts to compromise access to their protected resources.

In OAuth, an attacker can perform a Cross-site Request Forgery using the client application's redirect URI. The attacker can trick a user into using a redirect URI that contains their authorization code or access token. Hence the user will end up using the access token and protected resources of the attacker. When they save new data using this access token, the attacker can also view them (as they are the original owner of the protected resources).

The official Authgear SDKs have mechanisms for protecting your applications from CSRF built into them.

However, if you are not using the official SDK, you can secure your application by generating a random hard-to-guess value on the client application and passing it in the `state` parameter. Your application should store this value securely on the user's client-side using session cookies or some other form of local storage. Then, verify the `state` parameter in the redirect URI against the value stored locally to confirm that a user-agent is the origin of an authorization request before exchanging the authorization code for an access token.

## Examples: Including the State Parameter in Authorization Request

The following URL shows an example of an authorization request URL:

```
https://your_project.authgear.cloud/login?client_id=your_authgear_app_client_id&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth-redirect&state=12345678
```

As you can see from the above URL, `state` is a query parameter in addition to other parameters like the `client_id` and `redirect_uri`.

If you're constructing the authorization URL manually, you can include the state parameter by simply appending "`&state=random_state_value`" to the authorization URL.

Alternatively, if you're using any of the Authgear SDKs, you can use the built-in `state` field to set a value.

The following code samples show the use of the state parameter with Authgear.

{% tabs %}
{% tab title="JavaScript (React)" %}
**Step 1: Set up a React Project to use Authgear**

Create a new React project or use an existing project and configure the project to use Authgear. The following example is based on our [React example Git repository](https://github.com/authgear/authgear-example-react).

First, install the Authgear web SDK by running the following command:

```sh
npm install @authgear/web
```

Next, configure Authgear in your React Project's index.tsx file like this:

```typescript
import authgear from "@authgear/web";

export const endpoint = "https://your_project.authgear.cloud"; // The Authgear endpoint of your project e.g. https://my-app.authgearapps.com
export const clientID = ""; // Client ID can be obtained in the "Applications" page of the Portal

async function init() {
  try {
    await authgear.configure({
      endpoint,
      clientID,
      sessionType: "refresh_token",
    });
  } finally {
    createRoot(document.getElementById("react-app-root")!).render(<App />);
  }
}

// eslint-disable-next-line no-console
init().catch((e) => console.log(e));
```

**Step 2: Include State Parameter in Authorization Request**

Set the `state` field in your call to the `startAuthentication()` method of the Authgear SDK to a random hard-to-guess value based on your use case.

```typescript
  const startLogin = useCallback(() => {
    authgear
      .startAuthentication({
        redirectURI: "http://localhost:4000/auth-redirect",
        prompt: PromptOption.Login,
        state: "12345678"
      })
      .then(
        () => {
          // started authorization, user should be redirected to Authgear
        },
        (err) => {
          // failed to start authorization
          console.error(err);
        }
      );
  }, []);
```

**Step 3: Read and Use the Value of State Returned After the Authorization**

Implement the component that handles your OAuth 2.0 redirect like this:

```typescript
const AuthRedirect: React.FC = () => {
  const usedToken = useRef(false);

  const navigate = useNavigate();

  useEffect(() => {
    async function updateToken() {
      try {
        const u = new URL(window.location.href);
        const params = u.searchParams;
        const state = params.get("state") ?? undefined
        
        if (state !== undefined) {
          const initialState = "12345678"; // In a real app store the initial value on the client side using something like session cookies.
          //compare value of state returned in redirectURL to initial value set in startAuthentication()
          if (state === initialState) {
            //values match, do things log state to an analytic tool, set custom URL to navigate user to...
            console.log("state parameter match");
          } else {
            //the value for state param return does not match, do something like stopping the authentication
            console.log("state parameter dont match");
            return;
          }
        }
        
        await authgear.finishAuthentication();
      } finally {
        navigate("/");
        usedToken.current = true;
      }
    }

    if (!usedToken.current) {
      updateToken().catch((e) => console.error(e));
    }
  }, [navigate]);

  return <></>;
};
```

The above code will read the value of the state parameter returned in the redirect and compare it to the initial value.

For this example, when the initial value of the `state` parameter before authorization is not the same as the value returned in the redirectURL, we halt the authentication process.
{% endtab %}

{% tab title="PHP" %}
**Step 1: Set up PHP Project**

This example uses the [League OAuth 2.0 client](https://oauth2-client.thephpleague.com/) PHP package. Install the package using the following command:

```sh
composer require league/oauth2-client
```

Next, configure your PHP to use Authgear like this:

```php
<?php
require 'vendor/autoload.php';
session_start(); 

$appUrl = "https://your_project.authgear.cloud";
$clientID = "";
$clientSecret = "";
$redirectUri = "http://localhost:8081/";

$provider = new \League\OAuth2\Client\Provider\GenericProvider([
    'clientId'                => $clientID,    // The client ID assigned to you by the provider
    'clientSecret'            => $clientSecret,    // The client password assigned to you by the provider
    'redirectUri'             => $redirectUri,
    'urlAuthorize'            => $appUrl . '/oauth2/authorize',
    'urlAccessToken'          => $appUrl . '/oauth2/token',
    'urlResourceOwnerDetails' => $appUrl . '/oauth2/userInfo',
    'scopes' => 'openid offline_access'
]);

if (!isset($_GET['code'])) {
    // Fetch the authorization URL from the provider; this returns the
    // urlAuthorize option and generates and applies any necessary parameters
    // (e.g. state).
    $authorizationUrl = $provider->getAuthorizationUrl();

    // Get the state generated for you and store it to the session.
    $_SESSION['oauth2state'] = $provider->getState();

    // Redirect the user to the authorization URL.
    header('Location: ' . $authorizationUrl);
    exit;
}
```

The League OAuth 2.0 client we are using in this example helps us generate random strings for the `state` parameter. In the above code, we store the value for the state generated in session on the line with `$_SESSION['oauth2state'] = $provider->getState()`;.

**Step 2: Read and Use the Value of State**

Add an else block for the `if (!isset($_GET['code']))` condition with the following code:

```php
else {
    $code = $_GET['code'];

    if (empty($_GET['state']) || empty($_SESSION['oauth2state']) || $_GET['state'] !== $_SESSION['oauth2state']) {
        if (isset($_SESSION['oauth2state'])) {
            unset($_SESSION['oauth2state']);
        }

        exit('Invalid state');
    } else {
        try {
            $accessToken = $provider->getAccessToken('authorization_code', [
                'code' => $code
            ]);
            echo "Login successful ". $accessToken;
            
        } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
            // Failed to get the access token or user details.
            exit($e->getMessage());
        }
    }
}
```

The above code will prevent your PHP application from exchanging an authorization code for an access token when the value of `state` stored in the PHP session is not identical to the state returned in the redirect URI. The usage demonstrated above can prevent CSRF attacks.
{% endtab %}
{% endtabs %}


# Local Dev Setup for Connecting to Authgear SaaS in Cookie-based Auth

Setting up the local development environment for websites that use cookie-based authentication.

This guide provides a simple way to bootstrap your local application that communicates with the Authgear SaaS server.

{% hint style="warning" %}
This guide is only for cookie-based authentication in local development setups. A typical scenario is developing websites. If you are developing mobile applications which use token-based authentication, you can just skip this guide and continue with it as usual.
{% endhint %}

## Problems using localhost as a local website domain

Authgear sets a cookie in the browser inside the project domain. When running your application on `localhost`, the browser will not see the cookies because the website is not in the subdomain of the domain in which the cookies are set. Therefore, the browser will not be able to authenticate itself.

## Setup a new Authgear project

> For local development, it is highly recommended to create a new application on Authgear before continuing to the rest of the guide.

1. Log in and create a new project on <https://portal.authgear.com/>
2. Go to the **Application** tab in your dashboard
3. Add your local application domain `{SUBDOMAIN}.{PROJECT_NAME}.authgear.cloud` under the **Allowed Origins** list
4. Add an application, name it whatever you want. **DO NOT check the Issue JWT as access token** box because we are using cookie-based authorization.
5. Put your redirect URI for login and logout under the **Redirect URIs** list and **Post Logout Redirect URIs** list respectively.

## Map domain in `hosts`

To make the cookies visible to the browser, the local website domain has to be inside the domain where the cookies are set.

By adding the following line to the `/etc/hosts` file on the local machine, we can point the application domain name to localhost.

```
127.0.0.1 {SUBDOMAIN}.{PROJECT_NAME}.authgear.cloud
```

The browser will be able to see the auth cookies if visiting the website via this domain.

## Use HTTPS

Although you can see the cookies now, the cookies have the **Secure** attribute set. To include them in an HTTP request, the request has to be transmitted over a secure channel (**HTTPS** in most browsers). Therefore, we also need to establish **HTTPS** connections for our browser with the server.

### Generate certificates

One quick simple way to do this is to use [mkcert](https://github.com/FiloSottile/mkcert), you may follow the installation steps [here](https://github.com/FiloSottile/mkcert#installation). After installing mkcert, generate a certificate with the following command:

```
mkcert "*.{PROJECT_NAME}.authgear.cloud"
```

A key file and a cert file will be generated. They will be used in the next part of the guide.

### Using nginx

We will need an **nginx** server to serve the certificate and enable SSL.

Add the following config file to your `nginx/conf.d` directory, or mount it to a volume together with the cert and key if you are using nginx in docker.

The examples below show the nginx config files for nginx in host and nginx in docker.

{% tabs %}
{% tab title="nginx in host" %}

```
server {
  listen       443 ssl;
  server_name  {SUBDOMAIN}.{PROJECT_NAME}.authgear.cloud;

  ssl_certificate      /path/to/your/cert;
  ssl_certificate_key  /path/to/your/key;

  location / {
    # Change it to your service endpoint
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;

    auth_request /_auth;
    auth_request_set $x_authgear_session_valid $upstream_http_x_authgear_session_valid;
    auth_request_set $x_authgear_user_id $upstream_http_x_authgear_user_id;
    auth_request_set $x_authgear_user_anonymous $upstream_http_x_authgear_user_anonymous;
    auth_request_set $x_authgear_user_verified $upstream_http_x_authgear_user_verified;
    auth_request_set $x_authgear_session_acr $upstream_http_x_authgear_session_acr;
    auth_request_set $x_authgear_session_amr $upstream_http_x_authgear_session_amr;
    auth_request_set $x_authgear_session_authenticated_at $upstream_http_x_authgear_session_authenticated_at;
    auth_request_set $x_authgear_user_can_reauthenticate $upstream_http_x_authgear_user_can_reauthenticate;

    proxy_set_header x-authgear-session-valid $x_authgear_session_valid;
    proxy_set_header x-authgear-user-id $x_authgear_user_id;
    proxy_set_header x-authgear-user-anonymous $x_authgear_user_anonymous;
    proxy_set_header x-authgear-user-verified $x_authgear_user_verified;
    proxy_set_header x-authgear-session-acr $x_authgear_session_acr;
    proxy_set_header x-authgear-session-amr $x_authgear_session_amr;
    proxy_set_header x-authgear-session-authenticated-at $x_authgear_session_authenticated_at;
    proxy_set_header x-authgear-user-can-reauthenticate $x_authgear_user_can_reauthenticate;
  }

  location /_auth {
    internal;
    resolver 8.8.8.8;
    set $resolver https://{PROJECT_NAME}.authgear.cloud/_resolver/resolve;
    proxy_pass $resolver;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
  }
}
```

{% endtab %}

{% tab title="nginx in docker" %}
{% hint style="info" %}
Docker `host` network driver is not supported in Docker Desktop, it has to be in a `bridge` network. If your nginx in docker needs to proxy requests to services in your host network, it needs to resolve `host.docker.internal` through `127.0.0.11`. If your services are also in the same docker bridge network (i.e. same docker-compose without specifying multiple networks), the destination domain will be the container name.
{% endhint %}

```
server {
  listen       443 ssl;
  server_name  {SUBDOMAIN}.{PROJECT_NAME}.authgear.cloud;

  ssl_certificate      /path/to/your/cert;
  ssl_certificate_key  /path/to/your/key;

  location / {
    resolver 127.0.0.11;
    # change {CONTAINER_NAME} to host.docker.internal if accessing host
    proxy_pass http://{CONTAINER_NAME}:{PORT};
    proxy_set_header Host $host;

    auth_request /_auth;
    auth_request_set $x_authgear_session_valid $upstream_http_x_authgear_session_valid;
    auth_request_set $x_authgear_user_id $upstream_http_x_authgear_user_id;
    auth_request_set $x_authgear_user_anonymous $upstream_http_x_authgear_user_anonymous;
    auth_request_set $x_authgear_user_verified $upstream_http_x_authgear_user_verified;
    auth_request_set $x_authgear_session_acr $upstream_http_x_authgear_session_acr;
    auth_request_set $x_authgear_session_amr $upstream_http_x_authgear_session_amr;
    auth_request_set $x_authgear_session_authenticated_at $upstream_http_x_authgear_session_authenticated_at;
    auth_request_set $x_authgear_user_can_reauthenticate $upstream_http_x_authgear_user_can_reauthenticate;

    proxy_set_header x-authgear-session-valid $x_authgear_session_valid;
    proxy_set_header x-authgear-user-id $x_authgear_user_id;
    proxy_set_header x-authgear-user-anonymous $x_authgear_user_anonymous;
    proxy_set_header x-authgear-user-verified $x_authgear_user_verified;
    proxy_set_header x-authgear-session-acr $x_authgear_session_acr;
    proxy_set_header x-authgear-session-amr $x_authgear_session_amr;
    proxy_set_header x-authgear-session-authenticated-at $x_authgear_session_authenticated_at;
    proxy_set_header x-authgear-user-can-reauthenticate $x_authgear_user_can_reauthenticate;
  }

  location /_auth {
    internal;
    resolver 8.8.8.8;
    set $resolver https://{PROJECT_NAME}.authgear.cloud/_resolver/resolve;
    proxy_pass $resolver;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
  }
}
```

{% endtab %}
{% endtabs %}

In the above examples, nginx will also authenticate requests by creating sub-requests to the Authgear internal endpoint. You can learn more [here](https://docs.authgear.com/deploy-on-your-cloud/auth-nginx#add-nginx).

## Finish

Now visit the website through `https://<yourapp>.authgear.cloud`, and the browser will be able to send requests with the authorization cookies.

For implementing login and logout logic in your website, please refer to [Web SDK](/get-started/single-page-app/website).


# Add custom fields to a JWT Access Token or ID Token

Learn how to add custom attributes to a JWT Access Token or ID Token using Authgear

JWTs (JSON Web Tokens) are a common method for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. With Authgear, it is straightforward to add custom fields to your JWT access tokens or ID Tokens.

This how-to guide will walk you through the process of **adding custom fields such as** [**User Profiles**](/integration/user-profiles) **attributes to a JWT access token/ID token** payload using Authgear and Javascript [Hooks](/customization/events-hooks).

Here's an example of the [fields in the JWT Access Tokens by default](/reference/tokens/jwt-access-token) and an explanation of their values.

{% hint style="info" %}
You can also [add custom attributes](/integration/user-profiles) to [User Profiles](/integration/user-profiles) on the Authegear Portal.
{% endhint %}

## Prerequisites

* **An Authgear account:** You need an Authgear account to follow this guide. If you don't have one, you can [create it for free](https://accounts.portal.authgear.com/signup) on the Authgear website.
* **A Registered App:** You need a [registered application](https://docs.authgear.com/get-started/website#setup-application-in-authgear) (client) in Authgear.

## Mutation on Access Tokens

### Enable Access Token for your App

Make sure the option **Issue JWT as access token** is enabled in your **Application** settings in the Portal.

1. Log into your [Authgear account](https://portal.authgear.com/?_ga=2.25390521.563520449.1688969336-1174359617.1686657394).
2. Navigate to the **Applications** tab and choose the existing App.
3. On the **App Configuration** dashboard, locate the "**Access token**" section.
4. Make the toggle **Issue JWT as access token** switch on.

<figure><img src="/files/UcvaBft4Rqi5IrNYdhcM" alt="" width="537"><figcaption></figcaption></figure>

### Create a new Event Hook

With the use of Hooks, Authgear provides flexibility for adding custom logic to your authentication pipeline. You can create a Hook which is triggered any of these [Events ](/customization/events-hooks)about to occur. For example, `oidc.jwt.pre_create` the event happens just before issuing the JWT access token and it can be used to put extra information into the token.

1. Navigate to your Authgear Dashboard's **Advanced**->**Hooks** section.
2. **Add** a new **Blocking Event**.
3. Choose the Block Hook **Type** as the *TypeScript* and set the Event option to `oidc.jwt.pre_create`. You will write a new Typescript function from scratch.

<figure><img src="/files/GJe4uwb9BsphevqUKrKC" alt=""><figcaption></figcaption></figure>

4. Click on **Edit Script** under the **Config** option.
5. Copy and paste the following into the editor:

```typescript
import { EventOIDCJWTPreCreate, EventOIDCJWTPreCreateHookResponse } from "https://deno.land/x/authgear_deno_hook@v2.0.0/mod.ts";

export default async function(e: EventOIDCJWTPreCreate): Promise<EventOIDCJWTPreCreateHookResponse> {
  return {
    mutations:{
      jwt: {
        payload:{
          ...e.payload.jwt.payload,
          standard_attributes: e.payload.user.standard_attributes,
          custom_attributes: e.payload.user.custom_attributes
        }
      }
    },
    is_allowed: true
  };
}
```

6. Click on **Finish Editing**.
7. Back to the **Hooks** page from the navigation bar and click on the **Save** button at the top of the page.

In the above code, we are importing the necessary modules such as `EventOIDCJWTPreCreateHookResponse` and `EventOIDCJWTPreCreate` which are types from the Authgear [Deno](https://deno.land/) hook [Typescript library](https://deno.land/x/authgear_deno_hook). We modify the JWT payload by adding [Standard Attributes](/integration/user-profiles)(`e.payload.user.standard_attributes`) and [Custom Attributes](/integration/user-profiles)(`e.payload.user.custom_attributes`) of the user.

### Verify the Custom Field in a JWT token

There are two ways to test it:

* You can do this by [decoding the JWT token](/get-started/backend-api/jwt) on your application server side using a JWT decoder and inspecting the payload.
* If you created the application type **OIDC Client Application,** you need to follow the steps below. Expand it to see instructions.

<details>

<summary>Verify the custom field in the JWT Token with <strong>OIDC Client Application</strong></summary>

This part explains how to retrieve an access token using **OpenID App** Endpoints and check if newly added custom attributes are in place in the JWT Access token.

**Prerequisites**

* Make sure that you have a registered app type of **OIDC Client Application** in Authgear Portal.

**Step 1: Obtain the necessary parameters**

Open your **OpenID Auth App** configuration, and find **Client ID**, **Client Secret**, and check **Authorization**, and **Token** endpoints. You will use them in the next steps.

![](/files/VRHmR9TwzHTLEfNua9pA) ![](/files/ifrIQ9Ncj42oF06E95p1)

**Step 2: Construct the authorization endpoint URL**

The URL for this endpoint is usually provided by the authorization server and includes parameters specifying the requested `scope`, `client_id`, and response\_type. Here's an example URL for the authorization endpoint:

```
https://<YOUR_AUTHGEAR_ENDPOINT>/oauth2/authorize?client_id={YOUR_CLIENT_ID}&response_type=code&scope=openid
```

Replace `<YOUR_AUTHGEAR_ENDPOINT>` with your Authgear server's domain, `YOUR_CLIENT_ID` with your application's Client ID from **OpenID App.**

**Step 3: Redirect the user to the authorization endpoint**

Next, you need to redirect the user to the authorization endpoint. You can just put the URL in your browser and log in with a user credential you are interested to retrieve an access token for. After successful authentication and consent, the Authgear will redirect the user back to your specified redirect URI, including an **authorization code** as a query parameter. You will need the code in the next step

<img src="/files/mLaxhcRUXao0HOWlYsSU" alt="" data-size="original">

**Step 4: Obtain an access token**

You need to make a request to the **OpenID App's Token endpoint** to exchange the authorization code we retrieved in the previous step for an access token.

* The token endpoint URL is usually something like `https://<YOUR_AUTHGEAR_ENDPOINT>/oauth2/token`.
* Include parameters such as `grant_type=authorization_code`, `code=AUTHORIZATION_CODE`, `client_id=YOUR_CLIENT_ID`, `client_secret=YOUR_CLIENT_SECRET`, and `redirect_uri=YOUR_REDIRECT_URI`.
* Make a POST request to the token endpoint to obtain the access token.

```bash
curl --request POST \
  --url 'https://<YOUR_AUTHGEAR_ENDPOINT>/oauth2/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=authorization_code \
  --data code={YOUR_AUTHORIZATION_CODE} \
  --data redirect_uri={YOUR_REDIRECT_URI} \
  --data 'client_id={YOUR_CLIENT_ID}' \
  --data client_secret={YOUR_CLIENT_SECRET} \
  --data scope=openid
```

**Step 5: Verify custom attributes in the access token**

Finally, we can debug the access token using the [JWT Debugger tool](https://www.authgear.com/tools/jwt-jwe-debugger) to see if the custom field and value we added previously are there inside the JWT payload.

<img src="/files/ZyGh8sqHUUXhAX41nwkU" alt="" data-size="original">

\\

</details>

## Mutation on ID Tokens

With the use of Hooks, Authgear provides flexibility for adding custom logic to your authentication pipeline. You can create a Hook which is triggered any of these [Events ](/customization/events-hooks/blocking-events)about to occur. For example, `oidc.id_token.pre_create` the event happens just before issuing the JWT access token and it can be used to put extra information into the token.

1. Navigate to your Authgear Dashboard's **Advanced**->**Hooks** section.
2. **Add** a new **Blocking Event**.
3. Choose the Block Hook **Type** as the *TypeScript* and set the Event option to `oidc.id_token.pre_create`. You will write a new Typescript function from scratch.

<figure><img src="/files/RmCE5LufulzmUo83fofw" alt=""><figcaption></figcaption></figure>

4. Click on **Edit Script** under the **Config** option.
5. Copy and paste the following into the editor:

```typescript
import { EventOIDCIDTokenPreCreate, EventOIDCIDTokenPreCreateHookResponse } from "https://deno.land/x/authgear_deno_hook@v2.0.0/mod.ts";

export default async function(e: EventOIDCIDTokenPreCreate): Promise<EventOIDCIDTokenPreCreateHookResponse> {
  const customAttributes = e.payload?.user?.custom_attributes ?? null;
  return {
    is_allowed: true,
    mutations: {
      id_token: {
        payload: {
          ...e.payload.id_token.payload,
          custom_attributes: customAttributes
        }
      }
    }
  }
}

```

6. Click on **Finish Editing**.
7. Back to the **Hooks** page from the navigation bar and click on the **Save** button at the top of the page.

In the above code, we are importing the necessary modules such as `EventOIDCIDTokenPreCreateHookResponse` and `EventOIDCIDTokenPreCreate` which are types from the Authgear [Deno](https://deno.land/) hook [Typescript library](https://deno.land/x/authgear_deno_hook). We modify the JWT payload by adding [Custom Attributes](/integration/user-profiles)(`e.payload.user.custom_attributes`) of the user.


# User Profiles


# What is User Profile

The user profiles contain information about your end-users such as name, email, addresses, and their unique identifier. You can manage the profiles via the Portal & Admin API. The end-users can also manage their own profile through the Profile section in the [User Setting page](/customization/built-in-ui/user-settings) provided by the AuthUI.

The complete information in the user profiles is a combination of standard attributes and custom attributes. Attributes are a way of grouping the fields of the user profile information. With standard attributes containing common fields, you'll find in a user profile, hence the names of these fields are set by Authgear. You set custom fields on the other hand based on the unique needs of your project.

## Standard Attributes

The following attributes are built-in supported by Authgear. They are the set of [**Standard Claims** defined by the OIDC specifications](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). Some of them are default hidden from the Admin Portal and end-users. Their visibility and mutability can be configured through the Admin Portal.

| Attribute name | Default Visibility | Format                                                                                |
| -------------- | ------------------ | ------------------------------------------------------------------------------------- |
| Name           | Hidden             | String                                                                                |
| Given Name     | Editable           | String                                                                                |
| Family Name    | Editable           | String                                                                                |
| Middle Name    | Hidden             | String                                                                                |
| Nickname       | Hidden             | String                                                                                |
| Profile        | Hidden             | URL String                                                                            |
| Picture        | Editable           | URL String                                                                            |
| Website        | Hidden             | URL String                                                                            |
| Gender         | Editable           | `male`, `female` or Custom String                                                     |
| Birthdate      | Editable           | Date in YYYY-MM-DD                                                                    |
| Timezone       | Editable           | [tz database zone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) |
| Language       | Editable           | BCP47 language tag enabled by the project                                             |
| Address        | Hidden             | JSON Object                                                                           |

### Standard Attributes that are coupled with Identities

The following attributes are coupled with the [identities](/concepts/identity-fundamentals) owned by the end-user. They represent the email addresses, phone numbers, or usernames the end-users are using to authenticate themselves on Authgear. If the end-user uses a third-party identity provider for authentication, these attributes will be coupled with the corresponding attributes returned by the provider.

The standard attributes coupled with identities are listed below:

* `email`
* `email_verified`
* `phone_number`
* `phone_number_verified`
* `preferred_username`

The above attributes are coupled with identities when a user specifies them during sign-up, when users verify their email address or phone number, or from **User Management** > **Users** > select a user > **Identities** in Authgear portal or via the Admin API.

The Login Methods enabled for an Authgear project affect the identities available.

## Custom Attributes

You can define a set of custom attributes in the user profile. They are returned as a JSON object in under the `custom_attributes` key in `userInfo`.

```json5
{
    ...,
    "custom_attributes": {
        "department": "example department"
    }
}
```

### Add new custom attributes

Go to **Portal** > **User Profile** > **Custom Attributes** and click **Add New Attribute**

The custom attribute name should consist of lowercase letters (a-z), digits (0-9) and underscore (\_) only. It must start with lowercase letters (a-z), and NOT end with an underscore (*\_*). The default display name will be the attribute name split with underscore and in title case. e.g. `my_string` will render as `My String` in the [AuthUI Settings page](/customization/built-in-ui/user-settings).

Authgear supports the following attribute types:

* String
* Number
* Integer
* Dropdown
* Phone Number
* Email Address
* URL
* Country Code

### Modify custom attributes

You can change a custom attribute name and validation settings such as min and max value. The attribute type cannot be changed once it's set. To migrate the attribute into a new type, create a new attribute, migrate the values from the old to the new one, and then change the name and access right of the old attribute to make it obsolete.

### Delete custom attributes

Deleting custom attributes is not supported. You can change the name and access rights to make an attribute obsolete.

### Custom attribute order

You can arrange the attribute order by drag-and-drop the handle in the custom attribute configuration in the Portal. This will control the order of how the attributes are shown to the end-users in the [AuthUI User Settings page](/customization/built-in-ui/user-settings).

## User Profile Configuration

The access rights for different parties on individual attributes can be configured through the Authgear Portal. Under the hood, all the attributes are available, however, they can be configured to be `hidden` or `read-only` according to the needs of your projects to avoid confusion.

These are the parties that have access to the user profile:

### The Admin API

Through [the Admin API](/reference/apis/admin-api), developers **ALWAYS** have **full access** to **ALL** the standard attributes and custom attributes. The Admin API allows the developer to view or edit the standard attributes and the custom attributes.

### The Portal

The admin user can view or edit the standard attributes via the Authgear Portal.

### The Session Bearer

The session bearer is someone who has a valid session cookie or a valid access token. The standard attributes of the end-user whom the session represents can be viewed by accessing [the UserInfo endpoint](#userinfo-endpoint) and [the resolver endpoint](/get-started/backend-api/nginx). The session bearer can be the end-user, the client mobile app, or the client website.

### The End-user

The end-user can view or edit the standard attributes through the Profile section in the [User Setting page](/customization/built-in-ui/user-settings) provided by the AuthUI.

## Profiles from Third-party Identity Providers

Authgear supports various [social and enterprise identity providers](/authentication-and-access/social-enterprise-login-providers). End-users can sign up and log in to your apps via these connections. Upon signup, these providers will return a set of user attributes about the end-user. Authgear will copy those attributes and populate the profile of the end-user.

More info about the population logic can be found in [the specification](https://github.com/authgear/authgear-server/blob/master/docs/specs/user-profile/design.md#standard-attributes-population).


# Access User Profiles

Learn how to access User Profiles

To access any of the applications in your Authgear account, each user must have a profile in the account. [User Profiles](/integration/user-profiles/user-profile) contain information about your users such as name, contact information, and [standard](/integration/user-profiles/user-profile#standard-attributes) and[ custom attributes](/integration/user-profiles/user-profile#custom-attributes) you define. You can retrieve and manage user profiles in the following ways:

* [From the Authgear UI portal](#id-1.-access-user-profiles-from-the-authgear-ui-portal).
* [From your apps using Authgear SDKs](#id-2.-access-user-profiles-from-apps-using-authgear-sdks).
* [From Admin API](#id-3.-access-user-profiles-from-admin-api).
* [From the OIDC UserInfo endpoint](#id-4.-access-user-profiles-from-the-oidc-userinfo-endpoint).
* [Embed User Profiles into JWT](#id-5.-embed-user-profiles-into-jwt)

{% hint style="info" %}
The standard attributes in UserProfile in OIDC are a standardized schema for representing the end-users identity information and you can not add or delete them. To introduce additional attributes, use custom attributes.
{% endhint %}

### User Profiles Access Right

Access Right defines what information can be viewed or modified when a user's profile is accessed via the Authgear Portal, User Info endpoint, or User Settings page.

An Access Right can be set for each profile attribute under a specific medium of accessing user profiles. For example, in the following screenshot, the Primary Email attribute has the Editable Access Right for Portal, `Read-only` for User Info endpoint (also known as access Token Bearer), and `Editable` for User Settings page. In simpler terms, the example means an admin can modify Primary Email in Authgear Portal, while a client application can use a user's access token to view their Primary Email. Finally, users can edit their own Primary Email from the User Settings page.

<figure><img src="/files/KdsYebg4FtmyDVcqTTpX" alt=""><figcaption></figcaption></figure>

#### Access Right Options

The following are the available Access Right options that can be applied to each attribute for a specific medium.

* **Editable:** Grants a medium access to view and modify the value of an attribute.
* **Read-only:** A medium with this access for an attribute can only view the value of the attribute.
* **Hidden:** If the access for an attribute is set to `Hidden` for a medium, that medium can not view or modify the attribute.

By default, Authgear sets different access rights for the standard attributes on each way of accessing user profiles. You can view and configure this default access right from **User Profile** > **Standard Attributes** in the Authgear Portal.

For Custom Attributes, you can define the access right for each way of accessing a user profile from **User Profile** > **Custom Attributes** in the portal.

Each way of accessing user profiles belongs to one of the following Access Right Categories:

* **Portal Admin Access Right:** Use this category to set the access right of an attribute in the Authgear Portal for an admin user.
* **Token Bearer Access Right:** This sets the access right of an attribute for the User Info endpoint when a token bearer accesses it. It can be "Hidden" or "Read-Only".
* **End-user Access Right:** Use this to set the access right of the attribute for the default User Settings UI when accessed by an end-user.

## How to Retrieve and Manage User Profiles

### Prerequisites

* **An Authgear account:** You need an Authgear account to follow this guide. If you don't have one, you can[ create it for free](https://accounts.portal.authgear.com/signup) on the Authgear website.
* **A Registered App:** You need a [registered application](https://docs.authgear.com/get-started/website#setup-application-in-authgear) (client) in Authgear.

### 1. Access user profiles from the Authgear UI portal

It is the fastest and easiest way to view user profiles and manage them.

<details>

<summary>Expand this to see the guide</summary>

To view see user profile of a specific user:

1. Go to the [Authgear Portal](https://portal.authgear.com/).
2. Navigate to **User Management.**
3. Choose a user you would like to view a profile.
4. On the **User Details** page, you will see the **Profile** tab.

<img src="/files/RNues4UkLsmE3QrK6fHw" alt="" data-size="original">

To manage access for standard attributes for all users:

Go to **Portal** > **User Profile** > **Standard Attributes**

<img src="/files/MRyB2Li4YWpAJSnghopM" alt="" data-size="original">

To manage access and add new custom attributes for all users:

Go to **Portal** > **User Profile** > **Custom Attributes** and click **Add New Attribute**

<img src="/files/IAh73EuTG4qGfUESZbaO" alt="" data-size="original">

</details>

### 2. Access user profiles from apps using Authgear SDKs

Once Authgear completes authentication and returns control to your application, it provides the user profile to the application. Most developers prefer to use the **Authgear SDKs** to get the UserInfo object using the **fetch user info** function. To start using this function read [getting started guides](/get-started/start-building) relevant to the **SDK of your choice**. Here are some code snippets that call the fetch user info function for different Authgear SDKs:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
try{
    const userInfo = await authgear.fetchUserInfo()
    // example: output the 'sub' attribute from userInfo to console
    // sub is a unique identifier for each user.
    // You can read other attributes such as email and phoneNumber depending on your login method
    console.log(userInfo.sub)
} catch(e) {
    // failed to fetch user info
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
authgear.fetchUserInfo { userInfoResult in
    // sessionState is now up to date
    // it will change to .noSession if the session is invalid
    let sessionState = authgear.sessionState

    switch userInfoResult {
    case let .success(userInfo):
        // read the userInfo if needed
        let userId = userInfo.sub
        let email = userInfo.email
    case let .failure(error):
        // failed to fetch user info
        // the refresh token maybe expired or revoked
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
authgear.fetchUserInfo(new OnFetchUserInfoListener() {
    @Override
    public void onFetchedUserInfo(@NonNull UserInfo userInfo) {
        // sessionState is now up to date
        // read the userInfo if needed
        // example: output the 'sub' attribute from userInfo to LogCat
        // sub is a unique identifier for each user.
        // You can read other attributes such as email and phoneNumber depending on your login method
        Log.d("UserID:", userInfo.sub);
    }

    @Override
    public void onFetchingUserInfoFailed(@NonNull Throwable throwable) {
        // sessionState is now up to date
        // it will change to NO_SESSION if the session is invalid
    }
});
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
  UserInfo userInfo = await authgear.getUserInfo();
  // read the userInfo if needed
  // example: output the 'sub' attribute from userInfo to console
  // sub is a unique identifier for each user.
  // You can read other attributes such as email and phoneNumber depending on your login method
  print(userInfo.sub);
} catch (e) {
  // failed to fetch user info
  // the refresh token maybe expired or revoked
}
// sessionState is now up to date
SessionState state = authgear.sessionState;
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
try
{
    var userInfo = await authgear.FetchUserInfoAsync()
    // example: output the 'sub' attribute from userInfo to console
    // sub is a unique identifier for each user.
    // You can read other attributes such as email and phoneNumber depending on your login method
    console.log(userInfo.sub)
}
catch
{
    // failed to fetch user info
    // the refresh token maybe expired or revoked
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
See the [UserInfo page](https://docs.authgear.com/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo) for more details about the shape of user info and the available user profile attributes.
{% endhint %}

### 3. Access user profiles from Admin API

Authgear provides an [Admin API](/reference/apis/admin-api) GraphQL endpoint that allows applications and services to access and manipulate the User Profile object. The [API Explorer](/reference/apis/admin-api#api-explorer) lets users interactively explore the Admin API. With the API Explorer, you can search for users' profiles or update their standard or custom attributes. See the example steps of how to achieve this below:

<details>

<summary>Expand this to see the guide</summary>

1. Go to the **Portal**.
2. Navigate to the **Advanced** -> **Admin API.**
3. Find a section called **GraphiQL Explorer.**
4. Click on the **GraphiQL tool** link.

<img src="/files/BKGw5WlrZc7jBbrtJGRI" alt="" data-size="original">

The explorer will be opened in a separate browser tab.

<img src="/files/AsE38UvRLq2uTv95pjAG" alt="" data-size="original">

5. Search for a user by `email`and put in your query `standardAttributes` and `customAttributes`. For example:

```json5
query {
  users(
    searchKeyword: "bobur@oursky.com"
  ) {
    edges {
      node {
	standardAttributes
        customAttributes
      }
    }
  }
}
```

</details>

### 4. Access user profiles from the OIDC UserInfo endpoint

The OpenID Connect (OIDC) [UseInfo](/integration/user-profiles/user-profile#userinfo-endpoint) endpoint is a protected resource that provides information about a user when a service provider presents an access token that has been issued by your **Authgear Token endpoint**. The scopes in the access token specify the user attributes that are returned in the response of the user info endpoint. It is important to note that the `openid` scope must be one of the access token claims.

#### UserInfo Endpoint

The UserInfo endpoint returns the Claims about the authenticated end-user, including the standard profile and custom attributes.

The `userInfo` object is returned from calling **fetch user info** function which contains a unique identifier of the user.

The following are some attributes that are usually in the userInfo object. Other attributes like email, phoneNumber and custom attributes will be in the userInfo object if those attributes were set for the current user.

| Key         | Type      | Description                                                                                                                                                                                       |
| ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| isAnonymous | *boolean* | Indicate if the user is anonymous, i.e. no [identity](/concepts/user-identity-and-authenticator#identity) or [authenticator](/concepts/user-identity-and-authenticator#authenticator) is provided |
| isVerified  | *boolean* | Indicate if the user completed the verification requirement                                                                                                                                       |
| sub         | *string*  | Unique identifier of the user in your Authgear project                                                                                                                                            |

To access a user's profile using the UserInfo endpoint of OpenID Connect, you need to follow these steps:

<details>

<summary>Expand this to see the guide</summary>

To access a user profile endpoint, you need to have a JWT access token in the header of a request to `/oauth2/userinfo`

We are going to use **cURL** commands in our API calls or you can also use [Postman](https://www.postman.com/) or similar tools.

**Prerequisites**

* Make sure that you have a registered app type of **OIDC Client Application** in Authgear Portal.
* You have successfully configured your application to use Authgear as an OIDC provider. See your [getting started guide](/get-started/start-building) for detailed steps.

**Step 1: Obtain an access token**

Your client application needs a valid access token for a user in order to make request to the UserInfo endpoint.

To get an access token, you need to make a request to the **OpenID App's Token endpoint** to exchange the **authorization code** that was retrieved after authorization for an access token.

* The token endpoint URL is usually something like `https://<YOUR_AUTHGEAR_ENDPOINT>/oauth2/token`.
* Include parameters such as `grant_type=authorization_code`, `code=AUTHORIZATION_CODE`, `client_id=YOUR_CLIENT_ID`, `client_secret=YOUR_CLIENT_SECRET`, and `redirect_uri=YOUR_REDIRECT_URI`.
* Make a POST request to the token endpoint to obtain the access token.

The following is an example of a request to the token endpoint sent for a terminal using cURL:

```bash
curl --request POST \
  --url 'https://<YOUR_AUTHGEAR_ENDPOINT>/oauth2/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=authorization_code \
  --data code={YOUR_AUTHORIZATION_CODE} \
  --data redirect_uri={YOUR_REDIRECT_URI} \
  --data 'client_id={YOUR_CLIENT_ID}' \
  --data client_secret={YOUR_CLIENT_SECRET} \
  --data scope=openid
```

**Step 2: Make a request to the Userinfo endpoint**

Once you have obtained a **JWT access token**, you can use it to make a request to the Userinfo endpoint. The request to the Userinfo endpoint should include the access token in the `Authorization` header using the `Bearer` scheme.

```bash
curl -X GET \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  https://<YOUR_AUTHGEAR_ENDPOINT>/userinfo

```

If you are using Postman, you can enable the Authorization type of OAuth2.0, provide the necessary information for getting the authorization code, obtaining an access token and use that token to request the Userinfo endpoint:

![](/files/cxbC1tt4UTYj5yLLfZQX)

</details>

See a detailed explanation of the structure and fields included in the response of the UserInfo endpoint [here](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo).

### 5. Embed User Profiles into the JWT Access Token

Authgear WebHooks makes it possible to embed the standard attributes and custom attributes for a user's profile into the OIDC JSON Web Token (JWT). Hence, you access both profile attributes in the JWT returned to your OIDC client without making another call to the UserInfo endpoint.

See our post about how to [Add custom fields to a JWT Access Token](/integration/add-custom-fields-to-a-jwt-access-token) to learn more.


# Update User Profiles

Guide on the different ways you can modify the profiles of the users of your application.

Authgear offers multiple ways for you (the admin) and your users to update user profiles. As an admin, you can modify profile attributes for any of the users of your Authgear project. End users on the other hand can modify their own profile using the Profile settings UI provided by Authgear or using a custom logic you implement in your code.

In this guide, we'll walk through all the options for updating user profiles.

### Pre-requisites

To follow along, you need to have the following:

* An Authgear Account. You can create one for free [here](https://accounts.portal.authgear.com/signup).
* At least one registered user on your Authgear Project.

#### What you'll learn

At the end of this post you'll be able to do the following:

* Update a user profile as an admin from the Authgear Portal or using Admin API.
* Update a user profile as an end user via the profile settings page.

## 1.0 Update User Profiles as an Admin

You can update the profiles for your users as an admin using either the Authgear Portal or the Admin API. Below are guides on how to use both options.

### 1.1 How to Update User Profile from the Authgear Portal

This option provides an easier way to manage your users within the Authgear Portal.

To update user profiles using this method, first, log in to the [Authgear Portal](https://portal.authgear.com/), then select your project.

Next, Navigate to **User Management** section to view all the users currently registered under your project.

<figure><img src="/files/gTmgmnFu027wrDgojJih" alt=""><figcaption></figcaption></figure>

Click on the ID (name, email, or phone number) for the user you wish to update their profile to open the edit user details page.

<figure><img src="/files/letRtqfS2WxsJ5C2UT3G" alt=""><figcaption></figcaption></figure>

From the edit user details page, you can edit the selected user's standard attributes such as Given Name, Family Name, Birthday, etc. If you scroll down on the edit user details page, you'll find more fields like the custom attributes that you can also update.

Once you're done, click the **Save** button to keep your update.

### 1.2 Using Admin API to Update User Profiles

The second option available for updating user profiles is the Admin API. The Admin API is a GraphQL API that you can use to manage your users. You can access Admin API from the [API Explorer](https://docs.authgear.com/reference/apis/admin-api#api-explorer) or by making requests to the API from your code or a client like Postman.

To update a user profile via the Admin API, you need to create a mutation like the following:

First, create a variable with all the existing profile attributes for the user, then include new fields or update the value for the fields you wish to update. Note that omitting an existing attribute in your variable will cause Authgear to delete that attribute.

**Variable**

```graphql
{
  "standardAttributes": {
    "family_name": "John",
    "given_name": "Doe",
    "gender": "male"
  }
}
```

**Mutation**

```graphql
mutation ($standardAttributes: UserStandardAttributes) {
  updateUser(input: {userID: "<ENCODED USER ID>", standardAttributes: $standardAttributes}) {
    user {
      id
      standardAttributes
    }
  }
}
```

For a detailed guide on making a GraphQL query to update user profiles, see our dedicated post on updating[ user's standard attributes using Admin API](https://docs.authgear.com/reference/apis/admin-api/api-examples/update-users-standard-attributes).

Also, see the [Admin API example for updating custom attributes](https://docs.authgear.com/reference/apis/admin-api/api-queries-and-mutations#id-2.20.-updateuser) to learn how to update custom attributes of a user profile.

## 2 How End Users Can Update Their Own Profile

Authgear provides a user settings page in addition to the default Auth UI for user registration and login.

Your users can update their profile from this settings page by following the steps below:

First, within your application, provide a link to the user settings page for your Authgear project. The URL should look like this: `https://<YOUR_AUTHGEAR_ENDPOINT>/settings`. Note that users can not access this link directly without logging in first.

After a user logs in and visits the settings page, they can view their profile photo and other details.

<figure><img src="/files/G8lMsomZE1ecZSKqdnWv" alt=""><figcaption></figcaption></figure>

Users can click on the **Edit Profile** button to update profile details such as name, gender, photo, and more.

<figure><img src="/files/mSob761Ul2umHubdZiaM" alt=""><figcaption><p>user settings edit profile ui</p></figcaption></figure>

To edit a profile attribute, the user should click on that attribute.

<figure><img src="/files/7BGozL1AkGjZxntENBx7" alt=""><figcaption><p>user settings editing attribute</p></figcaption></figure>


# Profile Custom Attributes

Learn how to add custom attributes to user profiles and manage existing custom attributes

In Authgear, a user's profile includes two types of attributes.

The first is the standard attributes which are basically common profile fields such as Primary Email, Primary Phone, Username, Name, Birthday, etc that are present by default.

The second type is the custom attributes an Authgear user can set for their project. This allows you to add custom fields to the profiles of your users. For example, you can add a Post Code custom attribute to your Authgear so users can provide this data for their profile.

All custom attributes are returned in the [UserInfo](/reference/apis/oauth-2.0-and-openid-connect-oidc/userinfo) response under the `custom_attributes` field.

In this post, you'll learn how to add new custom attributes to your Authgear project. You'll also learn how to manage existing custom attributes.

### 1. How to Add a New Custom Attribute

You can add new custom attributes to your project from the Authgear Portal. To do this, first, navigate to **User Profile** > **Custom Attributes**. Then, click on the **Add New Attribute** button to open the **Add Custom Attribute** page.

<figure><img src="/files/YsYNwlc0wALKHDCq39by" alt=""><figcaption></figcaption></figure>

Enter the **Attribute Name**, a valid attribute name should consist of lowercase letters (a-z), digits (0-9), and underscore (*) only, must start with lowercase letters (a-z), and NOT end with an underscore (\_*).

Select the appropriate **Attribute Type** based on the type of data you expect users to enter in the custom attribute. The option you select will affect the type of input field (text field, dropdown, etc) users will see in their profile settings UI.

<figure><img src="/files/WLj08qo3Kj4UwtRBWnai" alt=""><figcaption></figcaption></figure>

Once you're done, click the **Save** button to add the new custom attribute.

### 2. Set Who Can Access a Custom Attribute

Usually, the next key action to take after adding a new custom attribute will be to set the access right. An attribute's Access Right is a configuration that determines who can see (Read-only) or add or modify (Editable) the value of an attribute's field.

You can set the **Access Right** for an attribute under the following groups:

* **Portal Admin**: Access control for the user profile attributes in Admin Portal > User Management.
* **Token Bearer**: Access control for the user profile attributes in the UserInfo endpoint.
* **End-user**: Access control for the user profile attributes in the End-user settings page.

To set the access right for an attribute, navigate to User Profile > Custom Attributes in the Authgear Portal. On the Custom Attributes list, click the Access Right dropdown for the group and custom attribute you wish to update. Click **Save** when you're done to keep the changes.

<figure><img src="/files/5FhCcbNl2CpOLJELw75b" alt=""><figcaption></figcaption></figure>

**Note:** Selecting the **Hidden** access right, for any group will hide the field for the attribute from that group. For example, selecting Hidden for the `post_code` attribute for the Token Bearer group means the `post_code` field will be hidden (not included) in the UserInfo endpoint response.

### 3. Modify Custom Attribute Name

From the Custom Attributes list, click on the pencil icon to open the **Edit Custom Attribute** page.

**Note:** You can't modify the **Attribute Type** for an existing attribute. The alternative will be to create a new custom attribute with the new **Type** and migrate data from the old attribute to it. Also, rename and change the access right for the old attribute to make it obsolete.

<figure><img src="/files/HYLLspsIfAXcshKDtvH1" alt=""><figcaption></figcaption></figure>

### 4. Reorder Custom Attribute Position

You can set the order of how custom attributes appear in the UI.

To do that, in the Custom Attributes list, click and drag the stacked icon next to an attribute up or down to move the position.

<figure><img src="/files/733HhVEQW4Iy1fEJ0nB7" alt=""><figcaption></figcaption></figure>

### 5. Delete Custom Attribute

Delete is not supported. Instead, you should change the name and access right to make the attribute obsolete.


# Update user profile on sign-up using Hooks

Learn how to update a User profile's custom attributes on sign-up using Hooks

Using [Hooks](/customization/events-hooks/denohooks) you can put **extra information** into the user profile's [custom attributes](/integration/user-profiles/profile-custom-attributes) programmatically. This is useful for [Profile Enrichment](https://www.authgear.com/post/how-profile-enrichment-can-boost-your-product) where making your current customer data better by adding more details from outside sources.

Here are easy steps to achieve this:

**Step 1.** Make sure that you have an Authgear account. If you don't have one, you can [create it for free](https://accounts.portal.authgear.com/signup) on the Authgear website. Start by logging into your [Authgear dashboard](https://portal.authgear.com/).

**Step 2.** Go to **User Profile** → **Custom Attributes** page.

**Step 3.** Add 3 new attributes there, namely *city*, *name*, and *timezone*:

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64c3b36a761d2d11e1c5ea89_Untitled%20(5).png" alt=""><figcaption></figcaption></figure>

**Step 4**. Navigate to your Authgear Dashboard's **Advanced**->**Hooks** section.

**Step 5. Add** a new **Blocking Event**.

**Step 6.** Choose the Block Hook **Type** as the *TypeScript* and set the Event option to *User* *pre-create*. You will write a new Typescript function from scratch.

**Step 7.** Click on **Edit Script** under the **Config** option.

**Step 8.** Write a function logic for how you integrate any external API to populate custom attributes into the editor. For example.

```typescript
		
export default async function(e: EventUserPreCreate): Promise
			 {
  // API Key for IP Geolocation
  const apiKey = 'MY_API_KEY';
  // Any random IP address
	const ipAddress = '8.8.8.8' 

  // Fetch data from the IP Geolocation API
  const response = await fetch(`https://api.ipgeolocation.io/ipgeo?apiKey=${apiKey}&ip=${ipAddress}`);
  const data = await response.json();

return {
    is_allowed: true,
    mutations:{
      user: {
          custom_attributes: {
            "city": data.city, 
            "country": data.country_name,
            "timezone": data.time_zone.name
        }
      }
    },
  };
}
```

**Step 9.** Now if you navigate to **User Management** and **Add** a new user.

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64c3b456e2d961a288183a98_Untitled%20(6).png" alt=""><figcaption></figcaption></figure>

**Step 10.** After the user is created, you should able to see custom attributes values have been updated for the user:

<figure><img src="https://uploads-ssl.webflow.com/60658b47b03f0c77e8c14884/64c3b46e2482cbed9e3a37af_Untitled%20(7).png" alt=""><figcaption></figcaption></figure>

#### Next steps

Once you learned how to update user profiles, now you can discover different ways of [accessing user profiles](/integration/user-profiles/access-user-profiles) in Authgear.


# Mobile Apps

Learn how to integrate Authgear with mobile apps using SDKs and advanced configurations

This section covers mobile-specific integration patterns and configurations for Authgear.

{% content-ref url="/pages/-Mj8Y1oAhH7-FL0y4tol" %}
[Force authentication on app launch](/integration/mobile-apps/force-authentication-on-app-launch)
{% endcontent-ref %}

{% content-ref url="/pages/LluSBuPDQab4Jrb3FK4I" %}
[Customize the Login Pop-up / Disable the login alert box](/integration/mobile-apps/using-webview-to-open-the-authgear-ui)
{% endcontent-ref %}

{% content-ref url="/pages/-MbFM4jjzD5DKZzSMBE8" %}
[Client SDK to make authorized API calls to backend](/get-started/backend-api/using-sdk-to-call-your-application-server)
{% endcontent-ref %}


# Force authentication on app launch

If your mobile app has security requirements similar to that of mobile banking applications, you may want the end-users to authenticate themselves every time they use your app.

By default, the SDK stores the refresh token in a persistent storage specific to your app. The end-user signs in once and their session lasts for a long period, even if they quit the app.

You can alter this behavior by switching to a transient storage by setting the `tokenStorage` option to `TransientTokenStorage()` when configuring the SDK. The refresh token will be removed after the app is cleared, therefore authentication will be required on every app launch.

{% tabs %}
{% tab title="React Native" %}

```typescript
import authgear, { TransientTokenStorage } from "@authgear/react-native";

authgear.configure({
    clientID: CLIENT_ID,
    endpoint: ENDPOINT,
    tokenStorage: new TransientTokenStorage(),
});
```

{% endtab %}

{% tab title="Flutter" %}

```dart
final authgear = Authgear(
    clientID: CLIENT_ID,
    endpoint: ENDPOINT,
    tokenStorage: TransientTokenStorage(),
);
```

{% endtab %}

{% tab title="Xamarin" %}

```csharp
var authgearOptions = new AuthgearOptions
{
    ClientId = CLIENT_ID,
    AuthgearEndpoint = ENDPOINT,
    TokenStorage: new TransientTokenStorage(),
};
#if __ANDROID__
var authgear = new AuthgearSdk(GetActivity().ApplicationContext, authgearOptions);
#else
#if __IOS__
var authgear = new AuthgearSdk(UIKit.UIApplication.SharedApplication, authgearOptions);
#endif
#endif
```

{% endtab %}

{% tab title="iOS" %}

```swift
Authgear(
    clientId: CLIENT_ID,
    endpoint: ENDPOINT,
    tokenStorage: TransientTokenStorage()
)
```

{% endtab %}

{% tab title="Android" %}

```java
new Authgear(
    application,
    CLIENT_ID,
    ENDPOINT,
    new TransientTokenStorage() //tokenStorage
);
```

{% endtab %}
{% endtabs %}


# Customize the Login Pop-up / Disable the login alert box

Learn how to switch between ASWebAuthenticationSession/Custom Tabs to WebKitWebView using Authgear Mobile SDKs.

The default of the Authgear Mobile SDKs is to launch AuthUI in [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) in iOS and [Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs) in Android. ASWebAuthentication is an API provided by Apple for login purpose. It will store the session cookie and share with Safari, which makes Single Sign-on (SSO) between mobile apps and web apps possible.

However, it requires user consent, and will display a login alert box:

<figure><img src="/files/2EyCVIy2rVKf99vA3mIE" alt="" width="188"><figcaption></figcaption></figure>

There are multiple ways you can avoid the login alert box:

## 1. Use ephemeral sessions

If you do not need SSO between mobile and web apps, you can disable it by setting `isSSOEnabled = false`.

{% tabs %}
{% tab title="iOS (Native)" %}

```swift
Authgear(
            clientId: "<CLIENT_ID>",
            endpoint: "<AUTHGEAR_ENDPOINT>",
            isSSOEnabled: false,
        )
```

{% endtab %}

{% tab title="React Native" %}

```javascript
authgear
      .configure({
        clientID: "<CLIENT_ID>",
        endpoint: "<AUTHGEAR_ENDPOINT>",
        isSSOEnabled: false,
});
```

{% endtab %}
{% endtabs %}

It will not share the session cookies between your app and Safari. And the login alert box will not show to prompt user consent.

## 2. Use WebKitUIImplementation

The mobile SDKs include `WebKitWebViewUIImplementation`, a `UIImplementation` which makes it possible to customize more UI.

Depending on the platforms, there are various alternatives:

* iOS: WKWebViewUIImplementation
* Other platforms / Android: WebKitWebViewUIImplementation

Setting the `uiImplementation` attribute in the `configure()` method of the Authgear SDK to `WebKitWebViewUIImplementation()` will open AuthUI using [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview) on iOS and [android.webkit.WebView](https://developer.android.com/reference/android/webkit/WebView) on Android.

Omitting the `uiImplementation` attribute in the `configure()` method will fall back to the default behavior (launching AuthUI in ASWebAuthenticationSession/Custom Tabs, which is `DeviceBrowserUIImplementation`).

### Example Code

The following examples show how to set the `uiImplementation` attribute.

{% tabs %}
{% tab title="Ionic" %}

```typescript
authgearCapacitor.configure({
    clientID: "<CLIENT_ID>",
    endpoint: "<AUTHGEAR_ENDPOINT>",
    isSSOEnabled: true,
    uiImplementation: new WebKitWebViewUIImplementation(),
    tokenStorage: new TransientTokenStorage()
});
```

{% endtab %}

{% tab title="React Native" %}

```typescript
authgear
      .configure({
        clientID: "<CLIENT_ID>",
        endpoint: "<AUTHGEAR_ENDPOINT>",
        uiImplementation: new WebKitWebViewUIImplementation()
});
```

{% endtab %}

{% tab title="Android (Native)" %}

```java
private Authgear authgear = new Authgear(
                                getApplication(),
                                "<CLIENT_ID>",
                                "<AUTHGEAR_ENDPOINT>",
                                tokenStorage,
                                new WebKitWebViewUIImplementation(),
                                isSsoEnabled,
                                null,
                                app2appOptions
                                );
```

{% endtab %}

{% tab title="iOS (Native)" %}

```swift
Authgear(
            clientId: "<CLIENT_ID>",
            endpoint: "<AUTHGEAR_ENDPOINT>",
            tokenStorage: TransientTokenStorage(),
            uiImplementation: WKWebViewUIImplementation(),
            isSSOEnabled: true,
        )
```

{% endtab %}
{% endtabs %}

Setting `uiImplementation` to `WebKitWebViewUIImplementation` in the above example will change the behavior of your application from using **ASWebAuthenticationSession** on iOS and **Custom Tabs** on Android to using `WKWebView` and `android.webkit.WebView` respectively.

To set `uiImplementation` to `WKWebView` in the native iOS SDK, use `WKWebViewUIImplementation` .

### Customizing the WebKitWebView UI

`WebKitWebViewUIImplementation` allows you to customize some parts of the UI. You can do this by passing your customization options as parameters in WebKitWebViewUIImplementation(). You can customize the following parts of the UI:

**Android**

* `actionBarBackgroundColor`: Use this option to customize the color of the action bar on the WebView Activity screen. The value should be of type integer according to this encoding: <https://developer.android.com/reference/android/graphics/Color#encoding>
* `actionBarButtonTintColor`: This option can be used to set the color of the icons and texts on the action bar. The value should also be of type integer and use the encoding here: <https://developer.android.com/reference/android/graphics/Color#encoding>.

**iOS**

* `navigationBarBackgroundColor`: This option can be used to customize the color of the navigation bar on iOS. The value should be of type [UIColor](https://developer.apple.com/documentation/uikit/uicolor/) or an integer (React Native or Ionic SDKs) using the following encoding: <https://developer.android.com/reference/android/graphics/Color#encoding>.
* `navigationBarButtonTintColor`: This option sets the color of icons and texts on the navigation bar. The value should also be UIColor or an integer (React Native or Ionic SDKs) using the same encoding as `navigationBarBackgroundColor`.
* `modalPresentationStyle`: Sets the type of modal to be shown. The value can be any of the following: "automatic", "fullScreen", "pageSheet".

The following examples show how to set custom background color, tint color, and modal presentation style.

{% tabs %}
{% tab title="Ionic" %}

```typescript
authgearCapacitor.configure({
  clientID: "<CLIENT_ID>",
  endpoint: "<AUTHGEAR_ENDPOINT>",
  uiImplementation: new WebKitWebViewUIImplementation({
    ios: {
      modalPresentationStyle: "fullScreen"
    },
    android: {
      actionBarBackgroundColor: 0xffffff00,
      actionBarButtonTintColor: 0xff000000
    }
  }),
  tokenStorage: new TransientTokenStorage()
});
```

{% endtab %}

{% tab title="React Native" %}

```typescript
authgear
      .configure({
        clientID: "<CLIENT_ID>",
        endpoint: "<AUTHGEAR_ENDPOINT>",
        uiImplementation: new WebKitWebViewUIImplementation({
          ios: {
            modalPresentationStyle: "fullScreen"
          },
          android: {
            actionBarBackgroundColor: 0xffffff00,
            actionBarButtonTintColor: 0xff000000
          }
        })
      })
```

{% endtab %}

{% tab title="Android (Native)" %}

```java
private int actionBarBackgroundColor = 0xffffff00;
private int actionBarButtonTintColor = 0xff000000;
private Authgear authgear = new Authgear(
                                getApplication(),
                                "<CLIENT_ID>",
                                "<AUTHGEAR_ENDPOINT>",
                                tokenStorage,
                                new WebKitWebViewUIImplementation(actionBarBackgroundColor, actionBarButtonTintColor),
                                isSsoEnabled,
                                null,
                                app2appOptions
                                );
```

{% endtab %}

{% tab title="iOS (Native)" %}

```swift
Authgear(
            clientId: "<CLIENT_ID>",
            endpoint: "<AUTHGEAR_ENDPOINT>",
            tokenStorage: TransientTokenStorage(),
            uiImplementation: WKWebViewUIImplementation(
               modalPresentationStyle: UIModalPresentationStyle.fullScreen,
               navigationBarBackgroundColor: UIColor.yellow,
               navigationBarButtonTintColor: UIColor.black
            ),
            isSSOEnabled: true
        )
```

{% endtab %}
{% endtabs %}

## 3. Implement Custom UIImplementation

You can implement your own custom `UIImplementation` when the `WebKitWebViewUIImplementation` does not meet the requirements of your use case.

The WebKitWebViewUIImplementation class itself is basically a class that implements `UIImplementation` and overrides the `openAuthorizationURL()`method. Hence your custom implementation may look like this:

```typescript
class MyUIImplementation implements UIImplementation {
 async openAuthorizationURL(options: OpenAuthorizationURLOptions):
Promise<string> {
 // Call your own plugin code to implement this.
 }
}
```

Then, you can use your custom implementation like this:

```typescript
authgear.configure({
 clientID: "my_client_id",
 endpoint: "my_endpoint",
 isSSOEnabled: true,
 uiImplementation: new MyUIImplementation(),
})
```

To get a deeper understanding of how to implement your UIImplementation, see the code for the [WebKitWebViewUIImplementation](https://github.com/authgear/authgear-sdk-android/blob/main/sdk/src/main/java/com/oursky/authgear/WebKitWebViewUIImplementation.kt) implementation.

## Unsupported Features

When you drop the default DeviceBrowserUIImplementation to use your own custom UI implementation that uses WebView, it is important to note that you'll be losing the following features, due to iOS or other security limitations.

1. **Login with Google:** Google prohibits the use of `WebKitWebView` with the Google SSO. Learn more here: <https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html>. As a result `WebKitWebViewUIImplementation` and Google SSO cannot be used together.
2. **Passkey:** Passkey is not supported in WebKitWebView.


# Integration with other Software


# Authgear and Supabase

Guide on using Authgear together with Supabase database to deliver a secure and scalable application

User authentication is a foundational part of any modern web application but getting it right is notoriously difficult. That’s where Authgear comes in. Authgear handles user registration, login flows, session management, and advanced security features like biometric login, 2FA, and social sign-ins out of the box.

Pairing Authgear with Supabase's Postgres database and powerful Row-Level Security (RLS) gives you a secure, scalable foundation for building user-specific applications.

In this guide, you’ll learn how to integrate Authgear and Supabase using React.js to build a basic CRUD application.

<figure><img src="/files/M9v90XgxOPSybci2XgIh" alt="Simple instrument tracking app where users can manage their personal collection after logging in with Authgear"><figcaption><p>Simple instrument tracking app where users can manage their personal collection after logging in with Authgear</p></figcaption></figure>

We will walkthrough how to **authenticate users with Authgear**, exchange tokens via a **Supabase Edge Function**, apply **RLS policies** based on Authgear-issued JWTs, and connect it all in a modern React frontend using Vite.

```
┌─────────────┐          ┌──────────────┐          ┌──────────────┐
│   Browser   │          │   Authgear   │          │   Supabase   │
│  (React App)│◄────────►│  (Auth IdP)  │          │  (Database)  │
└──────┬──────┘          └──────────────┘          └──────┬───────┘
       │                                                  │
       │ 1. Login with Authgear                           │
       │ 2. Get Authgear JWT (access token)               │
       │ 3. Call Supabase with Authgear JWT (request data)│
       └──────────────────────────────────────────────────►
       │                                                   │
       │ 4. Exchange JWT (Edge Function)                   │
       │ 5. Return Supabase JWT                            │
       │◄──────────────────────────────────────────────────
       │                                                   │
       │ 6. Access data with Supabase JWT (enforce RLS)    │
       └──────────────────────────────────────────────────►

```

## **Full example code**

The full example app source code is available on GitHub and can be used as a starting point for your own project: <https://github.com/authgear/authgear-example-supabase>

## What you need

* **Authgear account & project** – [Sign up for Authgear](https://portal.authgear.com/).
* **Supabase account & project** – [Sign up on supabase.com](https://supabase.com/)

## Step 1: Set up an Authgear project

First, configure Authgear to handle authentication for your app.

1. **Create an Authgear Application:** Log in to the Authgear Portal and create a new application. Choose **Single Page Application** as the application type.
2. **Configure OAuth Redirects:** In your Authgear app settings, set the redirect URIs:
   * **Redirect URI**: `http://localhost:5173/auth-redirect` – this is where Authgear will redirect the browser after a successful login (our React app will handle this route).
   * **Post-Logout Redirect URI:** `http://localhost:5173/` – where to navigate after users log out.
3. **Note Your Authgear Credentials:** After creation, note down:
   * The **Authgear Endpoint** for your app (it will look like `https://<your-project>.authgear.cloud`).
   * The **Client ID** of your Authgear application.

We will need these values when initializing Authgear in our React app and in the Supabase function.

## Step 2: Set up a Supabase project

Next, prepare your Supabase backend.

1. **Create a Supabase Project:** Log in to Supabase Dashboard, create a new project (choose an organization, project name, database password, and region).
2. **Retrieve Project Keys:** In your Supabase project, go to **Project Settings → API → API keys**. Copy the **Publishable Key** and the **URL** of your project (e.g., `https://xyzcompany.supabase.co`).
3. **Get the JWT Secret:** Still in **Project settings**, navigate to **JWT Keys** and find the **Legacy JWT Secret** for your project. This is a secret key that Supabase uses to sign and verify JWTs for RLS. Copy the JWT secret value – we will use it in our Edge Function config.\\

   <figure><img src="/files/S4LPydJaa5RET0tqq1Fp" alt=""><figcaption><p>Copy the Legacy JWT Secret from the JWT Keys page</p></figcaption></figure>
4. *(Optional)* **Install Supabase CLI:** If you plan to use the CLI to deploy the Edge Function, install it by following [Supabase’s instructions](https://supabase.com/docs/guides/local-development/cli/getting-started). Alternatively, you can deploy the function via the Dashboard UI.

## Step 3: Create an Edge Function to exchange JWTs

Now we set up the critical piece: a Supabase Edge Function that will accept Authgear’s JWT and return a new JWT signed with Supabase’s secret.

1. Navigate to "Edge Functions" -> "Secrets" and add the two secrets:
   1. `AUTHGEAR_ENDPOINT` = your Authgear app endpoint (e.g. `https://myapp.authgear.cloud`).
   2. `SB_JWT_SECRET` = your Supabase JWT secret (from Step 2 above).
2. Deploy the Function
   1. Navigate to "Edge Functions" -> "Functions" in Supabase web UI. Copy-paste the following code and deploy it there. Name the function `exchange-jwt`.
   2. Alternatively, you can use Supabase CLI to build and deploy the Edge Function from your local machine to your Supabase project.

```typescript
// supabase/functions/exchange-jwt/index.ts

import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import jwt from "npm:jsonwebtoken";
import jwks from "npm:jwks-rsa";
import * as jose from "https://deno.land/x/jose@v4.14.4/index.ts";

// CORS headers for cross-origin requests
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "Authorization, Content-Type",
  "Access-Control-Allow-Methods": "GET",
  "Access-Control-Max-Age": "3600"
};

// Read environment variables
const authgearEndpoint = Deno.env.get("AUTHGEAR_ENDPOINT");
const supabaseJwtSecret = Deno.env.get("SB_JWT_SECRET");
if (!authgearEndpoint || !supabaseJwtSecret) throw new Error("Missing env vars");

// Get JWKS URI from Authgear's OpenID configuration
async function getJwksUri(): Promise<string> {
  const configUrl = `${authgearEndpoint}/.well-known/openid-configuration`;
  const response = await fetch(configUrl);
  const config = await response.json();
  return config.jwks_uri;
}

// Extract Bearer token from Authorization header
function extractToken(req: Request): string | null {
  const authHeader = req.headers.get("Authorization");
  if (!authHeader) return null;
  
  const parts = authHeader.split(" ");
  if (parts.length !== 2 || parts[0] !== "Bearer") return null;
  
  return parts[1];
}

// Verify JWT token with Authgear's public key
async function verifyToken(token: string): Promise<any> {
  // Decode token to get key ID
  const decoded = jwt.decode(token, { complete: true }) as any;
  if (!decoded?.header?.kid) {
    throw new Error("Invalid token: missing key ID");
  }

  // Get JWKS URI and create JWKS client
  const jwksUri = await getJwksUri();
  const jwksClient = jwks({ jwksUri });

  // Get the signing key
  const key = await jwksClient.getSigningKey(decoded.header.kid);
  const signingKey = key.getPublicKey();

  // Verify the token
  const options = {
    algorithms: ["RS256"],
    issuer: authgearEndpoint,
  };

  return jwt.verify(token, signingKey, options);
}

// Sign a new JWT with Supabase secret
async function signSupabaseJwt(payload: any): Promise<string> {
  payload.role = "authenticated"; // Required by Supabase

  // Add or modify any other claims you need for RLS policies
  // payload.some_claim = "some claim";

  // Sign with Supabase JWT secret
  const supabaseSecret = new TextEncoder().encode(supabaseJwtSecret);

  const supabaseJwt = await new jose.SignJWT(payload)
    .setProtectedHeader({ alg: "HS256", typ: "JWT" })
    .setIssuer("supabase")
    .setIssuedAt(payload.iat)
    .setExpirationTime(payload.exp || "")
    .sign(supabaseSecret);

  return supabaseJwt;
}

// Main function handler
Deno.serve(async (req: Request) => {
  // Handle CORS preflight requests
  if (req.method === "OPTIONS") {
    return new Response("ok", { headers: corsHeaders });
  }

  try {
    // Extract token from request
    const token = extractToken(req);
    if (!token) {
      return new Response(
        JSON.stringify({ error: "Missing or invalid Authorization header" }),
        { 
          headers: { ...corsHeaders, "Content-Type": "application/json" },
          status: 401 
        }
      );
    }
    // Verify the token
    const verified = await verifyToken(token);
    // Sign a new JWT with Supabase secret
    const supabaseJwt = await signSupabaseJwt(verified);
    return new Response(
      JSON.stringify({ supabaseJwt }),
      {
        headers: { ...corsHeaders, "Content-Type": "application/json" },
        status: 200,
      }
    );
  } catch (error) {
    console.error("Token verification failed:", error.message);
    return new Response(
      JSON.stringify({ error: "Token verification failed" }),
      {
        headers: { ...corsHeaders, "Content-Type": "application/json" },
        status: 401,
      }
    );
  }
});

```

Once deployed, your function is accessible at:

```
${SUPABASE_URL}/functions/v1/exchange-jwt
```

## Step 4: Create the database table and RLS policies

Now let’s set up the database table and RLS rules in Supabase. We’ll create a table (`instruments`) to store items owned by users, and use RLS to ensure each user can only manipulate their own rows.

Open the SQL editor in your Supabase project and run the following SQL commands:

```sql
-- 1. Helper function to get the current Authgear user ID from the JWT
CREATE OR REPLACE FUNCTION current_user_id()
RETURNS TEXT AS $$
  SELECT auth.jwt() ->> 'sub';
$$ LANGUAGE SQL STABLE;
```

We created a SQL function `current_user_id()` that returns the JWT’s `sub` claim (subject) from the current request’s JWT. Supabase’s Postgres has an `auth.jwt()` function that exposes the JWT claims of the requester; `auth.jwt() ->> 'sub'` extracts the `sub` field as text.

```sql
-- 2. Instruments table: each row has a user_id to identify the owner
CREATE TABLE instruments (
  id BIGSERIAL PRIMARY KEY,
  created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
  name TEXT NOT NULL,
  user_id TEXT NOT NULL   -- store Authgear user’s ID (sub)
);
 
-- 3. Enable Row Level Security on the table
ALTER TABLE instruments ENABLE ROW LEVEL SECURITY;

```

* The `instruments` table has a `user_id` column which will store the Authgear user’s ID for each instrument.
* We enabled RLS on the table, which means by default no rows can be accessed unless allowed by a policy.

```sql
-- 4. RLS Policies:
-- Allow each authenticated user to SELECT rows where user_id = their own ID
CREATE POLICY "Users can read their own instruments"
ON instruments
FOR SELECT
TO authenticated
USING (user_id = current_user_id());
 
-- Allow INSERTs only if setting user_id to their own ID
CREATE POLICY "Users can insert their own instruments"
ON instruments
FOR INSERT
TO authenticated
WITH CHECK (user_id = current_user_id());
 
-- Allow UPDATE on rows they own
CREATE POLICY "Users can update their own instruments"
ON instruments
FOR UPDATE
TO authenticated
USING (user_id = current_user_id())
WITH CHECK (user_id = current_user_id());
 
-- Allow DELETE on rows they own
CREATE POLICY "Users can delete their own instruments"
ON instruments
FOR DELETE
TO authenticated
USING (user_id = current_user_id());
```

We then defined four policies, one for each CRUD operation (SELECT, INSERT, UPDATE, DELETE). Each policy is limited to the `authenticated` role and uses a condition requiring that the row’s `user_id` matches the user’s id from the JWT.

## Step 5. Build the React app with Supabase Client

Now for the frontend. We will create a React app using Vite, integrate Authgear’s SDK for authentication, and configure the Supabase client to use our token exchange flow automatically.

**Dependencies**

1. Scaffold a React App by `npm create vite@latest my-app -- --template react`
2. Follow the guide for build SPA in here: [React](/get-started/single-page-app/react) to setup a basic react app with Authgear authentication
3. Install the Supabase library by

   ```
   npm install @supabase/supabase-js
   ```
4. Configure the Environment Variables in `.env` and make the Supabase URL and Publishable key available. You `.env.local` should look like:

```
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=your-supabase-anon-key

VITE_AUTHGEAR_ENDPOINT=https://your-app.authgear.cloud
VITE_AUTHGEAR_CLIENT_ID=your-authgear-client-id
VITE_AUTHGEAR_REDIRECT_URL=http://localhost:5173/auth-redirect
VITE_AUTHGEAR_LOGOUT_REDIRECT_URL=http://localhost:5173/
```

**Configure the Supabase Client**

Now we can set up the Supabase JS client to use our Authgear token. Create a new file `my-app/src/lib/supabase.js` and add the following:

```javascript
// my-app/src/lib/supabase.js
import { createClient } from '@supabase/supabase-js';
import authgear from '@authgear/web';

const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;

// Optional: caches to avoid redundant token exchanges
let cachedSupabaseJwt = null;
let cachedAuthgearToken = null;

// Function to exchange Authgear token for Supabase token
async function exchangeToken(authgearToken) {
  // Return cached token if the Authgear token hasn’t changed
  if (cachedAuthgearToken === authgearToken && cachedSupabaseJwt) {
    return cachedSupabaseJwt;
  }
  // Call the Supabase Edge Function
  const res = await fetch(`${SUPABASE_URL}/functions/v1/exchange-jwt`, {
    headers: { Authorization: `Bearer ${authgearToken}` }
  });
  if (!res.ok) {
    throw new Error(`Token exchange failed with status ${res.status}`);
  }
  const { supabaseJwt } = await res.json();
  // Update cache and return the new JWT
  cachedAuthgearToken = authgearToken;
  cachedSupabaseJwt = supabaseJwt;
  return supabaseJwt;
}

// Create Supabase client with custom auth settings
export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  auth: {
    autoRefreshToken: false,   // Don't use Supabase’s token refresh
    persistSession: false,     // Don't store Supabase session (Authgear will handle session)
  },
  accessToken: async () => {
    // This function runs before every Supabase request that needs auth
    await authgear.refreshAccessTokenIfNeeded();
    const authgearToken = authgear.accessToken;
    if (!authgearToken) return null;
    // Exchange Authgear token for a Supabase token:contentReference[oaicite:12]{index=12}:contentReference[oaicite:13]{index=13}
    return exchangeToken(authgearToken);
  },
});

```

In this configuration:

* We disable `autoRefreshToken` and `persistSession` in Supabase’s client so that Authgear is the source of truth for the session.
* We provide an `accessToken` async function. The Supabase Client will call this function every time it needs a JWT for an authenticated request. In our case, that is every query to our `instruments` table.
* In `accessToken()`, we first ensure Authgear’s token is fresh by calling `authgear.refreshAccessTokenIfNeeded()`.
* We then get `authgear.accessToken` – this is the current JWT from Authgear’s SDK.
* If the token exists, we call our `exchangeToken()` helper, which calls the Edge Function’s endpoint with the Authgear token and receives a Supabase JWT in response.

**Build the CRUD functions**

After login, use the Supabase client to fetch and manipulate data. For example, to load the current user’s instruments, you can call:

{% code overflow="wrap" %}

```javascript
const { data, error } = await supabase.from('instruments').select('*');
```

{% endcode %}

To add a new instrument, call

```javascript
supabase.from('instruments').insert({
    name: "<new intruments>",
    user_id: userInfo.sub,
});
```

Similarly, to update or delete, similarly call `.update(...)` or `.delete(...)` in the Supabase Client with appropriate filters

## 6. Run and test the app

We’re ready to test the whole system. Start the development server:

```bash
npm run dev
```

Open `http://localhost:5173` in your browser and test the integration.

## That's it

You’ve successfully integrated Authgear for authentication with Supabase for secure, per-user data access using Row-Level Security.

With this setup, you can take full advantage of Authgear’s powerful authentication features like social logins, 2FA, and secure session management, while leveraging Supabase’s scalable database and built-in RLS to keep user data isolated and protected. This token exchange approach makes it easy to use Authgear (or any other JWT-based provider) with Supabase.


# Authgear and Firebase

Guide on how to use Authgear to secure Firebase services

## How to use a third-party authentication system to secure Firestore and other Firebase services.

Firebase is Google's flagship solution for building serverless web and mobile applications. Some of the services Firebase offers include Firestore database, Cloud Storage, Cloud Functions, and Authentication.

You can secure Firebase services like Firestore and Cloud Storage such that only authenticated users in your app have read and write access to certain parts or the entire database. This can be useful when your app allows users to generate content and you need to control who can add or view what.

In this guide, you'll learn how to use Authgear as a custom authentication system to secure Firestore database.

## Why use Authgear with Firebase?

Authgear is a cloud-based solution that is 100% focused on user authentication and IAM. While Firebase, on the other hand, offers other cloud services such as Cloud Storage and real-time database Firestore that enable developers to build full-stack apps without the need for implementing their own backend. Hence, you can use Authgear as an authentication provider for your app that uses Firebase to enjoy all the security and login features it offers.

## What is Firebase Custom Tokens?

Custom Tokens is a feature in Firebase that allows you to authenticate users using secure JWT (JSON Web Tokens) from a third-party authentication system.

Typically, to authenticate users via Custom Tokens, your authentication system (e.g., Authgear) will authenticate users using your preferred method (e.g., username and password), then return a valid JWT to your client application.

The client application will pass the JWT to Firebase to create a Firebase user instance using the user's `sub` attribute from Authgear.

The following flow diagram shows the complete sequence of using Firebase Custom Tokens and Authgear.

<figure><img src="/files/kJ0Fj5cUAigIDZmGCn3U" alt=""><figcaption><p>flow diagran for authgear-firebase integration</p></figcaption></figure>

## How to integrate Authgear with Firebase using Custom Tokens

In this section, we'll cover a step-by-step guide for using Authgear JWT with Firebase Custom Tokens.

{% hint style="info" %}
**Code Example**

The example app used for this guide is based on the Authgear React example repo (clone or download the code [here](https://github.com/authgear/authgear-example-react)). Once you get the code, you'll have to set up an Authgear client application with type "Single Page Application" in the Authgear portal. More details are in the README.md file for the repo.

Alternatively, you can view/download the complete code with the Firebase example from a separate GitHub repo [here](https://github.com/authgear/authgear-example-firebase).
{% endhint %}

As part of the guide, we'll build a demo Todo app. The app will have the following features:

* Users Login/Sign up
* Add to-do tasks
* Delete to-do tasks
* Only logged-in users can add tasks
* Data is stored on the cloud, and a user can only view/delete tasks they created.

### Prerequisites

In order to follow the steps in this guide, you should have the following:

* Authgear account (sign up for free [here](https://portal.authgear.com/))
* Firebase project
* Node.js installed on your computer

### Part 1: Configure Firebase

For this part, you'll login to your Firebase console and configure your project.

#### Step 1: Create Cloud Function

First, we'll create a Cloud Function that will verify the access token (JWT) from Authgear and use the `uid` associated with the JWT to create a Firebase Custom Token.

**Note:** this may require you to upgrade your Firebase project to at least the Blaze plan.

1. To create a Cloud Function, open <https://console.firebase.google.com/> and navigate to **Build** > **Functions** and enable the Cloud Function if it isn't already active for your project.
2. Next, create a `cloud_functions` folder in the root directory of the Authgear React example project you cloned earlier.
3. Change your working directory to the `cloud_functions` folder:

```sh
cd cloud_functions
```

4. Run the following command to install Firebase CLI:

```sh
npm install -g firebase-tools
```

5. After the CLI is installed, run the `firebase login` command and follow the prompt to login to your Firebase project in the CLI.
6. Run the following command to initialize Cloud Function in the cloud\_functions folder you created earlier:

```sh
firebase init
```

Follow the prompt to create a v2 Cloud Function. Now you should have a `function/index.js` file in your cloud\_functions folder. This is where you'll write the code for your cloud function.

#### Step 2: Enable token creator permission for cloud functions

Edit the IAM settings for the service account linked to your project. To do this, go to <https://console.cloud.google.com/iam-admin/iam> and add `Service Account Token Creator` to `{randomNumbers}-compute@developer.gserviceaccount.com`.

#### Step 3: Implement Cloud Function

In this step, we'll implement a cloud function that does the following:

* Accept HTTPS requests from your client application through an endpoint.
* Read the value of your Authgear JWT from the HTTPS request authorization header.
* Validate the JWT to ensure it is valid (not expired).
* If the JWT is valid, the function will read the `uid` attribute from it and use the value to call `firebaseAdmin.auth().createCustomToken(uid)`. This will generate a new Firebase Custom Token that your client application can use to login to Firebase.

To implement the above function, open `function/index.js` in your `cloud_functions` folder and replace the content with the following:

```javascript
const { onRequest } = require("firebase-functions/v2/https");
const jwt = require("jsonwebtoken");
const jwks = require("jwks-rsa");
const firebaseAdmin = require("firebase-admin");
const authgearEndpoint = ""; //place your authgear app endpoint here

// Get JWKs URI from Open ID Configuration documents
const getJwksUri = async (authgearEndpoint) => {
  const config_endpoint =
    authgearEndpoint + "/.well-known/openid-configuration";
  const response = await fetch(config_endpoint);
  const data = await response.json();
  return data.jwks_uri;
};

// Options for JWT verification
const options = {
  algorithms: ["RS256"],
  issuer: authgearEndpoint,
};

const checkReq = (req) => {
  const headerErr = ["must specify an Authorization header", null];
  const formatErr = ["format is 'Authorization: Bearer <token>'", null];
  if (!req) return "server error (request was invalid)";
  const { headers } = req;
  if (!headers) return headerErr; // missing header
  const { authorization } = headers;
  if (!authorization) return headerErr; // missing Authorization Header
  const parts = authorization.split(" ");
  if (parts.length != 2) return formatErr; // Authorization header format invalid
  const [scheme, credentials] = parts;
  if (!/^Bearer$/i.test(scheme)) return formatErr; // Authorization header is not Bearer
  return [false, credentials];
};

firebaseAdmin.initializeApp();

exports.getFirebaseToken = onRequest(async (req, res) => {
  res.set("Access-Control-Allow-Origin", "*");
  if (req.method === "OPTIONS") {
    res.set("Access-Control-Allow-Methods", "GET");
    res.set("Access-Control-Allow-Headers", "Authorization");
    res.set("Access-Control-Max-Age", "3600");
    return res.status(204).send("");
  }

  const [message, token] = checkReq(req);
  if (message) {
    return res.status(500).send({ message }); // return error message if checkReq failed
  } else {
    const decodedAccessToken = jwt.decode(token, { complete: true });
    // get signing key from JWKs with the "kid" in decoded access token
    jwksUri = await getJwksUri(authgearEndpoint);
    const client = jwks({
      rateLimit: true,
      strictSsl: true,
      jwksUri: jwksUri,
    });
    const signingKey = await client.getSigningKey(
      decodedAccessToken.header.kid
    );
    try {
      const decoded = jwt.verify(token, signingKey.publicKey, options); // verify jwt
      const uid = decoded.sub; // if the jwt is verified, use "sub" in JWT as uid
      const firebaseToken = await firebaseAdmin.auth().createCustomToken(uid); // create Firebase custom token with uid
      return res.json({ firebaseToken }); // return Firebase custom token
    } catch (err) {
      return res.status(500).send(err);
    }
  }
});

```

Put your Authgear endpoint in `const authgearEndpoint = "";`.

Run the following commands to install dependencies:

```sh
npm i jwks-rsa
npm i jsonwebtoken
```

Finally, run `firebase deploy` to deploy your new function.

On successful deploy, Firebase CLI will output the public endpoint for your function in the terminal. Copy the endpoint as you will use it later in your React app.

<figure><img src="/files/TiPhScnM7BL6G3zkwBKQ" alt=""><figcaption><p>firebase function URL</p></figcaption></figure>

### Part 2: Implement React App

In this part, we'll implement the client application that will use Authgear and Firebase.

As mentioned in the prerequisites for this post, you can get the starter code for the application from our React example app repo.

#### Step 4: Add Firebase to React App

1. Go to the Firebase Console and select your project. Next, click on **Web** as your target platform.
2. Enter the name of your app (e.g., My todo app), then click **Register app**.
3. In the Add Firebase SDK section, select **Use npm**, then run `npm install firebase` from the root directory of the Authgear React example repo (confirm that you're not running the command in the cloud\_functions folder).
4. Create a new `firebase.js` file in the root directory of your React project.
5. Copy the code that includes your Firebase config, and paste it in the `firebase.js` file.\\

<figure><img src="/files/D93k0mpR7bFW3C92SMId" alt=""><figcaption><p>Firebase app config</p></figcaption></figure>

1. Update `firebase.js` to expose the Firebase services you'll be using:

```javascript
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from "firebase/firestore";

// Your web app's Firebase configuration
const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: ""
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
```

#### Step 5: Create Todo Component

Here we'll implement a to-do feature in our React app. The Todo component will contain all the code for implementing this feature.

The component will do the following:

* Get the current user's access token from Authgear using the Authgear SDK.
* Make an HTTPS request to your Firebase function endpoint using the `fetch()` method of the Authgear SDK. The `fetch()` method includes the user's access token in the HTTPS request's authorization header.
* Use the custom token that is returned by the cloud function to sign in to Firebase.
* Add a new todo item to the Firestore database
* Display the todo items
* Delete a todo item

Create a `Todo.tsx` file in the src folder of your React project, then add the following code to the file:

```javascript
import React, { useEffect, useState, useContext } from "react";
import authgear from "@authgear/web";
import { UserContext } from "./context/UserProvider.js";

import { signInWithCustomToken, UserCredential } from "firebase/auth";
import { collection, getDocs, addDoc, deleteDoc, doc } from "firebase/firestore";
import { auth, db } from "../firebase.js";

interface Todo {
  id: string;
  text: string;
  userId: string;
}

const Todo: React.FC = () => {
  const { isLoggedIn } = useContext(UserContext);
  const [firebaseUser, setFirebaseUser] = useState<object>({});
  const [authError, setAuthError] = useState<boolean>(false);

  const [todos, setTodos] = useState<Todo[]>([]);
  const [newTodo, setNewTodo] = useState("");

  useEffect(() => {
    async function getFirebaseUser() {
      if (isLoggedIn) {
        const firebaseFunctionEndpoint = ""; // place your cloud function endpoint here.
        try {
          const getCustomToken = await authgear
            .fetch(firebaseFunctionEndpoint)
            .then((response) => response.json());
          if (getCustomToken.firebaseToken !== null) {
            const firebaseUserObj = await signInWithCustomToken(
              auth,
              getCustomToken.firebaseToken
            );
            setFirebaseUser(firebaseUserObj);
            setAuthError(false);
          }
        } catch (e) {
          console.log(e);
          setAuthError(true);
        }
      } else {
        setAuthError(true);
      }
    }

    getFirebaseUser()
      .then(() => {
        fetchTodos();
      })
      .catch((e) => {
        console.error(e);
      });
  }, [authgear]);

  async function fetchTodos() {
    try {
      const querySnapshot = await getDocs(collection(db, "todos"));
      const todoList = querySnapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      })) as Todo[];
      setTodos(todoList);
    } catch (error) {
      console.error("Error fetching todos:", error);
    }
  }

  async function addTodo(e: React.FormEvent) {
    e.preventDefault();
    if (!newTodo.trim() || authError) return;

    try {
      await addDoc(collection(db, "todos"), {
        text: newTodo,
        userId: (firebaseUser as UserCredential).user.uid,
        createdAt: new Date(),
      });
      setNewTodo("");
      fetchTodos();
    } catch (error) {
      console.error("Error adding todo:", error);
    }
  }

  async function deleteTodo(id: string) {
    try {
      await deleteDoc(doc(db, 'todos', id));
      fetchTodos();
    } catch (error) {
      console.error('Error deleting todo:', error);
    }
  }

  if (!authError) {
    return (
      <div className="app">
        <div className="todo-container">
          <form onSubmit={addTodo}>
            <input
              type="text"
              value={newTodo}
              onChange={(e) => setNewTodo(e.target.value)}
              placeholder="Add a new todo"
            />
            <button type="submit">Add Todo</button>
          </form>

          <ul>
            {todos.map((todo) => (
              <li key={todo.id}>
                {todo.text}
                <button onClick={() => deleteTodo(todo.id)}>Delete</button>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  } else {
    return (
      <div>
        <p>User not logged in</p>
      </div>
    );
  }
};

export default Todo;

```

Add your Cloud Function endpoint in `const firebaseFunctionEndpoint = "";`

#### Step 6: Create Route for Todo Component

Open `App.tsx` and add the following route in `<Routes>`:

```tsx
<Route path="/todos" element={<Todo />} />
```

Make sure to import the Todo in App.tsx.

```javascript
import Todo from "./Todo";
```

Finally, add a router link to `Home.tsx` just after the **User Settings** link:

```tsx
<Link to="/todos">My Todos</Link>
```

You can click on the **My Todos** link to open the Todo page.

#### Step 7: Update Firestore Security Rule

Return to your project in Firebase console, then navigate to **Build** > **Firestore Database**. Enable Firestore if it's not active for your project.

<figure><img src="/files/7Ob7XX6rQZLBH4LUKBF0" alt=""><figcaption></figcaption></figure>

Go to the Rules tab, then change the security rule to the following:

```javascript
service cloud.firestore {
  match /databases/{database}/documents {

  
    match /todos/{userId} {
      allow read: if request.auth.uid != null;
      allow write: if request.auth.uid != null;
    }

  }
}
```

The above rule will restrict read and write access to data in /todos/{userID} to only authenticated users. Click **Publish** to activate the new rule.

#### Step 8: Run React App

Follow the instructions in the `README.md` file for the Authgear React example app [here](https://github.com/authgear/authgear-example-react/blob/master/README.md) to configure the app with your Authgear project. Then run the app.

You should be able to authenticate with Authgear and add items to the to-do.

<figure><img src="/files/FzGWniHFYa4bpMmzZm9F" alt=""><figcaption><p>UI of demo app</p></figcaption></figure>


# Integrate with Google Tag Manager

Learn how to integrate popular analytics and tracking tools into your Authgear project using Google Tag Manager

Authgear allows you to add third-party user analytics tools to your project using Google Tag Manager.

### What is Google Tag Manager?

Google Tag Manager (GTM) is a tag management tool from Google that makes it easy to add marketing tags to your website without modifying the site's source code.

Tags can help you track traffic and user behavior on your website or application.

In this guide, we will show you how to add Google Tag Manager to your Authgear project and send data to Google Analytics. You can also configure Google Tag Manager to send data from your Authgear project to other marketing tags from providers like Facebook.

### Prerequisites

In order to setup Google Tag Manager and Google Analytics with Authgear, you need to have the following:

1. Authgear account
2. Google Tag Manager Account
3. Google Analytics Account

## Part 1: Connect Google Tag Manager to Authgear project

The process for connecting your GTM account to Authgear is simple and can be done in these two steps.

### Step 1: Get GTM container ID

Google Tag Manager lets you create containers that hold marketing tags. Each container has a unique ID and you'll need this container ID to connect your GTM container to Authgear.

To get the container ID, log in to [GTM](https://tagmanager.google.com/) and navigate to the dashboard's homepage. You should find a list of all your containers and their ID. Note down the ID for the container you wish to connect to Authgear.

<figure><img src="/files/zLn81TupjGXuZNQtIW0L" alt=""><figcaption></figcaption></figure>

If you don't have a container for your Authgear project yet, click on **Create Account** to create a new container. Enter your domain name for your Authgear project as the container name and select a target platform. For this example, we'll select **Web** as the target platform.

<figure><img src="/files/RLg5wFHD2DBsuYTbP2HN" alt=""><figcaption></figcaption></figure>

### Step 2: Add GTM container ID to Authgear

First, log in to the Authgear Portal, then select your project and navigate to **Integrations**.

<figure><img src="/files/2qjwxx8W1flhbia7WGyS" alt=""><figcaption></figcaption></figure>

Click on the **Connect** button next to the Google Tag Manager addon to open the configuration page.

<figure><img src="/files/Wg2Ni0XuoSNNZFAu252g" alt=""><figcaption></figcaption></figure>

Paste the GTM container ID you got from the previous step then click the Save button. And with that, you've successfully connected your GTM container to Authgear. In the next steps, we'll show you how to create tracking tags and send data to Google Analytics.

## Part 2: Track traffic and send data to Google Analytics

Google Analytics is one of the marketing tags we can manage from GTM. In this part of the guide, we'll set up some tags to track page views and user events like clicking on a link or button. The tags will send these data to Google Analytics.

### Step 1: Set up Google Analytics data stream

In order to create tags that send data to Google Analytics, you need to have an active data stream on Google Analytics. GTM requires the details for this stream while creating new tags for Google Analytics.

To create a stream, log in to Google Analytics then navigate to the **Admin** settings page.

<figure><img src="/files/keL2kd6UdkENfiY2CrXw" alt=""><figcaption></figcaption></figure>

Create a new Google Analytics property for your Authgear project or select an existing one. Click on the Data Streams item under the property to view all streams and add a new web stream for your Authgear project.

Note down the Measurement ID for your stream as we'll be using it later to create new tags.

<figure><img src="/files/jLhEsmyus35MpwbMdbOr" alt=""><figcaption></figcaption></figure>

### Step 2: Create a new user-defined variable in GTM

Before we start sending data to Google Analytics, let's create a new variable in Google Tag Manager.

Go back to GTM and select the correct container for your project.

<figure><img src="/files/mlbGIMRYfh21nHTnlTFq" alt=""><figcaption></figcaption></figure>

Next, click on the **Variables** item on the left side navigation bar and create a new user variable with the following details:

* **Variable type**: Data Layer Variable
* **Data Layer Variable Name**: gtm.element.dataset.authgearEvent

Once you're done save the variable as "gtm.element.dataset.authgearEvent" and continue to the next step.

### Step 3: Create a click Trigger

Navigate to **Triggers** from the sidebar and create a new trigger with the following details:

* **Trigger type**: Click > All Elements
* **This trigger fires on**: Some Clicks

Authgear's implementation of GTM is declarative. The primary button on each page has `data-authgear-event` attribute. We'll be setting a condition for the "Some Clicks" using that attribute. Configure Some "Click" as shown below:

<figure><img src="/files/g3jOtA0j6wmpXO8JvYhY" alt=""><figcaption></figcaption></figure>

Next, save the trigger as "Authgear-btn-click" and continue.

### Step 4: Create Page View Tag

Navigate to **Tags** from the sidebar and create a new tag with the following configurations:

* **Tag type**: Google Analytics > Google Tag
* **Tag ID**: \<Your Tag ID is the unique Measurement ID for your stream in Google Analytics (See part 2 step 1 for more details)>

Next, expand the **Advanced Settings** section and set **Tag firing options** to **Once per page**.

Now, scroll down to the Trigger section of the new tag and select **All Pages (page view)** as the trigger.

Save this new tag as "Auth-gear-pageview" and continue.

### Step 5: Create Event Tag

In this step, create another tag with the following configuration:

* Tag type: Google Analytics > Google Analytics: GA4 Event
* Measurement ID: \<Your Google Analytics stream measurement ID>
* Event Name: `gtm.element.dataset.authgearEvent`

Next, set the trigger for this tag to the "Authgear-btn-click" trigger we created earlier.

Save the tag as "Authgear-event-tag" and continue to preview the entire setup or publish to go live.

## Conclusion

After you publish your changes in Google Tag Manager when users generate hits or click buttons with the `data-authgear-event` attribute on your project you should see data on Google Analytics.

The following is a list of values for the `data-authgear-event` attribute:

* `authgear.button.change_password`
* `authgear.button.change_additional_password`
* `authgear.button.create_password`
* `authgear.button.change_login_id`
* `authgear.button.remove_login_id`
* `authgear.button.resend_oob_otp`
* `authgear.button.enter_oob_otp`
* `authgear.button.enter_password`
* `authgear.button.enter_recovery_code`
* `authgear.button.enter_totp`
* `authgear.button.send_reset_password_code`
* `authgear.button.sign_in`
* `authgear.button.sign_up`
* `authgear.button.sign_out`
* `authgear.button.oauth`
* `authgear.button.reset_password`
* `authgear.button.continue_with_current_account`
* `authgear.button.use_another_account`
* `authgear.button.remove_biometric`
* `authgear.button.schedule_account_deletion`
* `authgear.button.connect_oauth`
* `authgear.button.disconnect_oauth`
* `authgear.button.resend_verification_code`
* `authgear.button.update_profile`
* `authgear.button.regenerate_recovery_code`
* `authgear.button.download_recovery_code`
* `authgear.button.remove_totp`
* `authgear.button.remove_oob_otp`
* `authgear.button.setup_oob_otp`
* `authgear.button.setup_totp`
* `authgear.button.enter_verification_code`
* `authgear.button.revoke_session`
* `authgear.button.revoke_session_group`
* `authgear.button.revoke_all_sessions`


# FAQ for Integration


# How to Track User Before and After Signup?

Learn how to track a user that signs up from a particular source or campaign.

In this guide, you'll learn how to track users from a particular source before and after they sign up. For example, knowing how many people sign up for your application through a signup link that you have included in a blog post or an email campaign.

### Prerequisites

To follow this guide, you need to have the following:

* A free Authgear account.
* An application in the Authgear Portal and note down the configuration of the application.
* A client application that is written in your preferred language or framework.

### What We'll Build

For this guide, we'll build a demo Express application that has the following features:

* Read a `source` URL parameter defined by you (the developer).
* Send the value of the `source` parameter to the Authgear authorization server using the OAuth 2.0 `state` parameter.
* Read the value of `state` returned after user authorization (sign up) and log the value in a database or analytics system like Mixpanel.

### What is the OAuth 2.0 State Parameter?

The [OAuth 2.0 framework](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1) includes an optional `state` parameter. The value of the `state` parameter can be any random string or number defined by a client application (e.g a web or mobile that uses Authgear for user authentication) before the client makes an authorization request. In simple terms, the `state` parameter is added to the authorization URL as a URL query.

The authorization server (Authgear) will include the same state parameter value when redirecting the user-agent back to the client application. As a result, the client application can retrieve the value of `state` returned to verify that it is the origin of the authorization request.

We will use the above behavior of the authorization process to track a user before and after they sign up.

## How to Track a User Who Signs Up from a Particular Source

The following steps show the steps for building a simple application that is capable of tracking users before and after they sign up.

### Step 1: Set Up Your App to Use Authgear

First, create a new project directory and open it. The demo application for this guide is a simple [Express](https://expressjs.com/) application that use Axios to make HTTP requests. Hence, install both packages using the following commands:

1\. Express:

```sh
npm install express
```

2\. Axios:

```sh
npm install axios
```

Next, inside the project directory, create a new **app.js** file then add the following code to the file:

```javascript
const express = require('express');
const axios = require('axios');

const app = express();
const port = process.env.PORT || 3000;

const config = {
  client: {
    id: "CLIENT_ID",
    secret: "CLIENT_SECRET",
    redirect_url: "REDIRECT_URL"
  },
  auth: {
    tokenHost: "AUTHGEAR_ENDPOINT",
    tokenPath: '/oauth2/token',
    authorizePath: '/oauth2/authorize',
    scope: 'openid offline_access'
  },
};
```

You can get the correct configuration values (CLIENT\_ID, CLIENT\_SECRET, REDIRECT\_URL, and AUTHGEAR\_ENDPOINT) from the **Applications** section of the Authgear Portal.

### Step 2: Add State Parameter to Authorization Request

In this step, we'll implement a `/login` route in the demo application. This route will support a `source` query parameter, for example, `/login?source=002`.

Add the following code to the end of **app.js** to implement the `/login` route:

```javascript
app.get("/login", (req, res) => {

  const url = new URL(`${config.auth.tokenHost}${config.auth.authorizePath}`);
  url.searchParams.set('client_id', config.client.id);
  url.searchParams.set('redirect_uri', config.client.redirect_url);
  url.searchParams.set('response_type', 'code');
  url.searchParams.set('scope', config.auth.scope);

  if (req.query.source != null) {
    url.searchParams.set('state', req.query.source);
  }

  res.redirect(url);
  
});
```

The above code will check if the source parameter is included in the HTTP request to the `/login` route. If there is a `source` parameter, the value of the source will be added to the authorization URL's state parameter.

The following is an example of a login URL that includes the source parameter:

```url
http://localhost:3000/login?source=002
```

### Step 3: Read the Value of State After Authorization

As described earlier, Authgear will return whatever value you put in the `state` parameter of the authorization URL. In the last step, we will read the value of the `source` parameter from the `/login` route and pass it to the `state` parameter. In this step, we'll read the `state` parameter that Authgear returns in the redirect URI for the initial value we passed (`source`).

To do this, we'll add a `/` route to resolve our redirect URI. Add the following code to the end of **app.js** to implement the route:

```javascript
app.get("/", async (req, res) => {

  if (req.query.code != null) {
    const data = {
      client_id: config.client.id,
      client_secret: config.client.secret,
      code: req.query.code,
      grant_type: 'authorization_code',
      response_type: 'code',
      redirect_uri: config.client.redirect_url,
      scope: config.auth.scope
    };

    const sourceFromState = req.query.state;

    try {
      const getToken = await axios.post(`
        ${config.auth.tokenHost}${config.auth.tokenPath}`,
        data,
        {
        headers: { "Content-Type": "application/x-www-form-urlencoded" }
        }
      );

      const accessToken = getToken.data.access_token;
      res.send(`
      <p>Access token: ${accessToken}, ${sourceFromState}</p>
    `);
    } catch (error) {
      res.send("An error occurred! Login could not complete. Error data: " + error);
    }
  }

  else {
    res.send(`
      <div style="max-width: 650px; margin: 16px auto; background-color: #EDEDED; padding: 16px;">
        <p>Hi there!</p>
        <p>This demo app shows you how to add user authentication to your Express app using Authgear</p>
          <p>Checkout <a href="https://docs.authgear.com">docs.authgear.com</a> to learn more about adding Authgear to your apps.</p>
        <a href="/login">Login</a>
      </div>
    `);
  }
});

app.listen(port, () => {
  console.log(`server started on port ${port}!`);
});
```

The `sourceFromState` constant holds the value for the state parameter in the redirect URI. You can save this value to a database to track that the user has successfully signed up using the link with the source value in your original campaign link. You may also send this link to an analytic tool like Mixpanel to track the user and source.

To run the demo app, run the following command in the terminal:

```sh
node app.js
```

Then, open `http://localhost:3000/login?source=002` on a browser. You can change 002 to any random string that you wish to use for tracking a source or campaign. Also, be sure to add `http://localhost:3000` as a redirect URL for your application in the Authgear Portal.


# What happens with user directly access the Authgear Endpoint?

Endpoint Direct Access lets you configure what happens when users unexpectedly access your Authgear project endpoint without using OIDC/SDK/SAML.

Normally, the user should access the signup/login page through an OIDC flow, via the SDK or SAML. In such cases, the user will be redirected to the application specified by the flow.

Use the Endpoint Direct Access settings to configure what happens when users directly visit the endpoint URL (e.g. `https://my-project.authgear.cloud`) of an Authgear project unexpectedly.

By default, when users access the endpoint of a project directly, they'll see an error page that looks like this:

<figure><img src="/files/VRt8t0Y48CiFR2ibcreW" alt="" width="375"><figcaption></figcaption></figure>

## Show a "Back to home" link

Instead of the default error message, you can show a link to redirect the user away under your logo and project name. Go to **Advanced** > **Endpoint Direct Access,** select "**Show a simple brand page with a back to home link**" and provide the link.

<figure><img src="/files/MBY4t3Jl4dxb7i7Z8S4P" alt="" width="375"><figcaption><p>Show a link under your logo and project name</p></figcaption></figure>

## Cookie-based Authentication

If you are using [Cookie-based authentication](#cookie-based-authentication), you can set Authgear to render the signup/login page even when the user is accessing the Endpoint URL directly. And therefore these options are only available when [custom domain](/customization/custom-domain) is enabled.

When user access the endpoint directly, they will be redirected to the `/login` page. After login, the user will be directed to the [User Settings](/customization/built-in-ui/user-settings) page (`/settings`).

{% hint style="info" %}
This is only applicable for cookie-based authentication. If you integrate Authgear with the SDKs, OIDC or SAML protocols, the user should not access the endpoint URL directly in their browser. The authentication should be triggered from the flows.
{% endhint %}

### **Show the login page and redirect to the hosted settings page**

When the user accesses the endpoint URL directly in their browser, show the authentication page. After the user is authenticated, they are redirected to the hosted [User Settings](/customization/built-in-ui/user-settings) page which they can manage their account.

### **Show the login page and redirect to a custom URL**

When the user accesses the endpoint URL directly in their browser, show the authentication page. After the user is authenticated, they are redirected to a custom URL, which is usually your application website.

## Post-logout URL

You should use the logout function in the SDK, the end-session endpoint for OIDC integration and Logout URL for SAML integration, in which the post-logout behaviour are specified according to the flows.

But if the user directly accesses the logout page, e.g. `https://myproject.authgear.cloud/logout`, the Post-Logout URL in Endpoint Direct Access settings controls where Authgear will redirect users after logout.




---

[Next Page](/llms-full.txt/1)

