Skip to main content
DHMAD supports OAuth 2.0 for Single Sign-On (SSO) integration, allowing users to authenticate with their DHMAD accounts in your application.

Overview

OAuth 2.0 allows your application to:
  • Authenticate users with their DHMAD accounts
  • Access user information (with permission)
  • Provide seamless SSO experience

Getting Your OAuth Credentials

  1. Log into the Developer Dashboard
  2. Navigate to the “OAuth Client Credentials” section
  3. Copy your Client ID and Client Secret
Keep your Client Secret secure! Never expose it in client-side code or commit it to version control.

Configuring Redirect URIs

Redirect URIs must be full URLs including a path. During authorization, the redirect_uri parameter must exactly match one of your registered URIs.
https://example.com/callback
https://example.com/auth/dhmad/callback
Domain-only URIs (e.g., https://example.com) are not accepted. You must include a path such as /callback or /auth/callback. Each redirect URI is matched exactly — no wildcard or pattern matching is performed.

OAuth Flow

The OAuth 2.0 authorization code flow with PKCE:
  1. Authorization Request - Redirect user to authorization endpoint (the state parameter is required)
  2. Consent Screen - User sees which application is requesting access and what permissions (scopes) are being requested
  3. User Authentication - User logs in with their DHMAD account
  4. Authorization - User authorizes the application
  5. Authorization Code - User is redirected back with code and state
  6. Token Exchange - Exchange code for access token
  7. User Info - Use access token to get user information
The state parameter is required. Requests without a state parameter will be rejected with an invalid_request error. Generate a unique, random value for each authorization request and validate it on the callback to prevent CSRF attacks.

Implementation Example

// Step 1: Generate PKCE challenge and state
import crypto from 'crypto';

function generatePKCE() {
  const codeVerifier = crypto.randomBytes(32).toString('base64url');
  const codeChallenge = crypto
    .createHash('sha256')
    .update(codeVerifier)
    .digest('base64url');
  
  return { codeVerifier, codeChallenge };
}

// Step 2: Redirect to authorization
const { codeVerifier, codeChallenge } = generatePKCE();
sessionStorage.setItem('code_verifier', codeVerifier);

// state is REQUIRED — generate a random value and store it for validation
const state = crypto.randomBytes(16).toString('hex');
sessionStorage.setItem('oauth_state', state);

const params = new URLSearchParams({
  client_id: CLIENT_ID,
  redirect_uri: REDIRECT_URI,
  response_type: 'code',
  scope: 'openid profile email',
  code_challenge: codeChallenge,
  code_challenge_method: 'S256',
  state
});

window.location.href = `https://dhmad.tn/api/oauth/authorize?${params}`;

// Step 3: Handle callback — validate state before using the code
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const returnedState = urlParams.get('state');

if (returnedState !== sessionStorage.getItem('oauth_state')) {
  throw new Error('Invalid state parameter — possible CSRF attack');
}

// Step 4: Exchange code for token
const tokenResponse = await fetch('https://dhmad.tn/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code,
    redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    code_verifier: sessionStorage.getItem('code_verifier')
  })
});

const tokens = await tokenResponse.json();

// Step 5: Get user info
const userResponse = await fetch('https://dhmad.tn/api/oauth/userinfo', {
  headers: {
    'Authorization': `Bearer ${tokens.access_token}`
  }
});

const user = await userResponse.json();

Scopes

Available OAuth scopes:
  • openid - OpenID Connect (required for ID token)
  • profile - User profile information
  • email - User email address

Security Best Practices

DHMAD only supports the S256 code challenge method for PKCE. The plain method is not accepted and will be rejected with an invalid_request error. Always use SHA-256 to hash your code verifier.

Use PKCE (S256)

Always use PKCE with the S256 code challenge method (plain is not supported)

HTTPS Only

Always use HTTPS for redirect URIs in production

State is Required

The state parameter is mandatory. Requests without it are rejected. Generate a unique random value per request and validate it on callback to prevent CSRF attacks.

Secure Storage

Store tokens securely, never in localStorage for sensitive apps
When a user is redirected to the authorization endpoint, they are shown a consent screen that displays:
  • Your application name (as registered in the Developer Dashboard)
  • Requested permissions (scopes) with clear descriptions of what data will be shared
The user must explicitly authorize the request before an authorization code is issued. If the user clicks “Cancel”, they are redirected back to your redirect_uri with error=access_denied.

Client Info Endpoint

You can retrieve public information about an OAuth client (useful for pre-flight checks):
curl https://dhmad.tn/api/oauth/client-info?client_id=YOUR_CLIENT_ID
Response:
{
  "name": "Your Application Name",
  "scopes": ["openid", "profile", "email"]
}
This endpoint is public and does not require authentication. It returns only the client name and allowed scopes.

Discovery Endpoint

Get OAuth configuration:
curl https://dhmad.tn/.well-known/openid-configuration

Support

For OAuth integration help, contact support@dhmad.tn
OAuth credentials are automatically generated when you first access your developer dashboard. You can regenerate your client secret at any time.