Authorization

OAuth 2.0 Applications, tokens and integration flow for Speaknode API

The Speaknode API uses OAuth 2.0 (Authorization Code + PKCE) on top of a Keycloak realm. To call the API on behalf of a Speaknode user from your own application, register an OAuth Application, run the standard Authorization Code flow against the identity server, and use the returned access_token as a Bearer token.

Quick token for one-off curl / Postman calls — open https://app.speaknode.com/token while signed in. The page shows the current access token, no app registration required.

1. Register your OAuth Application

Sign in to Speaknode and open https://app.speaknode.com/developersOAuth ApplicationsCreate OAuth app:

  • Name — anything human-readable (e.g. My CRM Integration).
  • Redirect URIs — exact URLs your app will receive the auth code on (e.g. https://my-crm.example.com/oauth/callback). At least one is required; multiple are allowed for staging/prod.
  • Type:
    • Public — for browser/SPA/mobile apps that cannot keep a secret. PKCE is mandatory.
    • Confidential — for server-side apps. You receive a client_secret once on creation and must store it securely.

After creation the UI shows:

{
  "client_id":     "8f3a1c2e-...-...",
  "client_secret": "xPq..."
}

The client_secret is shown only once — copy it immediately. If lost, rotate it from the application's settings page.

The OAuth endpoints (auth_url, token_url, etc.) are the same for every application on the platform — see §2 below.

2. Endpoints

Base URL: https://identity.speaknode.com/realms/speaknode

PurposePath
OIDC discovery/.well-known/openid-configuration
Authorize/protocol/openid-connect/auth
Token/protocol/openid-connect/token
UserInfo/protocol/openid-connect/userinfo
Logout/protocol/openid-connect/logout
JWKS (signature keys)/protocol/openid-connect/certs

3. Authorization Code flow with PKCE

Step 1 — generate a PKCE pair

code_verifier  = random url-safe string (43–128 chars)
code_challenge = BASE64URL(SHA256(code_verifier))

Keep code_verifier server-side / in memory until step 3.

Step 2 — redirect the user to authorize

Send the user's browser to:

GET https://identity.speaknode.com/realms/speaknode/protocol/openid-connect/auth
  ?client_id=<your-client-id>
  &redirect_uri=https://my-crm.example.com/oauth/callback
  &response_type=code
  &scope=openid profile email offline_access
  &code_challenge=<challenge>
  &code_challenge_method=S256
  &state=<csrf-random>
  • Include offline_access in scope if you want a long-lived refresh_token (see §5).
  • Optional: &kc_locale=ru to force the login UI / emails to a specific language.

The user signs in to their Speaknode account and Keycloak redirects to your callback:

https://my-crm.example.com/oauth/callback?code=<code>&state=<state>

Validate that state matches the value you sent.

Step 3 — exchange the code for tokens

POST https://identity.speaknode.com/realms/speaknode/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=<your-client-id>
&client_secret=<your-client-secret>      # confidential clients only
&code=<code>
&redirect_uri=https://my-crm.example.com/oauth/callback
&code_verifier=<verifier>

Response:

{
  "access_token":       "<jwt>",
  "expires_in":         86400,
  "refresh_token":      "<jwt>",
  "refresh_expires_in": 2592000,
  "id_token":           "<jwt>",
  "token_type":         "Bearer",
  "scope":              "openid profile email offline_access"
}

Step 4 — call the API

curl -X GET https://api.speaknode.com/api/Users/me \
  -H "Authorization: Bearer <access_token>"

The backend validates the JWT signature against the JWKS endpoint and checks iss, aud, exp.

4. Refreshing the access token

access_token lives 24 hours. Before it expires (or on a 401), exchange the refresh_token:

POST https://identity.speaknode.com/realms/speaknode/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&client_id=<your-client-id>
&client_secret=<your-client-secret>      # confidential clients only
&refresh_token=<refresh_token>

Returns a new access_token plus a new refresh_token (rotated).

5. Token lifetimes

TokenLifetimeNotes
access_token24hUse as Bearer to call https://api.speaknode.com
Browser SSO session24h idle / 24h maxAffects the user's login session in Speaknode, not your refresh_token
refresh_token (offline)30 days idle, no hard capGranted only when scope contains offline_access. Survives the user logging out of Speaknode. Refresh at least once every 30 days.

If you do not request offline_access, your refresh_token is tied to the user's SSO session — when they log out of Speaknode, your refresh stops working. For background integrations always include offline_access.

6. Logout

Revoke the user's session and the refresh token:

POST https://identity.speaknode.com/realms/speaknode/protocol/openid-connect/logout
Content-Type: application/x-www-form-urlencoded

client_id=<your-client-id>
&client_secret=<your-client-secret>      # confidential clients only
&refresh_token=<refresh_token>

7. Security checklist

  • Public clients must use PKCE. Never embed a client_secret in a browser, mobile app or any binary you ship to users.
  • Confidential clients must keep the secret server-side. Rotate it from the application settings page if exposed.
  • Always validate state on the callback to prevent CSRF.
  • Validate id_token / access_token against the JWKS endpoint (/protocol/openid-connect/certs) — never trust the JWT body without signature verification.
  • Restrict redirect_uri to exact values; avoid wildcards.

On this page