Skip to content
Last updated: 2026-04-06
Reference

Authentication

Dxtra uses JWT-based authentication for secure API access. This guide covers personal access tokens and the JWT token exchange flow.

Personal Access Tokens

Personal Access Tokens (PATs) provide programmatic access to the GraphQL API. These are refresh tokens that can be exchanged for short-lived JWT access tokens.

What is a Personal Access Token?

Personal Access Tokens are long-lived refresh tokens. They authenticate your application and can be exchanged for JWT tokens to make API calls.

Generating a Personal Access Token

One-Time Display

Personal Access Tokens are shown only once during generation. Store your token securely immediately.

In the Dxtra Dashboard, navigate to Developers in the top bar. This page displays your existing tokens and allows you to create new ones.

Click "Create API Key" and provide a descriptive name (e.g., "Production API", "CI/CD Pipeline"). The system automatically sets an expiration date based on the token lifecycle policy.

Copy the token immediately after creation.

Security Critical

  • Copy immediately -- The token is only displayed once
  • Store securely -- Use environment variables or secrets management
  • Never commit to source control -- Add to .gitignore

Using Your Personal Access Token

Personal Access Tokens must be exchanged for JWT tokens before making API calls.

sequenceDiagram
    participant App as Your Application
    participant Auth as auth.dxtra.ai
    participant API as api.dxtra.ai

    App->>Auth: POST /v1/signin/pat
    Note over App,Auth: {"personalAccessToken": "PAT"}
    Auth-->>App: JWT + Refresh Token
    Note over Auth,App: {"session": {"accessToken": "JWT...", "refreshToken": "..."}}

    App->>API: GraphQL Query + JWT
    Note over App,API: Authorization: Bearer JWT<br/>X-Hasura-Role: user
    API-->>App: GraphQL Response

    alt Token Expired (401)
        API-->>App: 401 Unauthorized
        App->>Auth: POST /v1/signin/pat
        Auth-->>App: New JWT Token
        App->>API: Retry with New JWT
        API-->>App: Successful Response
    end

API Endpoints:

Environment Authentication GraphQL API
Production https://auth.dxtra.ai https://api.dxtra.ai
Bash
curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"personalAccessToken": "YOUR_PAT"}' \
  https://auth.dxtra.ai/v1/signin/pat

Response:

JSON
{
  "session": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "accessTokenExpiresIn": 3600,
    "refreshToken": "abc123...",
    "user": {
      "id": "user-uuid",
      "email": "user@example.com",
      "defaultRole": "dataSubject"
    }
  }
}

  • accessToken -- JWT access token, valid for 1 hour
  • accessTokenExpiresIn -- Token lifetime in seconds (3600 = 1 hour)
  • refreshToken -- Use to get new access tokens, valid for 12 hours
Bash
curl \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "X-Hasura-Role: user" \
  --data '{"query": "query { dataControllers { id title } }"}' \
  https://api.dxtra.ai/v1/graphql

Available Roles

Role Access Level Description
anonymous Public Access for public transparency widgets, consent tracking, and event tracking
dataSubject Default Access for privacy transparency center users
me User-scoped Access scoped to the authenticated user's own data
user Standard Access to data controllers and management features

Default role for new users: dataSubject

JWT Authentication (Client Applications)

For web applications and the Dxtra Dashboard, authentication uses JWT tokens.

JavaScript
const authenticateWithPAT = async (personalAccessToken) => {
  const response = await fetch('https://auth.dxtra.ai/v1/signin/pat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ personalAccessToken })
  });

  if (!response.ok) {
    throw new Error(`Authentication failed: ${response.statusText}`);
  }

  const data = await response.json();
  return {
    accessToken: data.session.accessToken,
    refreshToken: data.session.refreshToken
  };
};
Python
import requests

def authenticate_with_pat(personal_access_token: str) -> dict:
    response = requests.post(
        'https://auth.dxtra.ai/v1/signin/pat',
        json={'personalAccessToken': personal_access_token}
    )
    response.raise_for_status()
    session = response.json()['session']
    return {
        'access_token': session['accessToken'],
        'refresh_token': session['refreshToken']
    }
JavaScript
const makeGraphQLCall = async (jwtToken, query, role = 'user') => {
  const response = await fetch('https://api.dxtra.ai/v1/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${jwtToken}`,
      'X-Hasura-Role': role
    },
    body: JSON.stringify({ query })
  });

  const result = await response.json();

  if (result.errors) {
    throw new Error(`GraphQL Error: ${result.errors[0].message}`);
  }

  return result.data;
};
  1. Production GraphQL endpoint (Hasura)
  2. JWT Bearer token authentication
  3. Hasura role for permission checks
  4. Standard GraphQL query format
  5. Check for GraphQL errors in response
  6. Return data object
React Application with Nhost
import { NhostProvider, useAuthenticated } from '@nhost/react';
import { NhostClient } from '@nhost/nhost-js';

// Initialize Nhost client with custom domain
const nhost = new NhostClient({
  authUrl: 'https://auth.dxtra.ai',
  graphqlUrl: 'https://api.dxtra.ai/v1/graphql',
  storageUrl: 'https://storage.dxtra.ai',
});

function App() {
  return (
    <NhostProvider nhost={nhost}>
      <AuthenticatedApp />
    </NhostProvider>
  );
}

function AuthenticatedApp() {
  const isAuthenticated = useAuthenticated();

  if (!isAuthenticated) {
    return <LoginPage />;
  }

  return <Dashboard />;
}

Token Management

JWT Token Lifecycle

Token Type Duration Purpose
Access Token (JWT) 1 hour API calls and GraphQL queries
Refresh Token 12 hours Obtain new access tokens
Personal Access Token Variable Initial authentication (managed in Dashboard)

When a token expires, you receive a 401 Unauthorized response. Re-authenticate with your Personal Access Token to get a new JWT.

Token Refresh Example

JavaScript
const makeAuthenticatedRequest = async (personalAccessToken, query) => {
  let { accessToken } = await authenticateWithPAT(personalAccessToken);

  try {
    return await makeGraphQLCall(accessToken, query);
  } catch (error) {
    if (error.status === 401) {
      const { accessToken: newToken } = await authenticateWithPAT(personalAccessToken);
      return await makeGraphQLCall(newToken, query);
    }
    throw error;
  }
};

Proactive Token Refresh

For long-running applications, implement proactive token refresh:

JavaScript
class AuthManager {
  constructor(personalAccessToken) {
    this.pat = personalAccessToken;
    this.accessToken = null;
    this.refreshAt = null;
  }

  async getAccessToken() {
    const now = Date.now();
    if (!this.accessToken || now >= this.refreshAt) {
      const { accessToken, accessTokenExpiresIn } =
        await authenticateWithPAT(this.pat);

      this.accessToken = accessToken;
      // Refresh 2 minutes before expiration
      this.refreshAt = now + ((accessTokenExpiresIn - 120) * 1000);
    }

    return this.accessToken;
  }
}

Security Best Practices

Critical Security Guidelines

Personal Access Token Management

  • Keep tokens secret -- Never share or commit to version control
  • Use environment variables -- Store in .env files (add to .gitignore)
  • Rotate compromised tokens -- Delete and regenerate immediately if exposed
  • Use separate tokens per environment -- Different tokens for dev/staging/production

Access Control

  • Use appropriate roles -- Grant minimum necessary permissions
  • Validate user roles -- Check X-Hasura-Role matches expected value
  • Implement rate limiting -- Protect against abuse (see Rate Limits)

Token Handling

  • Handle expiration gracefully -- Implement automatic refresh logic
  • Store JWTs securely -- Memory only, never localStorage for sensitive apps
  • Use HTTPS exclusively -- All API calls must use secure transport

Environment Variables

Store sensitive credentials using environment variables:

.env (Never commit this file)
DXTRA_PAT=your_personal_access_token_here
DXTRA_AUTH_URL=https://auth.dxtra.ai
DXTRA_API_URL=https://api.dxtra.ai

Technical Details

Security Features

Feature Status Details
Multi-Factor Authentication Supported TOTP-based MFA
WebAuthn Supported Passwordless hardware key authentication
Email Passwordless Supported Magic link authentication
GitHub OAuth Supported Social login integration
Google OAuth Supported Social login integration
Rate Limiting Enabled See Rate Limits for details
Audit Logging Enabled All authentication events logged

Troubleshooting

Common Issues

401 Unauthorized Error

Cause: JWT token expired or invalid

Solution: Re-authenticate with your Personal Access Token to get a new JWT

Bash
curl -X POST https://auth.dxtra.ai/v1/signin/pat \
  -H "Content-Type: application/json" \
  -d '{"personalAccessToken": "YOUR_PAT"}'
403 Forbidden Error

Cause: JWT valid but role lacks permission for requested operation

Solution: Verify you are using the correct X-Hasura-Role header value

Available roles: anonymous, dataSubject, me, user

Invalid Token Format

Cause: Malformed JWT or missing Bearer prefix

Solution: Ensure Authorization header format: Bearer <token>

Getting Help

If you encounter authentication issues:

  1. Check token expiration -- JWTs expire after 1 hour
  2. Verify endpoints -- Ensure using correct environment URLs
  3. Review role permissions -- Confirm role has required access
  4. Contact support -- Email privacy@dxtra.ai with error details

Next Steps