The Aton Health API uses OAuth 2.0 with the client credentials grant type for server-to-server authentication. This method is ideal for backend applications and services.

Overview

The client credentials flow allows your application to authenticate directly with the Microsoft identity platform using your application's credentials (client ID and client secret).

sequenceDiagram
    participant App as Your Application
    participant Azure as Microsoft Identity Platform
    participant API as Aton Health API

    App->>Azure: POST /oauth2/v2.0/token (credentials)
    Azure-->>App: Access Token (expires in 1 hour)
    App->>API: API Request + Bearer Token
    API-->>App: API Response

Getting an Access Token

Endpoint

HTTP
POST https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token

Required Parameters

Send as application/x-www-form-urlencoded:

Parameter Value Description
grant_type client_credentials OAuth 2.0 grant type
client_id Provided by Aton Health Your application's client ID
client_secret Provided by Aton Health Your application's client secret
scope Provided by Aton Health API resource and permissions

Request Example

Bash
curl -X POST "https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id={your_client_id}" \
  -d "client_secret={your_client_secret}" \
  -d "scope={api_scope}"

Response Format

JSON
{
  "token_type": "Bearer",
  "expires_in": 3599,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik..."
}

Response Fields

Field Description
token_type Always "Bearer" for this grant type
expires_in Token lifetime in seconds (typically 3599 = ~1 hour)
access_token JWT token to use for API requests

Using the Access Token

Include the access token in the Authorization header of all API requests:

Bash
curl -X GET "https://api.aton.health/api/v1/referrals" \
  -H "Authorization: Bearer {access_token}" 

Code Examples

Python

Python
import os
import requests
from datetime import datetime, timedelta

class AtonHealthAuth:
    """
    Schema for Aton Health Patient Referrals API authentication data.
    """
    def __init__(self):
        self.client_id = os.getenv('ATON_CLIENT_ID')
        self.client_secret = os.getenv('ATON_CLIENT_SECRET')
        self.scope = os.getenv('ATON_API_SCOPE')
        self.access_token = None
        self.expires_at = None

    def get_access_token(self):
        """
        Get a new access token or return a cached valid token.
        """
        if self.access_token and self.expires_at > datetime.now():
            return self.access_token

        url = f"https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token"

        data = {
            'grant_type': 'client_credentials',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'scope': self.scope
        }

        response = requests.post(url, data=data)
        response.raise_for_status()

        token_data = response.json()
        self.access_token = token_data['access_token']
        expires_in = token_data['expires_in'] - 60  # 1-minute buffer before actual expiration
        self.expires_at = datetime.now() + timedelta(seconds=expires_in)

        return self.access_token

    def make_authenticated_request(self, method, url, **kwargs):
        """
        Make an authenticated request to the Aton Health Patient Referrals API.
        """
        token = self.get_access_token()
        headers = kwargs.get('headers', {})
        headers['Authorization'] = f'Bearer {token}'
        kwargs['headers'] = headers

        return requests.request(method, url, **kwargs)

# Usage example
auth = AtonHealthAuth()
response = auth.make_authenticated_request('GET', 'https://api.aton.health/health/ping')
pong = response.json()

Node.js

JavaScript
const axios = require('axios');

class AtonHealthAuth {
  constructor() {
    this.clientId = process.env.ATON_CLIENT_ID;
    this.clientSecret = process.env.ATON_CLIENT_SECRET;
    this.scope = process.env.ATON_API_SCOPE;
    this.accessToken = null;
    this.expiresAt = null;
  }

  async getAccessToken() {
    if (this.accessToken && this.expiresAt > Date.now()) {
      return this.accessToken;
    }

    const url = `https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token`;

    const data = new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: this.clientId,
      client_secret: this.clientSecret,
      scope: this.scope
    });

    try {
      const response = await axios.post(url, data, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
      });

      this.accessToken = response.data.access_token;
      this.expiresAt = Date.now() + (response.data.expires_in - 60) * 1000; // 1-minute buffer before actual expiration

      return this.accessToken;
    } catch (error) {
      throw new Error(`Failed to get access token: ${error.message}`);
    }
  }

  async makeAuthenticatedRequest(method, url, config = {}) {
    const token = await this.getAccessToken();

    const headers = {
      ...config.headers,
      'Authorization': `Bearer ${token}`
    };

    return axios({ method, url, ...config, headers });
  }
}

// Usage example
const auth = new AtonHealthAuth();
auth.makeAuthenticatedRequest('GET', 'https://api.aton.health/health/ping')
  .then(response => console.log(response.data))
  .catch(error => console.error('Error:', error.message));

.NET

C#
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class AtonHealthAuth
{
    private readonly HttpClient _httpClient;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly string _scope;
    private string _accessToken;
    private DateTime _expiresAt;

    public AtonHealthAuth()
    {
        _httpClient = new HttpClient();
        _clientId = Environment.GetEnvironmentVariable("ATON_CLIENT_ID");
        _clientSecret = Environment.GetEnvironmentVariable("ATON_CLIENT_SECRET");
        _scope = Environment.GetEnvironmentVariable("ATON_API_SCOPE");
    }

    public async Task<string> GetAccessTokenAsync()
    {
        if (!string.IsNullOrEmpty(_accessToken) && _expiresAt > DateTime.UtcNow)
        {
            return _accessToken;
        }

        var url = $"https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token";

        var parameters = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", _clientId),
            new KeyValuePair<string, string>("client_secret", _clientSecret),
            new KeyValuePair<string, string>("scope", _scope)
        };

        var content = new FormUrlEncodedContent(parameters);

        var response = await _httpClient.PostAsync(url, content);
        response.EnsureSuccessStatusCode();

        var jsonResponse = await response.Content.ReadAsStringAsync();
        var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(jsonResponse);

        _accessToken = tokenResponse.AccessToken;
        _expiresAt = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn - 60); // 1 minute buffer before actual expiration

        return _accessToken;
    }

    public async Task<HttpResponseMessage> MakeAuthenticatedRequestAsync(HttpMethod method, string url, HttpContent content = null)
    {
        var token = await GetAccessTokenAsync();

        var request = new HttpRequestMessage(method, url);
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

        if (content != null)
        {
            request.Content = content;
        }

        return await _httpClient.SendAsync(request);
    }

    private class TokenResponse
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("expires_in")]
        public int ExpiresIn { get; set; }

        [JsonProperty("token_type")]
        public string TokenType { get; set; }
    }
}

Environment Variables Setup

Store your credentials securely using environment variables or a key management solution:

Development (.env file)

Bash
ATON_CLIENT_ID=your-client-id
ATON_CLIENT_SECRET=your-client-secret
ATON_API_SCOPE=your-api-scope

Production

Set these variables in your production environment (Azure App Service, AWS Lambda, etc.)


Error Handling

Common Errors

Error Status Code Description Solution
invalid_client 401 Invalid client ID or secret Verify credentials with Aton Health
invalid_scope 400 Invalid or unauthorized scope Check scope value provided by Aton Health
invalid_grant 400 Invalid grant_type parameter Ensure grant_type is 'client_credentials'

Error Response Format

JSON
{
  "error": "invalid_client",
  "error_description": "AADSTS70002: Error validating credentials...",
  "error_codes": [70002],
  "timestamp": "2024-10-15 12:34:56Z",
  "trace_id": "abcd1234-...",
  "correlation_id": "efgh5678-..."
}

Testing Your Implementation

1. Validate Token Acquisition

Bash
# Test with curl
curl -X POST "https://login.microsoftonline.com/5178eeaf-1f56-44cb-940e-012fe79c2845/oauth2/v2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&scope={scope}"

2. Test API Access

Bash
# Use the acquired token
curl -X GET "https://api.aton.health/health/ping" \
  -H "Authorization: Bearer {access_token}"

Security Considerations

Security Best Practices - Never log or expose client secrets in code or logs - Use secure storage for credentials (Azure Key Vault, AWS Secrets Manager) - Implement token caching to avoid unnecessary token requests - Handle token refresh gracefully in your application - Monitor for authentication failures and implement alerting


Troubleshooting

Token Expired (401 Unauthorized)

  • Cause: Access token has expired (lifetime ~1 hour)
  • Solution: Request a new access token and retry the request

Invalid Credentials (401 Unauthorized)

  • Cause: Incorrect authentication url, Client ID, Client Secret, or Scope
  • Solution: Verify credentials with the Aton Health team

Forbidden (403 Forbidden)

  • Cause: Valid token but insufficient permissions for the requested resource
  • Solution: Contact Aton Health to review your application's scope and permissions

Need Help?

Contact our integration team at integration-support@atonhealth.com