Privileged Access for Web Apps Overview

Add per-request user identity to internal web apps behind a Twingate Gateway.

Twingate Privileged Access for Web Apps

Twingate Privileged Access for Web Apps adds per-request user identity to internal web applications. When you put a web app behind a Twingate Gateway as a Web App Resource, the Gateway injects a signed JWT into every HTTP request. Your app verifies the JWT and gets the authenticated user’s identity without any OIDC integration, client secrets, or redirect flows.

Key benefits

  • No OIDC integration required. The Gateway handles authentication using the user’s existing Twingate session. Your app only needs to verify a JWT.
  • Per-request identity. Every request carries a cryptographically signed token with the user’s identity, device info, and Resource metadata. No session state to manage on your backend.
  • Group-based authorization. The JWT includes the Twingate Group names that authorized the user’s access. Use these for fine-grained access control in your app.
  • Convenience headers for logging. The Gateway also injects X-Twingate-* headers with individual identity fields, useful for access logs and UI personalization without parsing the JWT.

How It Works

The Twingate Gateway acts as a Layer 7 reverse proxy between the Twingate Client and your web app. Before the Client can reach the resource, the Twingate Controller authorizes the user and issues a Gateway Access Token (GAT) scoped to that user, device, and resource. The Client presents the token to the Gateway when it opens the connection, and the Gateway injects it into every HTTP request it forwards upstream.

Browser → Twingate Client → Gateway → Your App
[Injects headers]

Here is what happens on each request:

  • A user navigates to an internal web app (e.g., http://app.internal) in their browser.
  • The Twingate Client intercepts the request and routes it through the Twingate network to the Gateway assigned to that Resource.
  • If the Client does not already hold a valid Gateway Access Token for the Resource, it requests one from the Controller. The Controller authorizes the user against their existing Twingate session and the Resource’s Security Policy, then returns an ES256-signed token carrying the user’s identity, device info, and Resource metadata. No separate login prompt or redirect is needed.
  • The Client connects to the Gateway and presents the token. The Gateway verifies the token and confirms it was issued to that Client.
  • On each HTTP request over that connection, the Gateway injects the headers you configured, such as Authorization: Bearer <token>, and forwards the request to the upstream app.
  • Your app verifies the JWT against Twingate’s JWKS endpoint and extracts the user’s identity.

JWKS Endpoint

Twingate exposes its public signing keys at a standard JWKS endpoint:

https://<your-tenant>.twingate.com/api/v1/jwk/ec

This endpoint is unauthenticated, following the JWKS convention. Most JWT libraries fetch and cache these keys automatically.

JWT Header

The Controller issues an ES256-signed JWT on every forwarded request. The token is a standard JWT in compact form (header.payload.signature), and its header looks like this:

{
"alg": "ES256",
"typ": "GAT",
"kid": "a1b2c3d4"
}
FieldDescription
algUsed to tell the consumer what signing algorithm is used for verification purposes.
typAlways GAT, for Gateway Access Token. This is not the conventional JWT value.
kidKey ID identifying which JWKS key signed this token. Your JWT library uses it to pick the right key.

JWT Payload

Here is the full payload structure:

{
"rnw": 1720003200,
"jti": "a7f2e891-4c3d-48b6-9e1a-5d2f7b8c0e3a",
"iss": "twingate",
"aud": "tenant-slug",
"exp": 1720040400,
"iat": 1720000000,
"ver": "1",
"zver": "0.1.2405001",
"cpk": "LS0tLS1CRUdJTi...LS0tLS0K",
"resource": {
"id": "UmVzb3VyY2U6MTIzNDU2Nzg5",
"type": "WEB_APP",
"address": "internal-dashboard.tenant.svc.cluster.local",
"aliases": [
"dashboard.internal"
],
"gateway_metadata": {
"downstream": {
"port": 80
},
"upstream": {
"port": 80
},
"request_header_rewrites": {}
}
},
"user": {
"id": "VXNlcjo1NTU1NQ==",
"username": "jane.doe@company.com",
"email": "jane.doe@company.com",
"first_name": "Jane",
"last_name": "Doe",
"groups": [
"Everyone",
"twingate:authenticated",
"engineering"
]
},
"device": {
"id": "RGV2aWNlOjg4ODg4",
"location": {
"lat": 37.77,
"lon": -122.42,
"country": "US",
"region": "California",
"city": "San Francisco"
}
}
}

Common Token Claims

ClaimTypeDescription
rnwIntegerUnix timestamp for the renewal window.
jtiStringUnique JWT identifier (UUID).
audStringAudience — your Twingate network name.
expIntegerUnix timestamp after which the token is invalid. Your middleware should reject expired tokens.
iatIntegerUnix timestamp when the token was created.
verStringToken format version.

User Claims

ClaimTypeDescription
user.idStringTwingate user ID. Stable across sessions for the same user.
user.usernameStringThe user’s email address or IdP username.
user.emailStringThe user’s email address.
user.first_nameStringThe user’s first name.
user.last_nameStringThe user’s last name.
user.groupsArrayTwingate Group names that authorized access to this Resource. Always includes twingate:authenticated. Use these for authorization decisions in your app.

Device Claims

ClaimTypeDescription
device.idStringTwingate device ID. Identifies the specific device the request came from.
device.location.latNumberLatitude of the device’s location.
device.location.lonNumberLongitude of the device’s location.
device.location.countryStringISO 3166-1 alpha-2 country code of the device’s location.
device.location.regionStringRegion or state of the device’s location.
device.location.cityStringCity name of the device’s location.

Resource Claims

ClaimTypeDescription
resource.idStringTwingate Resource ID.
resource.typeStringAlways WEB_APP for Web App Resources.
resource.addressStringThe upstream address configured for this Resource.
resource.aliasesArrayDNS aliases configured for this Resource.
resource.gateway_metadata.downstream.portIntegerThe port on which the Twingate Client intercepts the user’s request.
resource.gateway_metadata.upstream.portIntegerThe port of the web application that the Gateway connects to.
resource.gateway_metadata.request_header_rewritesObjectHeader rewrite rules applied to requests.

Request Headers

The Gateway can inject custom HTTP headers into every forwarded request. Until you configure at least one header, your app receives an ordinary request with no identity or extra information injected.

Where Headers Are Defined

Headers come from two places, and both are opt-in:

  • Gateway-wide headers apply to every web app a Gateway proxies. Use these for a header every app should receive, such as Authorization. You configure them on the Gateway itself, which means in your Helm values if you deploy with the Twingate Operator.
  • Per-Resource header rewrites apply to a single Resource, letting one app receive a header in the exact form it expects. You can manage these in the Admin Console, through the Twingate API, or in your Kubernetes manifests.

The Gateway applies per-Resource rewrites after Gateway-wide headers, so a rewrite overrides a Gateway-wide header of the same name.

Configuring Headers

Each header is defined with a key and a value template. The key can be any HTTP header name. The value supports template variables that the Gateway replaces with request-specific data.

Available Template Variables

VariableDescriptionExample Value
{{jwt}}The signed ES256 JWT containing the full user identity.eyJhbG...
{{username}}The user’s email address or username.user@company.com
{{groups}}Comma-separated list of Twingate Group names. Always includes the built-in twingate:authenticated group (and Everyone) in addition to the Groups you assign.twingate:authenticated,Everyone,engineering
{{clientGeoLatLong}}Client’s latitude and longitude.37.7749,-122.4194
{{clientGeoCity}}Client’s city.San Francisco
{{clientGeoRegion}}Client’s region or state.California
{{clientGeoCountry}}Client’s country code.US

Example Configuration

The recommended configuration is to pass the JWT as a Bearer token for applications that can validate JWTs signed by Twingate. As a fallback, you can use plaintext headers, but make sure they come only from the Gateway:

Header KeyValue Template
AuthorizationBearer {{jwt}}

| Header Key | Value Template | | X-Twingate-User | {{username}} | | X-Twingate-Groups | {{groups}} |

Configuring Headers with the Twingate Operator

If you deploy the Gateway with the Twingate Kubernetes Operator, you can define headers in your manifests rather than the Admin Console. Each surface expresses the same idea in a different shape, so check which one you are editing.

Gateway-wide headers are a map in your Helm values:

gateway:
webApp:
enabled: true
requestHeaders:
Authorization: "Bearer {{jwt}}"

Per-Resource rewrites are a list of name and value pairs on the TwingateResource:

apiVersion: twingate.com/v1beta
kind: TwingateResource
metadata:
name: internal-dashboard
spec:
type: WebApp
name: "Internal Dashboard"
address: internal-dashboard.default.svc.cluster.local
alias: dashboard.internal
gatewayRef:
name: twingate-gateway
downstream:
port: 80
upstream:
port: 3000
requestHeaderRewrites:
- name: X-Twingate-User
value: "{{username}}"

If you let the operator create the Resource from a Service annotation instead, the same rewrites are a JSON object inside a string:

metadata:
annotations:
resource.twingate.com: "true"
resource.twingate.com/type: "WebApp"
resource.twingate.com/gatewayName: "example-gateway"
resource.twingate.com/requestHeaderRewrites: '{"Authorization": "Bearer {{jwt}}"}'

Getting Started

To add Privileged Access to your web app, you need:

  • A Twingate account with administrator privileges
  • A deployed Twingate Gateway with a Web App Resource configured

See the developer guides for drop-in middleware you can add to your app in minutes. Guides are available for Express.js, Django, Next.js, and Next.js + Auth.js.

For off-the-shelf apps you run but don’t build, see the application integration guides. Apps like Grafana and Jenkins already support trusted-header authentication, so the Gateway can sign users in without any code changes.

For general information about deploying Twingate Identity Firewall, see the Identity Firewall Overview.

Join us in the community subreddit to share your setup experience or ask questions.

Last updated 1 hour ago