Code Examples

Last updated: December 2025

This guide provides complete, ready-to-use code examples in JavaScript, C#, and Python. These examples demonstrate the full OAuth2/OIDC flow, user management, and API key usage.

> ⚠️ Important: Your base URL must include /tenant/{tenantId}. Once configured, all endpoints are relative to this base URL:

OAuth Authority: {BASE_URL}/v2.0/Auth

User Management: {BASE_URL}/api/users

Group Management: {BASE_URL}/api/groups

> Example: If your server URL is https://auth.agglestone.com and tenant ID is abc-123, then BASE_URL = https://auth.agglestone.com/tenant/abc-123

Table of Contents

JavaScript/TypeScript Examples

Complete OAuth2/OIDC Flow

Using oidc-client-ts Library

import { UserManager, WebStorageStateStore } from 'oidc-client-ts';

// Configuration
const BASE_URL = 'https://auth.agglestone.com/tenant/{tenantId}';

const config = {
  authority: `${BASE_URL}/v2.0/Auth`,
  client_id: '{tenantId}',
  redirect_uri: 'https://yourapp.com/callback',
  response_type: 'code',
  scope: 'openid profile email',
  post_logout_redirect_uri: 'https://yourapp.com',
  automaticSilentRenew: true,
  userStore: new WebStorageStateStore({ store: window.sessionStorage })
};

const userManager = new UserManager(config);

// Login
export async function login() {
  try {
    await userManager.signinRedirect();
  } catch (error) {
    console.error('Login error:', error);
    throw error;
  }
}

// Handle callback
export async function handleCallback() {
  try {
    const user = await userManager.signinRedirectCallback();
    console.log('User authenticated:', user);
    return user;
  } catch (error) {
    console.error('Callback error:', error);
    throw error;
  }
}

// Get current user
export async function getCurrentUser() {
  try {
    const user = await userManager.getUser();
    return user;
  } catch (error) {
    console.error('Get user error:', error);
    return null;
  }
}

// Logout
export async function logout() {
  try {
    await userManager.signoutRedirect();
  } catch (error) {
    console.error('Logout error:', error);
    throw error;
  }
}

// Make authenticated API call
export async function fetchUsers() {
  const user = await getCurrentUser();
  if (!user || user.expired) {
    await login();
    return;
  }

  const response = await fetch(
    `${BASE_URL}/api/users`,
    {
      headers: {
        'Authorization': `Bearer ${user.access_token}`,
        'Content-Type': 'application/json'
      }
    }
  );

  if (response.status === 401) {
    // Token expired, try to renew
    await userManager.signinSilent();
    return fetchUsers();
  }

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return await response.json();
}

Manual Implementation (Without Library)

// PKCE utilities
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64UrlEncode(array);
}

async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return base64UrlEncode(new Uint8Array(digest));
}

function base64UrlEncode(array) {
  return btoa(String.fromCharCode(...array))
    .replace(/+/g, '-')
    .replace(///g, '_')
    .replace(/=/g, '');
}

function generateState() {
  const array = new Uint8Array(16);
  crypto.getRandomValues(array);
  return base64UrlEncode(array);
}

// Configuration
const tenantId = 'your-tenant-id';
const BASE_URL = `https://auth.agglestone.com/tenant/${tenantId}`;
const redirectUri = 'https://yourapp.com/callback';

// Initiate login
async function initiateLogin() {
  const codeVerifier = generateCodeVerifier();
  const codeChallenge = await generateCodeChallenge(codeVerifier);
  const state = generateState();

  // Store for later
  sessionStorage.setItem('code_verifier', codeVerifier);
  sessionStorage.setItem('state', state);

  // Build authorization URL
  const params = new URLSearchParams({
    response_type: 'code',
    client_id: config.tenantId,
    redirect_uri: config.redirectUri,
    scope: 'openid profile email',
    state: state,
    code_challenge: codeChallenge,
    code_challenge_method: 'S256'
  });

  const authUrl = `${BASE_URL}/v2.0/Auth/authorize?${params}`;
  window.location.href = authUrl;
}

// Handle callback
async function handleCallback() {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  const state = urlParams.get('state');
  const error = urlParams.get('error');

  if (error) {
    throw new Error(`Authorization error: ${error}`);
  }

  // Validate state
  const storedState = sessionStorage.getItem('state');
  if (state !== storedState) {
    throw new Error('State mismatch - possible CSRF attack');
  }

  // Get code verifier
  const codeVerifier = sessionStorage.getItem('code_verifier');

  // Exchange code for tokens
  const tokens = await exchangeCodeForTokens(code, codeVerifier);

  // Store tokens
  sessionStorage.setItem('access_token', tokens.access_token);
  sessionStorage.setItem('refresh_token', tokens.refresh_token);
  sessionStorage.setItem('id_token', tokens.id_token);

  // Clean up
  sessionStorage.removeItem('code_verifier');
  sessionStorage.removeItem('state');

  return tokens;
}

// Exchange code for tokens
async function exchangeCodeForTokens(code, codeVerifier) {
  const formData = new URLSearchParams({
    grant_type: 'authorization_code',
    code: code,
    redirect_uri: config.redirectUri,
    client_id: config.tenantId,
    code_verifier: codeVerifier
  });

  const response = await fetch(
    `${BASE_URL}/v2.0/Auth/token`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: formData
    }
  );

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error_description || 'Token exchange failed');
  }

  return await response.json();
}

// Refresh access token
async function refreshAccessToken() {
  const refreshToken = sessionStorage.getItem('refresh_token');
  if (!refreshToken) {
    throw new Error('No refresh token available');
  }

  const formData = new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: refreshToken,
    client_id: config.tenantId
  });

  const response = await fetch(
    `${BASE_URL}/v2.0/Auth/token`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: formData
    }
  );

  if (!response.ok) {
    // Refresh token expired, need to login again
    sessionStorage.clear();
    initiateLogin();
    return;
  }

  const tokens = await response.json();
  sessionStorage.setItem('access_token', tokens.access_token);
  sessionStorage.setItem('refresh_token', tokens.refresh_token);
  sessionStorage.setItem('id_token', tokens.id_token);

  return tokens;
}

// Make authenticated API call
async function apiCall(endpoint, options = {}) {
  let accessToken = sessionStorage.getItem('access_token');

  // Check if token is expired (simplified - you should decode JWT to check exp)
  // For now, try the request and refresh if needed

  const response = await fetch(
    `${BASE_URL}${endpoint}`,
    {
      ...options,
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    }
  );

  if (response.status === 401) {
    // Token expired, refresh and retry
    await refreshAccessToken();
    accessToken = sessionStorage.getItem('access_token');
    
    const retryResponse = await fetch(
      `${config.baseUrl}/tenant/${config.tenantId}${endpoint}`,
      {
        ...options,
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
          ...options.headers
        }
      }
    );

    if (!retryResponse.ok) {
      throw new Error(`API error: ${retryResponse.status}`);
    }

    return await retryResponse.json();
  }

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return await response.json();
}

// Usage examples
async function getUsers() {
    return apiCall('/api/users?pageNumber=1&pageSize=20');
}

async function createUser(userData) {
  return apiCall('/api/users', {
    method: 'POST',
    body: JSON.stringify(userData)
  });
}

API Key Usage (Server-Side)

// Node.js example
const express = require('express');
const app = express();

const API_KEY = process.env.API_KEY;
const BASE_URL = `https://api.agglestone.com/tenant/${process.env.TENANT_ID}`;

async function apiCallWithKey(endpoint, options = {}) {
  const response = await fetch(
    `${BASE_URL}${endpoint}`,
    {
      ...options,
      headers: {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json',
        ...options.headers
      }
    }
  );

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return await response.json();
}

// Get all users
app.get('/api/users', async (req, res) => {
  try {
    const users = await apiCallWithKey('/api/Users?pageNumber=1&pageSize=20');
    res.json(users);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Create user
app.post('/api/users', async (req, res) => {
  try {
    const newUser = await apiCallWithKey('/api/Users', {
      method: 'POST',
      body: JSON.stringify(req.body)
    });
    res.status(201).json(newUser);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

C# / .NET Examples

Using IdentityModel.OidcClient

using IdentityModel.OidcClient;
using System;
using System.Threading.Tasks;

public class AuthService
{
    private readonly OidcClient _oidcClient;
    private LoginResult _loginResult;

    public AuthService(string baseUrl, string tenantId, string redirectUri)
    {
        var BASE_URL = $"{baseUrl}/tenant/{tenantId}";
        var options = new OidcClientOptions
        {
        Authority = $"{BASE_URL}/v2.0/Auth",
        ClientId = tenantId,
        RedirectUri = redirectUri,
        Scope = "openid profile email",
        ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,
        Browser = new SystemBrowser()
        };

        _oidcClient = new OidcClient(options);
    }

    public async Task<bool> LoginAsync()
    {
        try
        {
            _loginResult = await _oidcClient.LoginAsync();
            return !_loginResult.IsError;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Login error: {ex.Message}");
            return false;
        }
    }

    public async Task<string> GetAccessTokenAsync()
    {
        if (_loginResult == null || _loginResult.IsError)
        {
            throw new InvalidOperationException("Not authenticated");
        }

        // Check if token is expired
        if (_loginResult.AccessTokenExpiration < DateTime.UtcNow.AddMinutes(1))
        {
            // Refresh token
            var refreshResult = await _oidcClient.RefreshTokenAsync(_loginResult.RefreshToken);
            if (refreshResult.IsError)
            {
                throw new Exception($"Token refresh failed: {refreshResult.Error}");
            }
            _loginResult = refreshResult;
        }

        return _loginResult.AccessToken;
    }

    public async Task<HttpResponseMessage> MakeApiCallAsync(string endpoint, HttpMethod method, object body = null)
    {
        var token = await GetAccessTokenAsync();
        var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

        var request = new HttpRequestMessage(method, endpoint);
        
        if (body != null)
        {
            request.Content = new StringContent(
                System.Text.Json.JsonSerializer.Serialize(body),
                System.Text.Encoding.UTF8,
                "application/json"
            );
        }

        var response = await client.SendAsync(request);
        response.EnsureSuccessStatusCode();
        return response;
    }
}

// Usage
var authService = new AuthService(
    "https://auth.agglestone.com",
    "your-tenant-id",
    "https://yourapp.com/callback"
);

if (await authService.LoginAsync())
{
    var response = await authService.MakeApiCallAsync(
        $"{BASE_URL}/api/users",
        HttpMethod.Get
    );
    var users = await response.Content.ReadFromJsonAsync<UserResponse>();
}

Using HttpClient with Manual Implementation

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;

public class OAuth2Client
{
    private readonly string _baseUrl;
    private readonly string _tenantId;
    private readonly string _redirectUri;
    private readonly HttpClient _httpClient;
    private string _accessToken;
    private string _refreshToken;
    private DateTime _tokenExpiry;

    public OAuth2Client(string baseUrl, string tenantId, string redirectUri)
    {
        _baseUrl = $"{baseUrl}/tenant/{tenantId}";
        _tenantId = tenantId;
        _redirectUri = redirectUri;
        _httpClient = new HttpClient();
    }

    public string GetAuthorizationUrl(string state, string codeChallenge)
    {
        var queryParams = HttpUtility.ParseQueryString(string.Empty);
        queryParams.Add("response_type", "code");
        queryParams.Add("client_id", _tenantId);
        queryParams.Add("redirect_uri", _redirectUri);
        queryParams.Add("scope", "openid profile email");
        queryParams.Add("state", state);
        queryParams.Add("code_challenge", codeChallenge);
        queryParams.Add("code_challenge_method", "S256");

        return $"{_baseUrl}/v2.0/Auth/authorize?{queryParams}";
    }

    public async Task<TokenResponse> ExchangeCodeForTokensAsync(string code, string codeVerifier)
    {
        var formData = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "authorization_code"),
            new KeyValuePair<string, string>("code", code),
            new KeyValuePair<string, string>("redirect_uri", _redirectUri),
            new KeyValuePair<string, string>("client_id", _tenantId),
            new KeyValuePair<string, string>("code_verifier", codeVerifier)
        };

        var request = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/v2.0/Auth/token")
        {
            Content = new FormUrlEncodedContent(formData)
        };

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
        _accessToken = tokenResponse.AccessToken;
        _refreshToken = tokenResponse.RefreshToken;
        _tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn);

        return tokenResponse;
    }

    public async Task<TokenResponse> RefreshTokenAsync()
    {
        var formData = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "refresh_token"),
            new KeyValuePair<string, string>("refresh_token", _refreshToken),
            new KeyValuePair<string, string>("client_id", _tenantId)
        };

        var request = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/v2.0/Auth/token")
        {
            Content = new FormUrlEncodedContent(formData)
        };

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
        _accessToken = tokenResponse.AccessToken;
        _refreshToken = tokenResponse.RefreshToken;
        _tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn);

        return tokenResponse;
    }

    public async Task<string> GetAccessTokenAsync()
    {
        if (string.IsNullOrEmpty(_accessToken) || _tokenExpiry < DateTime.UtcNow.AddMinutes(1))
        {
            await RefreshTokenAsync();
        }
        return _accessToken;
    }

    public async Task<T> MakeApiCallAsync<T>(string endpoint, HttpMethod method, object body = null)
    {
        var token = await GetAccessTokenAsync();
        var request = new HttpRequestMessage(method, $"{_baseUrl}{endpoint}");
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

        if (body != null)
        {
            request.Content = new StringContent(
                System.Text.Json.JsonSerializer.Serialize(body),
                Encoding.UTF8,
                "application/json"
            );
        }

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<T>();
    }
}

// Token response model
public class TokenResponse
{
    public string AccessToken { get; set; }
    public string TokenType { get; set; }
    public int ExpiresIn { get; set; }
    public string RefreshToken { get; set; }
    public string IdToken { get; set; }
    public string Scope { get; set; }
}

API Key Usage

using System.Net.Http;
using System.Threading.Tasks;

public class ApiKeyClient
{
    private readonly string _baseUrl;
    private readonly string _tenantId;
    private readonly string _apiKey;
    private readonly HttpClient _httpClient;

    public ApiKeyClient(string baseUrl, string tenantId, string apiKey)
    {
        _baseUrl = $"{baseUrl}/tenant/{tenantId}";
        _tenantId = tenantId;
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("X-API-Key", _apiKey);
    }

    public async Task<T> GetAsync<T>(string endpoint)
    {
        var response = await _httpClient.GetAsync($"{_baseUrl}{endpoint}");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>();
    }

    public async Task<T> PostAsync<T>(string endpoint, object body)
    {
        var content = new StringContent(
            System.Text.Json.JsonSerializer.Serialize(body),
            System.Text.Encoding.UTF8,
            "application/json"
        );

        var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>();
    }
}

// Usage
var client = new ApiKeyClient(
    "https://auth.agglestone.com",
    "your-tenant-id",
    "your-api-key"
);

var users = await client.GetAsync<PagedResponse<UserResponse>>("/api/Users?pageNumber=1&pageSize=20");

Python Examples

Using authlib Library

from authlib.integrations.requests_client import OAuth2Session
from authlib.oauth2.rfc7636 import CodeChallenge
import requests
import secrets
import hashlib
import base64

class OAuth2Client:
    def __init__(self, base_url, tenant_id, redirect_uri):
        self.base_url = f"{base_url}/tenant/{tenant_id}"
        self.tenant_id = tenant_id
        self.redirect_uri = redirect_uri
        self.authorization_endpoint = f"{self.base_url}/v2.0/Auth/authorize"
        self.token_endpoint = f"{self.base_url}/v2.0/Auth/token"
        self.access_token = None
        self.refresh_token = None
        
    def generate_code_verifier(self):
        """Generate PKCE code verifier"""
        return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
    
    def generate_code_challenge(self, verifier):
        """Generate PKCE code challenge"""
        challenge = hashlib.sha256(verifier.encode('utf-8')).digest()
        return base64.urlsafe_b64encode(challenge).decode('utf-8').rstrip('=')
    
    def get_authorization_url(self):
        """Get authorization URL with PKCE"""
        code_verifier = self.generate_code_verifier()
        code_challenge = self.generate_code_challenge(code_verifier)
        state = secrets.token_urlsafe(16)
        
        # Store for later
        self.code_verifier = code_verifier
        self.state = state
        
        params = {
            'response_type': 'code',
            'client_id': self.tenant_id,
            'redirect_uri': self.redirect_uri,
            'scope': 'openid profile email',
            'state': state,
            'code_challenge': code_challenge,
            'code_challenge_method': 'S256'
        }
        
        query_string = '&'.join([f"{k}={v}" for k, v in params.items()])
        return f"{self.authorization_endpoint}?{query_string}"
    
    def exchange_code_for_tokens(self, code):
        """Exchange authorization code for tokens"""
        data = {
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': self.redirect_uri,
            'client_id': self.tenant_id,
            'code_verifier': self.code_verifier
        }
        
        response = requests.post(self.token_endpoint, data=data)
        response.raise_for_status()
        
        tokens = response.json()
        self.access_token = tokens['access_token']
        self.refresh_token = tokens['refresh_token']
        
        return tokens
    
    def refresh_access_token(self):
        """Refresh access token"""
        data = {
            'grant_type': 'refresh_token',
            'refresh_token': self.refresh_token,
            'client_id': self.tenant_id
        }
        
        response = requests.post(self.token_endpoint, data=data)
        response.raise_for_status()
        
        tokens = response.json()
        self.access_token = tokens['access_token']
        self.refresh_token = tokens['refresh_token']
        
        return tokens
    
    def make_api_call(self, endpoint, method='GET', data=None):
        """Make authenticated API call"""
        if not self.access_token:
            raise Exception("Not authenticated")
        
        url = f"{self.base_url}{endpoint}"
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        response = requests.request(method, url, headers=headers, json=data)
        
        if response.status_code == 401:
            # Token expired, refresh and retry
            self.refresh_access_token()
            headers['Authorization'] = f'Bearer {self.access_token}'
            response = requests.request(method, url, headers=headers, json=data)
        
        response.raise_for_status()
        return response.json()

# Usage
client = OAuth2Client(
    "https://auth.agglestone.com",
    "your-tenant-id",
    "https://yourapp.com/callback"
)

# Get authorization URL
auth_url = client.get_authorization_url()
print(f"Visit: {auth_url}")

# After user authenticates, handle callback
code = input("Enter authorization code: ")
tokens = client.exchange_code_for_tokens(code)

# Make API calls
users = client.make_api_call('/api/users?pageNumber=1&pageSize=20')
print(users)

Using requests-oauthlib

from requests_oauthlib import OAuth2Session
import secrets

class OAuth2Client:
    def __init__(self, base_url, tenant_id, redirect_uri):
        self.base_url = f"{base_url}/tenant/{tenant_id}"
        self.tenant_id = tenant_id
        self.redirect_uri = redirect_uri
        self.authorization_base_url = f"{self.base_url}/v2.0/Auth/authorize"
        self.token_url = f"{self.base_url}/v2.0/Auth/token"
        
    def get_authorization_url(self):
        """Get authorization URL"""
        oauth = OAuth2Session(
            client_id=self.tenant_id,
            redirect_uri=self.redirect_uri,
            scope='openid profile email'
        )
        
        authorization_url, state = oauth.authorization_url(
            self.authorization_base_url,
            state=secrets.token_urlsafe(16)
        )
        
        self.state = state
        return authorization_url
    
    def fetch_token(self, authorization_response):
        """Fetch token from authorization response"""
        oauth = OAuth2Session(
            client_id=self.tenant_id,
            redirect_uri=self.redirect_uri
        )
        
        token = oauth.fetch_token(
            self.token_url,
            authorization_response=authorization_response
        )
        
        self.oauth = oauth
        return token
    
    def get(self, endpoint):
        """Make GET request"""
        url = f"{self.base_url}{endpoint}"
        return self.oauth.get(url).json()
    
    def post(self, endpoint, data=None):
        """Make POST request"""
        url = f"{self.base_url}{endpoint}"
        return self.oauth.post(url, json=data).json()

# Usage
client = OAuth2Client(
    "https://auth.agglestone.com",
    "your-tenant-id",
    "https://yourapp.com/callback"
)

auth_url = client.get_authorization_url()
print(f"Visit: {auth_url}")

# After redirect
authorization_response = input("Enter full redirect URL: ")
token = client.fetch_token(authorization_response)

# Make API calls
users = client.get('/api/Users?pageNumber=1&pageSize=20')

API Key Usage

import requests
import os

class ApiKeyClient:
    def __init__(self, base_url, tenant_id, api_key):
        self.base_url = base_url
        self.tenant_id = tenant_id
        self.api_key = api_key
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }
    
    def get(self, endpoint):
        """Make GET request"""
        url = f"{self.base_url}{endpoint}"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def post(self, endpoint, data=None):
        """Make POST request"""
        url = f"{self.base_url}{endpoint}"
        response = requests.post(url, headers=self.headers, json=data)
        response.raise_for_status()
        return response.json()
    
    def put(self, endpoint, data=None):
        """Make PUT request"""
        url = f"{self.base_url}{endpoint}"
        response = requests.put(url, headers=self.headers, json=data)
        response.raise_for_status()
        return response.json()
    
    def delete(self, endpoint):
        """Make DELETE request"""
        url = f"{self.base_url}{endpoint}"
        response = requests.delete(url, headers=self.headers)
        response.raise_for_status()
        return response.status_code == 204

# Usage
client = ApiKeyClient(
    "https://auth.agglestone.com",
    "your-tenant-id",
    os.getenv('API_KEY')
)

# Get users
users = client.get('/api/Users?pageNumber=1&pageSize=20')

# Create user
new_user = client.post('/api/users', {
    'email': 'newuser@example.com',
    'displayName': 'New User',
    'userType': 'Standard',
    'enabled': True,
    'password': 'SecurePassword123!'
})

Complete Workflow Examples

JavaScript: Full User Management Workflow

class UserManagementClient {
  constructor(baseUrl, tenantId, getAccessToken) {
    this.baseUrl = baseUrl;
    this.tenantId = tenantId;
    this.getAccessToken = getAccessToken;
  }

  async getUsers(pageNumber = 1, pageSize = 20) {
    const token = await this.getAccessToken();
    const response = await fetch(
      `${this.baseUrl}/api/users?pageNumber=${pageNumber}&pageSize=${pageSize}`,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      }
    );
    return response.json();
  }

  async createUser(userData) {
    const token = await this.getAccessToken();
    const response = await fetch(
      `${this.baseUrl}/api/users`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(userData)
      }
    );
    return response.json();
  }

  async updateUser(userId, updates) {
    const token = await this.getAccessToken();
    const response = await fetch(
      `${this.baseUrl}/api/users/${userId}`,
      {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(updates)
      }
    );
    return response.json();
  }

  async deleteUser(userId) {
    const token = await this.getAccessToken();
    const response = await fetch(
      `${this.baseUrl}/api/users/${userId}`,
      {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${token}`
        }
      }
    );
    return response.status === 204;
  }
}

JavaScript: User Management with API Key (Server-Side)

// Node.js server-side example
class UserManagementClient {
  constructor(baseUrl, tenantId, apiKey) {
    this.baseUrl = `${baseUrl}/tenant/${tenantId}`;
    this.tenantId = tenantId;
    this.apiKey = apiKey;
  }

  async getUsers(pageNumber = 1, pageSize = 20) {
    const response = await fetch(
      `${this.baseUrl}/api/users?pageNumber=${pageNumber}&pageSize=${pageSize}`,
      {
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        }
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async createUser(userData) {
    const response = await fetch(
      `${this.baseUrl}/api/users`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(userData)
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async updateUser(userId, updates) {
    const response = await fetch(
      `${this.baseUrl}/api/users/${userId}`,
      {
        method: 'PUT',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(updates)
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async deleteUser(userId) {
    const response = await fetch(
      `${this.baseUrl}/api/users/${userId}`,
      {
        method: 'DELETE',
        headers: {
          'X-API-Key': this.apiKey
        }
      }
    );
    return response.status === 204;
  }
}

// Usage
const client = new UserManagementClient(
  'https://api.agglestone.com',
  process.env.TENANT_ID,
  process.env.API_KEY
);

const users = await client.getUsers(1, 20);
const newUser = await client.createUser({
  email: 'newuser@example.com',
  displayName: 'New User',
  userType: 'Standard',
  enabled: true
});

C#: User Management with API Key (Server-Side)

using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class UserManagementClient
{
    private readonly string _baseUrl;
    private readonly string _tenantId;
    private readonly string _apiKey;
    private readonly HttpClient _httpClient;

    public UserManagementClient(string baseUrl, string tenantId, string apiKey)
    {
        _baseUrl = baseUrl;
        _tenantId = tenantId;
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("X-API-Key", _apiKey);
    }

    public async Task<PagedResponse<UserResponse>> GetUsersAsync(int pageNumber = 1, int pageSize = 20)
    {
        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/users?pageNumber={pageNumber}&pageSize={pageSize}"
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<PagedResponse<UserResponse>>();
    }

    public async Task<UserIdResponse> CreateUserAsync(CreateUserRequest userData)
    {
        var content = new StringContent(
            JsonSerializer.Serialize(userData),
            Encoding.UTF8,
            "application/json"
        );

        var response = await _httpClient.PostAsync(
            $"{_baseUrl}/api/users",
            content
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<UserIdResponse>();
    }

    public async Task<UserIdResponse> UpdateUserAsync(string userId, UpdateUserRequest updates)
    {
        var content = new StringContent(
            JsonSerializer.Serialize(updates),
            Encoding.UTF8,
            "application/json"
        );

        var response = await _httpClient.PutAsync(
            $"{_baseUrl}/api/users/{userId}",
            content
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<UserIdResponse>();
    }

    public async Task<bool> DeleteUserAsync(string userId)
    {
        var response = await _httpClient.DeleteAsync(
            $"{_baseUrl}/tenant/{_tenantId}/api/Users/{userId}"
        );
        return response.StatusCode == System.Net.HttpStatusCode.NoContent;
    }
}

// Usage
var client = new UserManagementClient(
    "https://auth.agglestone.com",
    Environment.GetEnvironmentVariable("TENANT_ID"),
    Environment.GetEnvironmentVariable("API_KEY")
);

var users = await client.GetUsersAsync(1, 20);
var newUser = await client.CreateUserAsync(new CreateUserRequest
{
    Email = "newuser@example.com",
    DisplayName = "New User",
    UserType = "Standard",
    Enabled = true
});

Python: User Management with API Key (Server-Side)

import requests
import os
from typing import Dict, List, Optional

class UserManagementClient:
    def __init__(self, base_url: str, tenant_id: str, api_key: str):
        self.base_url = base_url
        self.tenant_id = tenant_id
        self.api_key = api_key
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }
    
    def get_users(self, page_number: int = 1, page_size: int = 20) -> Dict:
        """Get paginated list of users"""
        url = f"{self.base_url}/api/users"
        params = {'pageNumber': page_number, 'pageSize': page_size}
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        return response.json()
    
    def get_user(self, user_id: str) -> Dict:
        """Get user by ID"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Users/{user_id}"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def create_user(self, user_data: Dict) -> Dict:
        """Create a new user"""
        url = f"{self.base_url}/api/users"
        response = requests.post(url, headers=self.headers, json=user_data)
        response.raise_for_status()
        return response.json()
    
    def update_user(self, user_id: str, updates: Dict) -> Dict:
        """Update an existing user"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Users/{user_id}"
        response = requests.put(url, headers=self.headers, json=updates)
        response.raise_for_status()
        return response.json()
    
    def delete_user(self, user_id: str) -> bool:
        """Delete a user"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Users/{user_id}"
        response = requests.delete(url, headers=self.headers)
        response.raise_for_status()
        return response.status_code == 204

# Usage
client = UserManagementClient(
    "https://auth.agglestone.com",
    os.getenv('TENANT_ID'),
    os.getenv('API_KEY')
)

# Get users
users = client.get_users(page_number=1, page_size=20)
print(f"Total users: {users['totalRecords']}")

# Create user
new_user = client.create_user({
    'email': 'newuser@example.com',
    'displayName': 'New User',
    'userType': 'Standard',
    'enabled': True,
    'password': 'SecurePassword123!'
})
print(f"Created user: {new_user['userId']}")

# Update user
updated = client.update_user(new_user['userId'], {
    'displayName': 'Updated Name'
})

# Delete user
client.delete_user(new_user['userId'])

JavaScript: Group Management with API Key (Server-Side)

// Node.js server-side example
class GroupManagementClient {
  constructor(baseUrl, tenantId, apiKey) {
    this.baseUrl = `${baseUrl}/tenant/${tenantId}`;
    this.tenantId = tenantId;
    this.apiKey = apiKey;
  }

  async getGroups(pageNumber = 1, pageSize = 20) {
    const response = await fetch(
      `${this.baseUrl}/api/groups?pageNumber=${pageNumber}&pageSize=${pageSize}`,
      {
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        }
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async getGroup(groupId) {
    const response = await fetch(
      `${this.baseUrl}/api/groups/${groupId}`,
      {
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        }
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async createGroup(groupData) {
    const response = await fetch(
      `${this.baseUrl}/api/groups`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(groupData)
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async updateGroup(groupId, updates) {
    const response = await fetch(
      `${this.baseUrl}/api/groups/${groupId}`,
      {
        method: 'PUT',
        headers: {
          'X-API-Key': this.apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(updates)
      }
    );
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return response.json();
  }

  async deleteGroup(groupId) {
    const response = await fetch(
      `${this.baseUrl}/api/groups/${groupId}`,
      {
        method: 'DELETE',
        headers: {
          'X-API-Key': this.apiKey
        }
      }
    );
    return response.status === 204;
  }
}

// Usage
const groupClient = new GroupManagementClient(
  'https://api.agglestone.com',
  process.env.TENANT_ID,
  process.env.API_KEY
);

const groups = await groupClient.getGroups(1, 20);
const newGroup = await groupClient.createGroup({
  groupName: 'Developers',
  description: 'Development team members'
});

C#: Group Management with API Key (Server-Side)

using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class GroupManagementClient
{
    private readonly string _baseUrl;
    private readonly string _tenantId;
    private readonly string _apiKey;
    private readonly HttpClient _httpClient;

    public GroupManagementClient(string baseUrl, string tenantId, string apiKey)
    {
        _baseUrl = baseUrl;
        _tenantId = tenantId;
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("X-API-Key", _apiKey);
    }

    public async Task<PagedResponse<GroupResponse>> GetGroupsAsync(int pageNumber = 1, int pageSize = 20)
    {
        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/groups?pageNumber={pageNumber}&pageSize={pageSize}"
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<PagedResponse<GroupResponse>>();
    }

    public async Task<GroupResponse> GetGroupAsync(string groupId)
    {
        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/groups/{groupId}"
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<GroupResponse>();
    }

    public async Task<GroupIdResponse> CreateGroupAsync(CreateGroupRequest groupData)
    {
        var content = new StringContent(
            JsonSerializer.Serialize(groupData),
            Encoding.UTF8,
            "application/json"
        );

        var response = await _httpClient.PostAsync(
            $"{_baseUrl}/api/groups",
            content
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<GroupIdResponse>();
    }

    public async Task<GroupIdResponse> UpdateGroupAsync(string groupId, UpdateGroupRequest updates)
    {
        var content = new StringContent(
            JsonSerializer.Serialize(updates),
            Encoding.UTF8,
            "application/json"
        );

        var response = await _httpClient.PutAsync(
            $"{_baseUrl}/api/groups/{groupId}",
            content
        );
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<GroupIdResponse>();
    }

    public async Task<bool> DeleteGroupAsync(string groupId)
    {
        var response = await _httpClient.DeleteAsync(
            $"{_baseUrl}/api/groups/{groupId}"
        );
        return response.StatusCode == System.Net.HttpStatusCode.NoContent;
    }
}

// Usage
var groupClient = new GroupManagementClient(
    "https://auth.agglestone.com",
    Environment.GetEnvironmentVariable("TENANT_ID"),
    Environment.GetEnvironmentVariable("API_KEY")
);

var groups = await groupClient.GetGroupsAsync(1, 20);
var newGroup = await groupClient.CreateGroupAsync(new CreateGroupRequest
{
    GroupName = "Developers",
    Description = "Development team members"
});

Python: Group Management with API Key (Server-Side)

import requests
import os
from typing import Dict, Optional

class GroupManagementClient:
    def __init__(self, base_url: str, tenant_id: str, api_key: str):
        self.base_url = base_url
        self.tenant_id = tenant_id
        self.api_key = api_key
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }
    
    def get_groups(self, page_number: int = 1, page_size: int = 20) -> Dict:
        """Get paginated list of groups"""
        url = f"{self.base_url}/api/groups"
        params = {'pageNumber': page_number, 'pageSize': page_size}
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        return response.json()
    
    def get_group(self, group_id: str) -> Dict:
        """Get group by ID"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Groups/{group_id}"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def create_group(self, group_data: Dict) -> Dict:
        """Create a new group"""
        url = f"{self.base_url}/api/groups"
        response = requests.post(url, headers=self.headers, json=group_data)
        response.raise_for_status()
        return response.json()
    
    def update_group(self, group_id: str, updates: Dict) -> Dict:
        """Update an existing group"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Groups/{group_id}"
        response = requests.put(url, headers=self.headers, json=updates)
        response.raise_for_status()
        return response.json()
    
    def delete_group(self, group_id: str) -> bool:
        """Delete a group"""
        url = f"{self.base_url}/tenant/{self.tenant_id}/api/Groups/{group_id}"
        response = requests.delete(url, headers=self.headers)
        response.raise_for_status()
        return response.status_code == 204

# Usage
group_client = GroupManagementClient(
    "https://auth.agglestone.com",
    os.getenv('TENANT_ID'),
    os.getenv('API_KEY')
)

# Get groups
groups = group_client.get_groups(page_number=1, page_size=20)
print(f"Total groups: {groups['totalRecords']}")

# Create group
new_group = group_client.create_group({
    'groupName': 'Developers',
    'description': 'Development team members'
})
print(f"Created group: {new_group['id']}")

# Update group
updated = group_client.update_group(new_group['id'], {
    'description': 'Updated description'
})

# Delete group
group_client.delete_group(new_group['id'])

Next Steps