Integrating “Sign in with VNTX ID”
VNTX ID is a standard OAuth 2.0 Authorization Server with OpenID Connect on top. If your stack can talk to Google or GitHub, it can talk to us.
Endpoints
| Endpoint | URL | Spec |
|---|---|---|
| Discovery | https://vntxid.ventryx.xyz/.well-known/openid-configuration | OIDC Discovery |
| JSON Web Key Set | https://vntxid.ventryx.xyz/.well-known/jwks.json | OIDC JWKS |
| Authorization | https://vntxid.ventryx.xyz/authorize | RFC 6749 §3.1 |
| Token | https://vntxid.ventryx.xyz/token | RFC 6749 §3.2 (CORS enabled) |
| UserInfo | https://vntxid.ventryx.xyz/userinfo | OIDC Core §5.3 |
| Revocation | https://vntxid.ventryx.xyz/revoke | RFC 7009 |
| RP-initiated logout | https://vntxid.ventryx.xyz/logout | OIDC RP-Initiated Logout |
Step 1 · Get a client
Register in the developer portal. Public means SPAs and mobile apps: PKCE, no secret. Confidential means a server-side app with a secret. Redirect URIs must match exactly.
Step 2 · Send the user to /authorize
GET https://vntxid.ventryx.xyz/authorize ?client_id=YOUR_CLIENT_ID &redirect_uri=YOUR_REGISTERED_URI &response_type=code &scope=openid profile email &state=RANDOM_PER_REQUEST &nonce=RANDOM_ID_TOKEN_NONCE &code_challenge=BASE64URL(SHA256(code_verifier)) &code_challenge_method=S256
PKCE is required for public clients. An unknown client or redirect URI is a hard error page, never a redirect.
Step 3 · Exchange the code
POST https://vntxid.ventryx.xyz/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=RECEIVED_CODE &redirect_uri=SAME_AS_BEFORE &client_id=YOUR_CLIENT_ID &client_secret=YOUR_SECRET # confidential only &code_verifier=PKCE_VERIFIER # if you sent a challenge
Response: access_token (opaque, 1 h), refresh_token (rotating, 30 d, reuse-detection), id_token (RS256 JWT) when openid is in scope.
Step 4 · Read claims
GET https://vntxid.ventryx.xyz/userinfo
Authorization: Bearer <access_token>
{
"sub": "a3f8…" ← pairwise: unique to YOUR app, unlinkable,
"name": "Ada", ← scope: profile
"email": "u9c2f1…@alias.vntxid.ventryx.xyz", ← scope: email (alias)
"email_verified": true
}
ID token claims
| Claim | Meaning |
|---|---|
| iss | Issuer: https://vntxid.ventryx.xyz |
| sub | Pairwise pseudonymous user ID, different for every app |
| aud | Your client_id |
| nonce | Echoed from the authorize request |
| auth_time | When the user authenticated (unix seconds) |
| iat / exp | Issued / expires (ID tokens live 10 minutes) |
| kid | Signing key ID: resolve via JWKS, keys are rotatable |
Scopes & privacy
| Scope | Grants the app | Privacy model |
|---|---|---|
| openid | sub only | A different ID per app. Nobody can join profiles across sites. |
| profile | name | Your display name. Nothing else exists to share. |
| email, email_verified | Private per-app alias by default; the real address never leaves VNTX ID |
Verify the ID token
Fetch https://vntxid.ventryx.xyz/.well-known/jwks.json and verify RS256 with the kid from the JWT header. Your OIDC library does this automatically.
The button
Copy-paste, or restyle freely; just keep the “VNTX ID” wording.
<a class="signin-btn" href="https://your-id-host/authorize?client_id=…&…"> Sign in with VNTX ID </a>
Live example running right now on this deployment: the demo relying party →