OAuth 2.0 & OpenID Connect (OIDC)
Ternis Auth is a certified OAuth 2.0 (RFC 6749) and OpenID Connect 1.0 (OIDC) identity provider. It supports secure, token-based authorization for web applications, mobile apps, single-page applications (SPAs), and machine-to-machine services.
1. Supported Grant Types
| Grant Type | RFC | Primary Use Case | Requires Secret? |
|---|---|---|---|
| Authorization Code with PKCE | RFC 7636 | Web Apps (SSR), SPAs (React, Vue), and Mobile Apps. | No (PKCE recommended) |
| Client Credentials | RFC 6749 §4.4 | Backend daemons, cron jobs, microservices. | Yes |
| Refresh Token | RFC 6749 §6 | Renewing expired access tokens without user re-prompt. | Depending on client type |
2. Authorization Code Flow with PKCE
Proof Key for Code Exchange (PKCE, RFC 7636) prevents authorization code interception attacks and is strongly recommended for all clients.
Flow Diagram
[ User Browser ] [ Client Application ] [ Ternis Auth Server ]
│ │ │
│ ── 1. Click "Sign In" ─> │ │
│ │ ── 2. Generate PKCE ──────> │
│ │ (code_verifier & │
│ │ code_challenge) │
│ <── 3. Redirect to ───── │ │
│ /oauth/authorize │ │
│ │ │
│ ── 4. Prompt for Login & Consent ────────────────────> │
│ <── 5. Redirect to client with auth ?code=... ──────── │
│ │ │
│ ── 6. Deliver Code ────> │ │
│ │ ── 7. POST /oauth/token ──> │
│ │ (code + code_verifier)│
│ │ <── 8. Access & ID Token ── │
Step 1: Generate PKCE Parameters
In your application, generate a high-entropy cryptographically random string (code_verifier), then compute its URL-safe Base64-encoded SHA-256 hash (code_challenge):
// JavaScript (Web Crypto API)
function generateRandomString(length = 64) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
const array = new Uint8Array(length);
crypto.getRandomValues(array);
return Array.from(array, byte => charset[byte % charset.length]).join('');
}
async function generateChallenge(verifier) {
const data = new TextEncoder().encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
Step 2: Redirect User to Authorization Endpoint
GET /oauth/authorize?
response_type=code
&client_id=9dc6e84a-714c-4e89-9a29-bc828cf99874
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20profile%20email%20ternis%3Asso
&state=c3ab8ff13720e8ad9047dd39466b3c89
&code_challenge=E9Melhoa2OwvFrGMTJguCH5rtG64DTb3Ag60-BIvJYA
&code_challenge_method=S256
HTTP/1.1
Host: auth.ternis.net
Step 3: Exchange Code for Tokens
Once the user approves access, Ternis Auth redirects back to your redirect_uri with ?code=AUTHORIZATION_CODE&state=....
Your backend sends a POST request to the token endpoint:
POST /oauth/token HTTP/1.1
Host: auth.ternis.net
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=9dc6e84a-714c-4e89-9a29-bc828cf99874
&client_secret=CLIENT_SECRET_IF_CONFIDENTIAL
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&code=DEF_AUTHORIZATION_CODE
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Success Response:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...",
"refresh_token": "def502008f1b6a378873ad8299a9a3b6...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
3. Scopes & Permissions Matrix
Scopes define the exact data and actions your application can access on behalf of the user:
| Scope | Category | Description |
|---|---|---|
openid |
OIDC Core | Required for OpenID Connect identity assertion; returns sub (user UUID). |
profile |
OIDC Core | User's full name, username, avatar picture URL, and company affiliation. |
email |
OIDC Core | Verified primary email address. |
ternis:sso |
Ecosystem SSO | Allows cross-domain seamless single sign-on without secondary authentication. |
ternis:member |
Membership | Core team affiliation, organization badges, and internal workspace entitlements. |
ternis:customer |
Billing | Subscription status (starter, pro, enterprise), quotas, and SLA support tier. |
ternis:partner |
Commercial | B2B developer status, self-service OAuth client registration, and webhooks. |
ternis:admin |
Administrative | Full platform administrative management (restricted to internal Ternis staff). |
4. OpenID Connect UserInfo (/oauth/userinfo)
Send the access token in the Authorization: Bearer <token> header to receive verified identity claims:
GET /oauth/userinfo HTTP/1.1
Host: auth.ternis.net
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...
Accept: application/json
Response Payload:
{
"sub": "b2f6b897-4001-460d-8386-db93f1d8c1c4",
"name": "Elena Rostova",
"preferred_username": "elena.rostova",
"email": "elena.rostova@ternis.org",
"email_verified": true,
"picture": "https://user.t-cdn.de/b2f6b897-4001-460d-8386-db93f1d8c1c4.png",
"user_type": "ternis_member",
"is_admin": false,
"organization_ids": [
"c8a7f920-5d61-41b3-a1f4-3d078be12884"
],
"subscription_tier": "enterprise",
"updated_at": "2026-09-17T18:00:00Z"
}
5. Refreshing Expired Tokens
When an access token expires, use the refresh_token grant to retrieve a fresh token without user interruption:
POST /oauth/token HTTP/1.1
Host: auth.ternis.net
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&client_id=9dc6e84a-714c-4e89-9a29-bc828cf99874
&client_secret=CLIENT_SECRET
&refresh_token=def502008f1b6a378873ad8299a9a3b6...