Deriv API
K

OAuth 2.0

A complete guide to implementing Login and Sign Up using Deriv's OAuth 2.0 Authorization Code flow with PKCE.

How the flow works

1Generate PKCE
2Redirect to Deriv
3User Authenticates
4Exchange Code
5Use Token
  1. Generate PKCE — Create a code_verifier (random string) and derive code_challenge = BASE64URL(SHA256(code_verifier)). Also generate a random state for CSRF protection.
  2. Redirect to Deriv — Send the user to Deriv's authorization URL with all required parameters.
  3. User authenticates — Deriv shows either the login or registration form. All login and consent screens are managed by the OAuth provider.
  4. Redirect back — Deriv redirects the user to your redirect_uri with an authorization code and state.
  5. Verify state — Confirm the returned state matches what you stored. This prevents CSRF attacks.
  6. Exchange code for token — Your backend sends the code + code_verifier to Deriv's token endpoint and receives an access_token.
  7. Use the token — Make authenticated API calls using the Bearer token.

Before you start

You need:

  • A registered OAuth2 client from Deriv with a client_id and a pre-registered redirect_uri.
  • HTTPS enabled on your redirect URL.
  • Your app must handle redirects, read the authorization code, and exchange it for tokens.

Step 1: Generate PKCE parameters

What is PKCE?

PKCE (Proof Key for Code Exchange, pronounced “pixy”) prevents authorization code interception attacks. Even if an attacker intercepts the authorization code, they cannot exchange it without the original code_verifier that only your app generated and stored.

TermWhat it is
code_verifierA cryptographically random string (43–128 characters) generated by your app
code_challengeBASE64URL(SHA256(code_verifier)) — sent with the authorization request
code_challenge_methodAlways S256 (SHA-256)

Why it works: Only the app that generated the code_verifier can complete the token exchange. Even if an attacker intercepts the authorization code, they cannot exchange it without the verifier.

Generating PKCE in JavaScript

// 1. Generate a random code_verifier
const array = crypto.getRandomValues(new Uint8Array(64));
const codeVerifier = Array.from(array)
  .map(v => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'[v % 66])
  .join('');

// 2. Derive the code_challenge
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(hash)))
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=+$/, '');

// 3. Generate a random state for CSRF protection
const state = crypto.getRandomValues(new Uint8Array(16))
  .reduce((s, b) => s + b.toString(16).padStart(2, '0'), '');

// 4. Store code_verifier and state before redirecting
sessionStorage.setItem('pkce_code_verifier', codeVerifier);
sessionStorage.setItem('oauth_state', state);

Step 2: Redirect the user to the authorization endpoint

Send users to Deriv's OAuth 2.0 authorization endpoint:

https://auth.deriv.com/oauth2/auth

Login

Login uses the standard OAuth2 + PKCE parameters with no additions.

Parameters

ParameterValueDescription
response_type
Required
codeRequest an authorization code
client_id
Required
Your app IDRegistered OAuth2 application ID from Deriv
redirect_uri
Required
Your callback URLMust exactly match the URI registered with Deriv
scope
Required
tradeRequested permissions (trade or admin)
state
Required
Random stringCSRF protection — generate a new value for each request
code_challenge
Required
BASE64URL(SHA256(verifier))The PKCE challenge derived from code_verifier
code_challenge_method
Required
S256Always SHA-256
app_id
Optional
Your legacy app IDYour V1 app ID from the Legacy Deriv API — include this only if you also maintain a legacy API app

Login URL

https://auth.deriv.com/oauth2/auth?
  response_type=code
  &client_id={YOUR_CLIENT_ID}          # e.g. app12345
  &redirect_uri={YOUR_REDIRECT_URI}    # e.g. https://yourapp.com/callback
  &scope=trade
  &state={RANDOM_STATE}                # e.g. abc123random
  &code_challenge={PKCE_CHALLENGE}     # e.g. E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Login URL with legacy app support

https://auth.deriv.com/oauth2/auth?
  response_type=code
  &client_id={YOUR_CLIENT_ID}
  &redirect_uri={YOUR_REDIRECT_URI}
  &scope=trade
  &state={RANDOM_STATE}
  &code_challenge={PKCE_CHALLENGE}
  &code_challenge_method=S256
  &app_id={YOUR_LEGACY_APP_ID}      # V1 app ID from legacy-api.deriv.com

Sign Up

Sign up uses the same base URL and parameters as login, plus one additional required parameter:

Required sign up parameter

ParameterValueDescription
prompt
Required
registrationAlways this exact value. Tells Deriv to show the signup form instead of login.

Optional partner attribution parameters

The following parameters are all optional and managed and set in the Partners dashboard. Include them to attribute signups to your partner account.

ParameterValuePurpose
sidcYour session ID (GUID)Tracking and attribution
utm_campaignYour campaign nameIdentifies the marketing campaign
utm_mediumaffiliateIndicates a partner integration
utm_sourceYour affiliate IDCommission tracking and reporting

Sign Up URL

https://auth.deriv.com/oauth2/auth?
  response_type=code
  &client_id={YOUR_CLIENT_ID}          # e.g. app12345
  &redirect_uri={YOUR_REDIRECT_URI}    # e.g. https://yourapp.com/callback
  &scope=trade
  &state={RANDOM_STATE}                # e.g. abc123random
  &code_challenge={PKCE_CHALLENGE}     # e.g. E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  &prompt=registration
  &sidc={YOUR_SESSION_GUID}            # e.g. 0FB46285-28A0-425E-B2E4-74F07D51EBB8
  &utm_campaign={YOUR_CAMPAIGN}        # e.g. dynamicworks
  &utm_medium=affiliate
  &utm_source={YOUR_AFFILIATE_ID}      # e.g. CU303219

Step 3: Handle the callback

Whether the user logged in or signed up, the callback works exactly the same way. After authentication, Deriv redirects to your redirect_uri:

https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE

If something went wrong:

https://yourapp.com/callback?error=access_denied&error_description=User+cancelled

Your app must:

  1. Verify the state — compare the state from the URL with the value you stored before the redirect. If they don't match, abort — it may be a CSRF attack.
  2. Extract the code — read the code query parameter.

Step 4: Exchange code for tokens

Make a POST request from your backend to the token endpoint. Never perform the token exchange from the browser.

POST https://auth.deriv.com/oauth2/token

Request body (form-encoded)

grant_type=authorization_code
client_id=YOUR_CLIENT_ID
code=AUTH_CODE_FROM_CALLBACK
code_verifier=YOUR_ORIGINAL_CODE_VERIFIER
redirect_uri=https://your-app.com/callback

cURL example

curl -X POST https://auth.deriv.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "code=AUTH_CODE" \
  -d "code_verifier=YOUR_CODE_VERIFIER" \
  -d "redirect_uri=https://your-app.com/callback"

Token response

{
  "access_token": "ory_at_...",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Step 5: Use the access token in API calls

Include the access token as a Bearer token in the Authorization header for all API calls:

Authorization: Bearer YOUR_ACCESS_TOKEN

Example

curl -X GET "https://api.derivws.com/trading/v1/options/accounts" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Quick reference

EndpointURL
Authorizationhttps://auth.deriv.com/oauth2/auth
Token exchangehttps://auth.deriv.com/oauth2/token
API base URLhttps://api.derivws.com

Where to find your values:

ValueWhere
client_idRegister an OAuth2 app with Deriv — you'll receive an app ID
redirect_uriSet during app registration — must match exactly
sidc (signup)Managed and set in the Partners dashboard
utm_source / affiliate ID (signup)Managed and set in the Partners dashboard
utm_campaign (signup)Managed and set in the Partners dashboard
app_id (legacy)Your V1 app ID from legacy-api.deriv.com — only needed if you maintain a Legacy API app

Troubleshooting

ProblemLikely causeFix
State mismatch errorstate in the callback doesn't match stored valueStore state in sessionStorage before redirecting, and don't regenerate it on page load
invalid_grant on token exchangecode_verifier doesn't match the challenge, or code expired/already usedSend the original code_verifier, not a newly generated one; exchange the code immediately
Redirect URI mismatchURL doesn't exactly match what's registeredCheck for trailing slashes, http vs https, port numbers
invalid_clientWrong client_idVerify your credentials from the Deriv dashboard
Login form shows instead of signupMissing prompt=registrationAdd prompt=registration to the authorization URL
Signup not tracked to partnerMissing or wrong UTM parametersVerify sidc, utm_source, utm_medium, and utm_campaign are all present and correct

Implementation checklist

Login

  • response_type is code
  • client_id and redirect_uri are registered with Deriv
  • code_challenge and state are generated fresh for each request
  • code_verifier is stored in sessionStorage before redirect
  • Callback verifies state before exchanging the code
  • Token exchange happens server-side (not in the browser)
  • code_verifier is cleared from storage after use
  • If maintaining a legacy app, app_id is set to your Legacy app ID (optional)

Sign Up (additional)

  • prompt is set to registration (required)
  • sidc, utm_source, utm_campaign, and utm_medium are set if needed — get these from the Partners dashboard (optional)
Click to open live chat support. Get instant help from our support team.