Skip to main content

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.
Before you can make any SSO or API requests, the following must be registered and whitelisted by the Terang AI team:
  1. Domain (iss) — your organization’s domain used as the JWT issuer (e.g. iai.or.id)
  2. Server IP address(es) — the outbound IP(s) that will send requests to api.terang.ai
Please contact us at founders@terang.ai with your domain and server IPs to get started. CIDR notation (e.g. 103.x.x.0/24) is supported.

Finding Your Server IP

The IP address you need to whitelist is the outbound IP of your server — the IP that api.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.
Use the public IP address of your server. You can find it by running:
GKE pods use ephemeral IPs by default, which change on restart. Set up Cloud NAT to get a static outbound IP:
  1. Go to Network Services → Cloud NAT in the GCP Console
  2. Create a NAT gateway for your VPC and region
  3. Choose Manual IP and assign a static IP
  4. Share that static IP with us
This ensures all outbound traffic from your cluster uses a predictable IP.
  • 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.
    1. Go to VPC → NAT Gateways in the AWS Console
    2. Create a NAT Gateway with an Elastic IP
    3. Update your private subnet route table to route 0.0.0.0/0 through the NAT Gateway
    4. Share the Elastic IP with us
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

Use the RS256 algorithm. The kid 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 the sub 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.
The exp must be at most 5 minutes after iat. Tokens with a longer expiry will be rejected.

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 the iss claim. You have two options for sharing your public key: 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.
Response format:
Share your JWKS URL with the Terang AI team, and we will configure it on our end.
Need help generating the JWKS response? See our Implementation Examples for copy-paste code in PHP and Node.js.

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.
With this option, key rotation requires manual coordination. You must notify the Terang AI team before rotating your key, otherwise JWT verification will fail. We strongly recommend Option 1 for production use.

SSO Redirect

Once the JWT is ready, your backend (not the user’s browser) must call the gateway:
Do not redirect the user’s browser directly to api.terang.ai/sso/verify (e.g. header('Location: https://api.terang.ai/sso/verify?token=...') or window.location = ...).The gateway’s IP whitelist checks the IP of whoever hits it. If your browser forwards the user there, the gateway sees the user’s IP and will reject the request with 403 IP x.x.x.x is not whitelisted.Always call the gateway from your backend and forward the Location header to the user’s browser. See the implementation examples below.

Gateway behavior

The gateway (api.terang.ai) does not verify the JWT itself. It:
  1. Peeks at the iss claim to identify your organization
  2. Checks that the request comes from a whitelisted IP (your backend’s outbound IP)
  3. Returns a HTTP 302 response with a Location header pointing to the Terang AI LMS
Your backend must then forward that 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:
  1. Verifies the JWT signature using your registered public key
  2. Validates iss, aud, exp, and jti claims
  3. Finds or creates the user account based on email and membershipId
  4. 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.
Your HTTP client must not auto-follow redirects when calling api.terang.ai/sso/verify. If it does (e.g. PHP curl’s default CURLOPT_FOLLOWLOCATION=true, file_get_contents, fetch() default in Node.js), the session cookie will land on your backend instead of the user’s browser, and the user will not be logged in.Disable redirect following (CURLOPT_FOLLOWLOCATION=false, redirect: 'manual') and forward the Location header explicitly.

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 through api.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 RS256 algorithm
  • JWT kid header is set (and matches the kid in your JWKS if using Option 1)
  • All required payload fields are present
  • aud is set to terang.ai
  • exp is within 5 minutes of iat
  • jti is unique per request
  • The call to api.terang.ai/sso/verify is made from your backend, not the user’s browser
  • Redirect following is disabled (CURLOPT_FOLLOWLOCATION=false / redirect: 'manual')
  • The Location header 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.
If you switch to the new private key before we update the public key, all JWT verification will fail and your members will not be able to log in.