Authgear
Start BuildingHomePortalCommunity
  • Authgear Overview
  • Get Started
    • Start Building
    • 5-Minute Guide
    • Single-Page App
      • JavaScript (Web)
      • React
      • Angular
      • Vue
    • Native/Mobile App
      • iOS SDK
      • Android SDK
        • Android Kotlin coroutine support
        • Android OKHttp Interceptor Extension (Optional)
      • Flutter SDK
      • React Native SDK
      • Ionic SDK
      • Xamarin SDK
      • Using Authgear without SDK (Client side)
    • Regular Web App
      • Express
      • Next.js
      • Python Flask App
      • Java Spring Boot
      • ASP.NET Core MVC
      • Laravel
      • PHP
    • Backend/API Integration
      • Validate JWT in your application server
      • Forward Authentication to Authgear Resolver Endpoint
    • AI Coding tools
      • Cursor/Windsurf
  • How-To Guides
    • Authenticate
      • Add Passkeys Login
      • Add WhatsApp OTP Login
      • Add Email Magic Link Login
      • Add Biometric Login
      • Add Anonymous Users
      • Add authentication to any web page
      • Enable Two-Factor Authentication (2FA)
      • Reauthentication
      • Passwordless Login for Apple App Store Review
      • Change Forgot/Reset Password settings
      • Adjust Phone Number Validation
      • Set Password Expiry
      • Use the OAuth 2.0 State Parameter
      • Setup local development environment for Cookie-based authentication
      • Use Social/Enterprise Login Providers Without AuthUI
    • Single Sign-on
      • App2App Login
      • Pre-authenticated URLs
      • SSO between Mobile Apps / Websites
      • Force Authgear to Show Login Page
      • Single Sign-On with OIDC
      • Single Sign-On with SAML
        • Use Authgear as SAML Identity Provider for Salesforce
        • Use Authgear as SAML Identity Provider for Dropbox
        • SAML Attribute Mapping
    • Social Login / Enterprise Login Providers
      • Social Login Providers
        • Connect Apps to Apple
        • Connect Apps to Google
        • Connect Apps to Facebook
        • Connect Apps to GitHub
        • Connect Apps to LinkedIn
        • Connect Apps to WeChat
      • Enterprise Login Providers
        • Connect Apps to Azure Active Directory
        • Connect Apps to Microsoft AD FS
        • Connect Apps to Azure AD B2C
      • Force Social/Enterprise Login Providers to Show Login Screen
    • Integrate
      • Add custom fields to a JWT Access Token
      • User Analytics by Google Tag Manager
      • Track User Before and After Signup
      • Custom domain
      • Custom Email Provider
      • Custom SMS Provider
        • Twilio
        • Webhook/Custom Script
      • Integrate Authgear with Firebase
    • Monitor
      • Audit Log For Users Activities
      • Audit Log for Admin API and Portal
      • Analytics
    • User Management
      • Account Deletion
      • Import Users using User Import API
      • Export Users using the User Export API
      • Manage Users Roles and Groups
      • How to Handle Password While Creating Accounts for Users
    • User Profiles
      • What is User Profile
      • Access User Profiles
      • Update User Profiles
      • Profile Custom Attributes
      • Update user profile on sign-up using Hooks
    • Events and Hooks
      • Event List
      • Webhooks
      • JavaScript / TypeScript Hooks
      • Only Allow Signups from Inside the Corporate Network using Hooks
    • Mobile Apps
      • Use SDK to make authorized API calls to backend
      • Force authentication on app launch
      • Customize the Login Pop-up / Disable the login alert box
    • Migration
      • Bulk migration
      • Rolling migration
      • Zero-downtime migration
    • Directly accessing Authgear Endpoint
    • Troubleshoot
      • How to Fix SubtleCrypto: digest() undefined Error in Authgear SDK
      • How to Fix CORS Error
  • Design
    • Built-in UI
      • Branding in Auth UI
      • User Settings
      • Privacy Policy & Terms of Service Links
      • Customer Support Link
      • Custom Text
    • Custom UI
      • Authentication Flow API
      • Implement Authentication Flow API using Express
      • Implement Authentication Flow API using PHP
      • Add Custom Login/Signup UI to Native Apps
      • Manually Link OAuth Provider using Account Management API
      • Implement a custom account recovery UI using Authentication Flow API
    • Languages and Localization
    • Custom Email and SMS Templates
  • Concepts
    • Identity Fundamentals
    • Authgear use cases
    • User, Identity and Authenticator
  • Security
    • Brute-force Protection
    • Bot Protection
    • Non-HTTP scheme redirect URI
    • Password Strength
  • Authgear ONCE
    • What is Authgear ONCE
    • Install Authgear ONCE on a VM
    • Install Authgear ONCE on Vultr
    • Install Authgear ONCE on Amazon Web Services (AWS)
  • Reference
    • APIs
      • Admin API
        • Authentication and Security
        • API Schema
        • Admin API Examples
        • Using global node IDs
        • Retrieving users using Admin API
        • User Management Examples
          • Search for users
          • Update user's standard attributes
          • Update user's picture
          • Generate OTP code
      • Authentication Flow API
      • OAuth 2.0 and OpenID Connect (OIDC)
        • UserInfo
        • Supported Scopes
      • User Import API
      • User Export API
    • Tokens
      • JWT Access Token
      • Refresh Token
    • Glossary
    • Billing FAQ
    • Rate Limits
      • Account Lockout
  • Client App SDKs
    • Javascript SDK Reference
    • iOS SDK Reference
    • Android SDK Reference
    • Flutter SDK Reference
    • Xamarin SDK Reference
  • Deploy on your Cloud
    • Running locally with Docker
    • Deploy with Helm chart
    • Authenticating HTTP request with Nginx
    • Configurations
      • Environment Variables
      • authgear.yaml
      • authgear.secrets.yaml
    • Reference Architecture Diagrams
      • Google Cloud Reference Architecture
      • Azure Reference Architecture
      • AWS Reference Architecture
      • Throughput Scaling Reference
Powered by GitBook
On this page
  • Find the JSON Web Key Sets (JWKS) endpoint
  • Decode user from an access token
  • Check the validity of JWT
  • Decode user from cookies

Was this helpful?

Edit on GitHub
Export as PDF
  1. Get Started
  2. Backend/API Integration

Validate JWT in your application server

Authenticate the incoming HTTP requests by validating JWT in your application server

PreviousBackend/API IntegrationNextForward Authentication to Authgear Resolver Endpoint

Last updated 2 days ago

Was this helpful?

In this section, we will go through how to decode the JWT token to obtain the currently logged-in user.

Before we start, make sure the option Issue JWT as access token is enabled in your Application settings in the Portal.

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.

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

{
    "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.

Step 1: Install packages

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

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>.

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.

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"
        }

Step 1: Install dependencies

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).

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:

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:

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:

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:

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}`);
});

Use your Authgear endpoint as base_address

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "regexp"
    "time"

    "github.com/lestrrat-go/jwx/jwk"
    "github.com/lestrrat-go/jwx/jwt"
)


var (
    authzHeaderRegexp = regexp.MustCompile("(?i)^Bearer (.*)$")
    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, map[string]interface{}, 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 "", nil, fmt.Errorf("failed to fetch JWK: %s", err)
    }

    // get jwt token from Authorization header
    authzHeader := r.Header.Get("Authorization")
    match := authzHeaderRegexp.FindStringSubmatch(authzHeader)
    if len(match) != 2 {
        return "", nil, fmt.Errorf("no token")
    }

    // parse jwt token
    token, err := jwt.ParseString(match[1], jwt.WithKeySet(set))
    if err != nil {
        return "", nil, 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 "", nil, fmt.Errorf("invalid token: %s", err)
    }

    return token.Subject(), token.PrivateClaims(), nil
}

func handler(w http.ResponseWriter, r *http.Request) {
    // decode user example
    userid, claims, err := DecodeUser(r)
    isUserVerified, _ :=
        claims["https://authgear.com/claims/user/is_verified"].(bool)
    isAnonymousUser, _ :=
        claims["https://authgear.com/claims/user/is_anonymous"].(bool)

    // ... your handler logic
}

The following example uses Spring Boot.

Step 1: Install dependencies

Add the following dependencies to your build.gradle file:

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:

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:


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:

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.

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;
		}
	}
}

Step 1: Install Packages

First, install the dependencies required by running these com

composer require firebase/php-jwt
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
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:

$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:

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.

$decoded = JWT::decode($jwt, new Key($signingKey, 'RS256'));
echo json_encode($decoded);

Check the validity of JWT

Decode user from cookies

Validating JWT in your application server is currently only available for Token-based authentication.

Here is .

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 process.

See an example of how to verify the signature of the ID token, and then validate the claims auth_time inside .

For Cookie-based authentication, JWT in cookies is not supported yet. .

an example of how it looks
re-authentication
You can track the issue here
here
Enable this option in application settings in the portal