Overview
Terang AI supports Single Sign-On (SSO) via signed JWT tokens. Your organization signs a JWT with your private key, and Terang AI verifies it using your public key. This allows your members to access Terang AI directly from your platform without creating a separate account.Finding Your Server IP
The IP address you need to whitelist is the outbound IP of your server — the IP thatapi.terang.ai sees when your server makes a request.
The IP whitelist is enforced on the direct connection to
api.terang.ai. If your integration redirects the user’s browser with the JWT in the URL (e.g. window.location = "https://api.terang.ai/sso/verify?token=..."), the gateway will see the user’s browser IP, not your server’s IP, and the request will be rejected.To use IP whitelisting you must deliver the JWT server-to-server — for example by proxying the redirect through your backend so that the HTTP request to api.terang.ai originates from your whitelisted server. If this is not possible for your platform, contact us to discuss alternatives.VM / VPS (DigitalOcean, Linode, etc.)
VM / VPS (DigitalOcean, Linode, etc.)
Use the public IP address of your server. You can find it by running:
Google Cloud (GKE / Kubernetes)
Google Cloud (GKE / Kubernetes)
GKE pods use ephemeral IPs by default, which change on restart. Set up Cloud NAT to get a static outbound IP:
- Go to Network Services → Cloud NAT in the GCP Console
- Create a NAT gateway for your VPC and region
- Choose Manual IP and assign a static IP
- Share that static IP with us
AWS (EKS / EC2)
AWS (EKS / EC2)
- EC2: Use the instance’s Elastic IP (public static IP)
- EKS: Set up a NAT Gateway in your VPC with an Elastic IP. All pod traffic will route through it.
- Go to VPC → NAT Gateways in the AWS Console
- Create a NAT Gateway with an Elastic IP
- Update your private subnet route table to route
0.0.0.0/0through the NAT Gateway - Share the Elastic IP with us
Serverless (Vercel, Railway, etc.)
Serverless (Vercel, Railway, etc.)
Most serverless platforms use shared, rotating IPs which cannot be whitelisted. Options:
- Use a proxy service with a static IP (e.g. QuotaGuard, Fixie)
- Move SSO/API calls to a dedicated server with a static IP
- Contact us to discuss alternative authentication methods
How it works
Base URLs
JWT Specification
Header
Use the RS256 algorithm. Thekid field must match the key ID registered with Terang AI (or the kid in your JWKS):
Payload
¹
membershipId is required for any member who will purchase courses or receive
certificates (the LMS forwards it to the partner API to identify the member).
Tier & identity
The LMS distinguishes three tiers via thesub claim, and reads the matching
identifier from the single membershipId field. Send the value that applies
to the user — the partner (IAI) resolves whichever it is on their side, so the
purchase and certificate APIs do not change per tier.
sub is the authoritative source of the pricing tier on the LMS side. Keep its
value to exactly member / umum / mahasiswa.Public Key Exchange
The Terang AI LMS needs your public key to verify JWT signatures. The gateway itself does not perform cryptographic verification — it only routes requests based on theiss claim.
You have two options for sharing your public key:
Option 1: JWKS Endpoint (Recommended)
Expose your public key at a standard JWKS endpoint. This supports key rotation automatically — when you rotate keys, Terang AI picks up the new key without any manual update.The path and extension are convention, not a hard requirement. Any URL that returns the JWKS JSON over HTTPS works — for example
…/jwks.php or …/jwks is fine if your stack serves it from there. Just share the exact URL with us.
Share your JWKS URL with the Terang AI team, and we will configure it on our end.
Option 2: Share Public Key Directly
If you cannot host a JWKS endpoint, you can share the PEM-formatted public key directly with the Terang AI team.SSO Redirect
Once the JWT is ready, your backend (not the user’s browser) must call the gateway:Gateway behavior
The gateway (api.terang.ai) does not verify the JWT itself. It:
- Peeks at the
issclaim to identify your organization - Checks that the request comes from a whitelisted IP (your backend’s outbound IP)
- Returns a HTTP 302 response with a
Locationheader pointing to the Terang AI LMS
Location header to the user’s browser (via its own 302 response) so the browser can follow the rest of the redirect chain — that is where the LMS session cookie is set.
LMS behavior
The LMS then:- Verifies the JWT signature using your registered public key
- Validates
iss,aud,exp, andjticlaims - Finds or creates the user account based on
emailandmembershipId - Creates a session (sets a cookie on the user’s browser) and redirects the user to the dashboard
Success response
On success, the redirect chain the user’s browser follows looks like this:<your-lms-host> is the per-partner LMS host the gateway routes to based on your iss claim — for example, lms.iai.or.id for IAI. Whatever URL the gateway returns in the Location header is what you forward to the user’s browser.
Both new and existing SSO users land on /dashboard on the first hop they hit after the session cookie is set. There is no separate password-setup step for SSO users.
Error responses
Gateway errors — returned as JSON before the request reaches the LMS:
LMS errors — the LMS responds with a redirect (HTTP 307 in most cases) to its sign-in page with an
error and reason query parameter. The reason value is verbose and reflects the underlying validation failure verbatim — useful for logging on your side. Common cases:
Implementation Examples
PHP
Node.js
API Requests
All API calls go throughapi.terang.ai with a Bearer JWT token in the Authorization header.
Testing
You can decode and inspect your JWT at jwt.io before sending it to Terang AI. Checklist:- Your domain (
iss) is registered with Terang AI (required for gateway routing) - Your server IP address(es) are whitelisted (required for gateway IP check)
- Public key is shared with Terang AI (via JWKS URL or PEM file, used by the LMS for verification)
- JWT header uses
RS256algorithm - JWT
kidheader is set (and matches thekidin your JWKS if using Option 1) - All required payload fields are present
-
audis set toterang.ai -
expis within 5 minutes ofiat -
jtiis unique per request - The call to
api.terang.ai/sso/verifyis made from your backend, not the user’s browser - Redirect following is disabled (
CURLOPT_FOLLOWLOCATION=false/redirect: 'manual') - The
Locationheader returned by the gateway is forwarded to the user’s browser as a 302
Key Rotation Guide
Rotating your keys regularly is a security best practice. The process differs depending on which option you chose for public key exchange.Rotating with JWKS (Option 1)
With JWKS, you can rotate keys with zero downtime — no coordination with Terang AI needed.1
Generate a new RSA key pair
Create a new key pair with a new
kid (e.g. key-2).2
Add the new key to your JWKS endpoint
Your JWKS should now return both the old and new keys:
3
Start signing JWTs with the new key
Update your JWT signing code to use the new private key and set
kid to key-2.4
Wait, then remove the old key
Wait at least 5 minutes (max JWT lifetime) for all old tokens to expire. Then remove
key-1 from your JWKS endpoint.Terang AI caches JWKS responses for up to 1 hour. If you need the new key to be picked up immediately, contact us.
Rotating with Static PEM (Option 2)
With a static key, rotation requires coordination with the Terang AI team.1
Generate a new key pair
Same as above — create a new RSA key pair.
2
Send the new public key to Terang AI
Email the new
public_key.pem to founders@terang.ai. Do not start using the new key yet.3
Wait for confirmation
We will update our configuration and confirm when the new key is active.
4
Switch to the new key
Once confirmed, update your signing code to use the new private key.