--- description: >- Authenticate the incoming HTTP requests by validating JWT in your application server --- # Validate JWT in your backend [![LLM | View as markdown](https://img.shields.io/badge/LLM-View%20as%20markdown-blue)](https://raw.githubusercontent.com/authgear/docs/refs/heads/main/get-started/backend-api/jwt.md) 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.

Enable this option in 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. ## Payload of the JWT access token See the claims in the access token in this reference: [jwt-access-token.md](../../api-reference/tokens/jwt-access-token.md "mention"). Learn more about [add-custom-fields-to-a-jwt-access-token.md](../../integration/add-custom-fields-to-a-jwt-access-token.md "mention") 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:///.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://" 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 `. ```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" "regexp" "time" "github.com/lestrrat-go/jwx/jwk" "github.com/lestrrat-go/jwx/jwt" ) var ( authzHeaderRegexp = regexp.MustCompile("(?i)^Bearer (.*)$") baseAddress = "https://" ) 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 } ``` {% 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 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 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 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.md) 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.md#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 %}