INEX SSO (INEX ID)
OpenID ConnectINEX ID is INEX's single sign-on service. Your customers sign in to your platform with their INEX account — no separate registration or password — and you receive their permanent, unique identifier (sub), which you then use to create parcels in their name without asking for their personal number again.
Standard
openid-client for Node.js, league/oauth2-client / jumbojett/openid-connect-php for PHP, Spring Security, ASP.NET Core OpenIdConnect, AppAuth for mobile) — there is no need to hand-roll it.
What you get
- One-button sign-in — "Sign in with INEX" — in your website and mobile app.
- Registration on the INEX side: first and last name, email and phone (both confirmed with a code) and the personal number. You no longer need to collect customer data yourself.
- Google and Apple sign-in, password recovery and switching between accounts — all on the INEX ID pages.
-
A permanent identifier,
sub, which you pass asreceiver.inexUserIdwhen creating a parcel.
Environments
The issuer is written exactly like this in both environments (no trailing /) — the iss claim of the id_token must match it character for character. Client IDs and secrets differ between environments: you receive a separate pair for staging and production.
Discovery document
Every endpoint address, the supported scopes, claims and signing algorithm are published at the standard /.well-known/openid-configuration address. Most OIDC libraries need only the issuer URL and read everything else from there.
Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
| /.well-known/openid-configuration | GET | Configuration document (Discovery). |
| /oauth/authorize | GET | Redirect the customer here to sign in / register. |
| /oauth/token | POST | Exchange the code for tokens, refresh tokens (server side). |
| /userinfo | GET | The customer's current data, with an access token. |
| /oauth/jwks | GET | Public keys for verifying the id_token signature (JWKS). |
| /oauth/logout | GET | Sign out of INEX ID (RP-Initiated Logout). |
How it works — step by step
-
1
The customer clicks "Sign in with INEX"
Your server generates a
state, anonceand a PKCE pair, keeps them in the customer's session and redirects the browser to/oauth/authorize. -
2
Sign-in on INEX ID
The customer signs in (email/phone + password, Google, Apple) or registers. If the browser is already signed in, no form is shown at all — they come straight back.
-
3
Back to you with a code
INEX ID returns the browser to your registered
redirect_uriwith?code=…&state=…. You check thatstatematches the one you stored. -
4
Exchange the code for tokens
Your server calls
POST /oauth/tokenwith the code and receives anaccess_token, arefresh_tokenand anid_token. -
5
Verify the id_token
You verify the signature (JWKS),
iss,aud,expandnonce. On success you have thesuband the profile data. -
6
Link the customer
You store the
subwith your user. If you already know thissub, it is the same person; if not, you create a new local account or link an existing one. -
7
Create the parcel
At checkout you call
POST /integrators/parcels/createand name the receiver withreceiver.inexUserId = sub— no personal number, name or phone.
Onboarding
Your client (application) is registered by the INEX technical team. Send us, for each environment:
Application name and logo
Shown on the INEX ID sign-in page: "Sign in to your application with INEX". Logo — square PNG/SVG.
Website, privacy policy, terms and conditions
Links shown next to your application name on the form.
List of redirect URIs
The exact addresses (scheme, host, port, path) the customer returns to with a code, and returns to after logout. Matching is exact — wildcards do not work. HTTPS only in production.
Client type
Confidential (recommended) — your server exchanges the code and uses a client_secret. Public — a mobile app or SPA that cannot keep a secret; such a client relies on PKCE alone.
Public IP addresses of your servers
If you also create parcels through our Integrator API (the usual IP whitelist).
In return you receive a client_id and, for a confidential client, a client_secret. We store the secret only as a hash and cannot show it again — if it is lost, we issue a new one.
client_secret
client_secret in frontend code, a mobile app or anywhere visible in the browser. The token exchange must always happen on your server.
curl https://id-staging.inex.ge/.well-known/openid-configuration
{
"issuer": "https://id-staging.inex.ge",
"authorization_endpoint": "https://id-staging.inex.ge/oauth/authorize",
"token_endpoint": "https://id-staging.inex.ge/oauth/token",
"userinfo_endpoint": "https://id-staging.inex.ge/userinfo",
"jwks_uri": "https://id-staging.inex.ge/oauth/jwks",
"end_session_endpoint": "https://id-staging.inex.ge/oauth/logout",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_post", "client_secret_basic"],
"scopes_supported": ["openid", "profile", "email", "phone", "inex.parcel"],
"ui_locales_supported": ["ka", "en", "ru"],
"prompt_values_supported": ["none", "login", "select_account", "create"]
}
Customer Your app / server INEX ID
| | |
|-- "Sign in" ------>| |
| |-- 302 /oauth/authorize ->|
|------------------- sign in / register ------->|
|<------------- 302 redirect_uri?code&state ----|
|------ code ------->| |
| |-- POST /oauth/token ---->|
| |<-- id_token (sub) -------|
| | |
| |-- POST /integrators/parcels/create
| | receiver.inexUserId = sub