๐Ÿ  Server Navigation

Quick access to server resources

๐Ÿ  Server Home

๐Ÿ’ป Integration Examples

Complete examples and code snippets for integrating with the Hydrigital OAuth2 Authentication Server.

๐Ÿ”— Integration Steps

1Configure Your Application

Before integrating with the OAuth2 server, you'll need to configure your application with the following settings:

๐Ÿ“‹ Required Configuration Values

  • Client ID: Your application's unique identifier (e.g., your-client-id)
  • Client Secret: Secret key for confidential clients (server-side apps)
  • Redirect URI: Your application's callback URL (e.g., http://localhost:8080/callback)
  • Scopes: Permissions your application needs (e.g., openid profile email)
  • Authorization Server: https://auth.hydrigital.com

๐ŸŽฏ Available Scopes

The following scopes are available for your application:

Scope Description
Personal User Data
profile Access to basic profile information (name, picture, etc.).
email Access to the user's primary email address.
phone Access to the user's phone number.
user Access to user account information.
Account Actions
openid Required for OpenID Connect requests.
offline_access Allow the application to perform actions on your behalf even when you are not online.
General Entities
deya_read Read access to DEYA (organization) information.
deya_manage Full management access to DEYA information.
location_read Read access to location data.
location_manage Full management access to location data.
zone_read Read access to water zone information.
zone_manage Full management access to water zones.
route_read Read access to water route information.
route_manage Full management access to water routes.
supply_read Read access to water supply information.
supply_manage Full management access to water supply.
customer_read Read access to customer information.
customer_manage Full management access to customer data.
customer_export Export customer data.
Urban Water
urbanwater:entity_read Read access to Urban Water entities.
urbanwater:entity_write Write access to Urban Water entities.
urbanwater:metric_read Read access to Urban Water metrics.
urbanwater:metric_write Write access to Urban Water metrics.
urbanwater:datapoint_read Read access to Urban Water data points.
urbanwater:report_read Read access to Urban Water reports.
urbanwater:report_write Write access to Urban Water reports.
urbanwater:report_list List available Urban Water reports.
urbanwater:report_execute Execute Urban Water reports.
WCM
wcm:devices_read Read access to WCM devices.
wcm:devices_encryptionkeys Read WCM Devices encryption keys.
wcm:devices_write Write access to WCM devices.
wcm:consumptions Access to consumption data in WCM.
wcm:alerts_read Read access to WCM alerts.
wcm:alerts_write Write and manage WCM alerts.
WCM Importer
wcmimporter:import Import data using WCM Importer.
wcmimporter:read Read access to WCM Importer data.
Consumption Recorder
consumptionrecorder:record Record consumption data.
consumptionrecorder:read Read consumption records.
System & Administrative
system:admin Administrative access to all systems.
system:audit_read Read access to audit logs.
system:health Access to system health and monitoring data.
system:mass_update Perform mass update operations across systems.
system:notifications_read Read user notifications.
system:notifications_write Send and manage user notifications.

2Authorization Code Flow

The most common OAuth2 flow for web applications. This involves redirecting the user to the authorization server.

๐ŸŸจ JavaScript (SPA with PKCE)

// Using PKCE for security (required for public clients)
import { generateCodeVerifier, generateCodeChallenge } from './pkce-utils.js';

class OAuth2Client {
    constructor(clientId, redirectUri, authServerUrl) {
        this.clientId = clientId;
        this.redirectUri = redirectUri;
        this.authServerUrl = authServerUrl;
    }
    
    async authorize() {
        // Generate PKCE parameters
        const codeVerifier = generateCodeVerifier();
        const codeChallenge = await generateCodeChallenge(codeVerifier);
        
        // Store code verifier for later use
        sessionStorage.setItem('code_verifier', codeVerifier);
        
        // Build authorization URL
        const params = new URLSearchParams({
            response_type: 'code',
            client_id: this.clientId,
            redirect_uri: this.redirectUri,
            scope: 'openid profile email',
            state: Math.random().toString(36).substring(7),
            code_challenge: codeChallenge,
            code_challenge_method: 'S256'
        });
        
        // Redirect to authorization server
        window.location.href = `${this.authServerUrl}/oauth/authorize?${params}`;
    }
    
    async exchangeCodeForToken(code) {
        const codeVerifier = sessionStorage.getItem('code_verifier');
        
        const response = await fetch(`${this.authServerUrl}/oauth/token`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                grant_type: 'authorization_code',
                code: code,
                client_id: this.clientId,
                redirect_uri: this.redirectUri,
                code_verifier: codeVerifier
            })
        });
        
        return await response.json();
    }
}

// Usage
const client = new OAuth2Client(
    'your-client-id',
    'http://localhost:8080/callback',
    'https://auth.hydrigital.com'
);

// Start authorization flow
client.authorize();

๐Ÿ”ต PHP (Server-side)

<?php
class OAuth2Client {
    private $clientId;
    private $clientSecret;
    private $redirectUri;
    private $authServerUrl;
    
    public function __construct($clientId, $clientSecret, $redirectUri, $authServerUrl) {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->redirectUri = $redirectUri;
        $this->authServerUrl = $authServerUrl;
    }
    
    public function getAuthorizationUrl() {
        $state = bin2hex(random_bytes(16));
        $_SESSION['oauth_state'] = $state;
        
        $params = http_build_query([
            'response_type' => 'code',
            'client_id' => $this->clientId,
            'redirect_uri' => $this->redirectUri,
            'scope' => 'openid profile email',
            'state' => $state
        ]);
        
        return $this->authServerUrl . '/oauth/authorize?' . $params;
    }
    
    public function exchangeCodeForToken($code, $state) {
        // Verify state parameter
        if (!isset($_SESSION['oauth_state']) || $_SESSION['oauth_state'] !== $state) {
            throw new Exception('Invalid state parameter');
        }
        
        $data = [
            'grant_type' => 'authorization_code',
            'code' => $code,
            'redirect_uri' => $this->redirectUri,
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->authServerUrl . '/oauth/token');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/x-www-form-urlencoded'
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        return json_decode($response, true);
    }
}

// Usage
session_start();
$client = new OAuth2Client(
    'your-client-id',
    'your-client-secret',
    'http://localhost:8080/callback',
    'https://auth.hydrigital.com'
);

// Redirect to authorization server
header('Location: ' . $client->getAuthorizationUrl());
?>

๐Ÿ Python

import requests
import secrets
import base64
import hashlib
from urllib.parse import urlencode, urlparse, parse_qs

class OAuth2Client:
    def __init__(self, client_id, client_secret, redirect_uri, auth_server_url):
        self.client_id = client_id
        self.client_secret = client_secret
        self.redirect_uri = redirect_uri
        self.auth_server_url = auth_server_url
    
    def generate_pkce_pair(self):
        """Generate PKCE code verifier and challenge"""
        code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
        challenge = base64.urlsafe_b64encode(
            hashlib.sha256(code_verifier.encode('utf-8')).digest()
        ).decode('utf-8').rstrip('=')
        return code_verifier, challenge
    
    def get_authorization_url(self):
        """Build authorization URL"""
        code_verifier, code_challenge = self.generate_pkce_pair()
        state = secrets.token_urlsafe(32)
        
        params = {
            'response_type': 'code',
            'client_id': self.client_id,
            'redirect_uri': self.redirect_uri,
            'scope': 'openid profile email',
            'state': state,
            'code_challenge': code_challenge,
            'code_challenge_method': 'S256'
        }
        
        # Store for later use
        self.code_verifier = code_verifier
        self.state = state
        
        return f"{self.auth_server_url}/oauth/authorize?{urlencode(params)}"
    
    def exchange_code_for_token(self, code):
        """Exchange authorization code for access token"""
        data = {
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': self.redirect_uri,
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code_verifier': self.code_verifier
        }
        
        response = requests.post(
            f"{self.auth_server_url}/oauth/token",
            data=data,
            headers={'Content-Type': 'application/x-www-form-urlencoded'}
        )
        
        return response.json()

# Usage
client = OAuth2Client(
    client_id='your-client-id',
    client_secret='your-client-secret',
    redirect_uri='http://localhost:8080/callback',
    auth_server_url='https://auth.hydrigital.com'
)

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

๐ŸŒ cURL Commands

# Step 1: Get authorization code (manual browser step)
# Visit this URL in your browser:
curl -G "https://auth.hydrigital.com/oauth/authorize" \
  --data-urlencode "response_type=code" \
  --data-urlencode "client_id=your-client-id" \
  --data-urlencode "redirect_uri=http://localhost:8080/callback" \
  --data-urlencode "scope=openid profile email" \
  --data-urlencode "state=random-state-string"

# Step 2: Exchange code for token
curl -X POST "https://auth.hydrigital.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE_FROM_CALLBACK" \
  -d "redirect_uri=http://localhost:8080/callback" \
  -d "client_id=your-client-id" \
  -d "client_secret=your-client-secret"

# Step 3: Use access token to call protected resources
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "https://auth.hydrigital.com/oauth/userinfo"

# Step 4: Refresh token when needed
curl -X POST "https://auth.hydrigital.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=YOUR_REFRESH_TOKEN" \
  -d "client_id=your-client-id" \
  -d "client_secret=your-client-secret"

3Device Authorization Flow (RFC 8628)

For input-constrained devices like Smart TVs, IoT devices, etc. that can't easily handle web-based flows.

๐Ÿ–ฅ๏ธ JavaScript (Device Flow)

// Device Authorization Flow implementation
class DeviceAuthClient {
    constructor(clientId, authServerUrl) {
        this.clientId = clientId;
        this.authServerUrl = authServerUrl;
    }
    
    async requestDeviceCode() {
        const response = await fetch(`${this.authServerUrl}/oauth/deviceauthorization`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                client_id: this.clientId,
                scope: 'read write user'
            })
        });
        
        return await response.json();
    }
    
    async pollForToken(deviceCode) {
        const response = await fetch(`${this.authServerUrl}/oauth/token`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
                client_id: this.clientId,
                device_code: deviceCode
            })
        });
        
        return await response.json();
    }
    
    async authenticate() {
        // Step 1: Request device code
        const deviceAuth = await this.requestDeviceCode();
        
        console.log(`Please visit: ${deviceAuth.verification_uri}`);
        console.log(`Enter code: ${deviceAuth.user_code}`);
        
        // Step 2: Poll for authorization
        const interval = deviceAuth.interval * 1000; // Convert to milliseconds
        
        return new Promise((resolve, reject) => {
            const pollInterval = setInterval(async () => {
                try {
                    const tokenResponse = await this.pollForToken(deviceAuth.device_code);
                    
                    if (tokenResponse.access_token) {
                        clearInterval(pollInterval);
                        resolve(tokenResponse);
                    } else if (tokenResponse.error === 'authorization_pending') {
                        // Continue polling
                        console.log('Waiting for user authorization...');
                    } else {
                        clearInterval(pollInterval);
                        reject(new Error(tokenResponse.error_description));
                    }
                } catch (error) {
                    clearInterval(pollInterval);
                    reject(error);
                }
            }, interval);
        });
    }
}

// Usage
const deviceClient = new DeviceAuthClient(
    'your-client-id',
    'https://auth.hydrigital.com'
);

deviceClient.authenticate()
    .then(tokens => console.log('Access token:', tokens.access_token))
    .catch(error => console.error('Error:', error));

๐Ÿ”ต PHP (Device Flow)

<?php
class DeviceAuthClient {
    private $clientId;
    private $authServerUrl;
    
    public function __construct($clientId, $authServerUrl) {
        $this->clientId = $clientId;
        $this->authServerUrl = $authServerUrl;
    }
    
    public function requestDeviceCode() {
        $url = $this->authServerUrl . '/oauth/deviceauthorization';
        $data = [
            'client_id' => $this->clientId,
            'scope' => 'read write user'
        ];
        
        $options = [
            'http' => [
                'header' => "Content-type: application/x-www-form-urlencoded\r\n",
                'method' => 'POST',
                'content' => http_build_query($data)
            ]
        ];
        
        $context = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        
        return json_decode($result, true);
    }
    
    public function pollForToken($deviceCode) {
        $url = $this->authServerUrl . '/oauth/token';
        $data = [
            'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
            'client_id' => $this->clientId,
            'device_code' => $deviceCode
        ];
        
        $options = [
            'http' => [
                'header' => "Content-type: application/x-www-form-urlencoded\r\n",
                'method' => 'POST',
                'content' => http_build_query($data)
            ]
        ];
        
        $context = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        
        return json_decode($result, true);
    }
    
    public function authenticate() {
        // Step 1: Request device code
        $deviceAuth = $this->requestDeviceCode();
        
        echo "Please visit: " . $deviceAuth['verification_uri'] . "\n";
        echo "Enter code: " . $deviceAuth['user_code'] . "\n";
        
        // Step 2: Poll for authorization
        $interval = $deviceAuth['interval'];
        
        while (true) {
            sleep($interval);
            
            $tokenResponse = $this->pollForToken($deviceAuth['device_code']);
            
            if (isset($tokenResponse['access_token'])) {
                return $tokenResponse;
            } elseif ($tokenResponse['error'] === 'authorization_pending') {
                echo "Waiting for user authorization...\n";
                continue;
            } else {
                throw new Exception($tokenResponse['error_description']);
            }
        }
    }
}

// Usage
$deviceClient = new DeviceAuthClient(
    'your-client-id',
    'https://auth.hydrigital.com'
);

try {
    $tokens = $deviceClient->authenticate();
    echo "Access token: " . $tokens['access_token'] . "\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

๐Ÿ Python (Device Flow)

import requests
import time
import json

class DeviceAuthClient:
    def __init__(self, client_id, auth_server_url):
        self.client_id = client_id
        self.auth_server_url = auth_server_url
    
    def request_device_code(self):
        url = f"{self.auth_server_url}/oauth/deviceauthorization"
        data = {
            'client_id': self.client_id,
            'scope': 'read write user'
        }
        
        response = requests.post(url, data=data)
        return response.json()
    
    def poll_for_token(self, device_code):
        url = f"{self.auth_server_url}/oauth/token"
        data = {
            'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
            'client_id': self.client_id,
            'device_code': device_code
        }
        
        response = requests.post(url, data=data)
        return response.json()
    
    def authenticate(self):
        # Step 1: Request device code
        device_auth = self.request_device_code()
        
        print(f"Please visit: {device_auth['verification_uri']}")
        print(f"Enter code: {device_auth['user_code']}")
        
        # Step 2: Poll for authorization
        interval = device_auth['interval']
        
        while True:
            time.sleep(interval)
            
            token_response = self.poll_for_token(device_auth['device_code'])
            
            if 'access_token' in token_response:
                return token_response
            elif token_response.get('error') == 'authorization_pending':
                print("Waiting for user authorization...")
                continue
            else:
                raise Exception(token_response.get('error_description', 'Unknown error'))

# Usage
device_client = DeviceAuthClient(
    'your-client-id',
    'https://auth.hydrigital.com'
)

try:
    tokens = device_client.authenticate()
    print(f"Access token: {tokens['access_token']}")
except Exception as e:
    print(f"Error: {e}")

๐ŸŒ cURL (Device Flow)

# Step 1: Request device code
curl -X POST "https://auth.hydrigital.com/oauth/deviceauthorization" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=your-client-id" \
  -d "scope=read write user"

# Response:
# {
#   "device_code": "GMMhmXXr61DE5f...",
#   "user_code": "WDJB-MJHT",
#   "verification_uri": "https://auth.hydrigital.com/device",
#   "verification_uri_complete": "https://auth.hydrigital.com/device?user_code=WDJB-MJHT",
#   "expires_in": 600,
#   "interval": 5
# }

# Step 2: User visits verification_uri and enters user_code

# Step 3: Poll for token (repeat every 5 seconds until success)
curl -X POST "https://auth.hydrigital.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "client_id=your-client-id" \
  -d "device_code=GMMhmXXr61DE5f..."

# Pending response:
# {
#   "error": "authorization_pending",
#   "error_description": "The authorization request is still pending"
# }

# Success response:
# {
#   "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
#   "token_type": "Bearer",
#   "expires_in": 3600,
#   "refresh_token": "def50200e8f...",
#   "scope": "read write user"
# }

4Client Credentials Flow

For server-to-server communication where no user is involved.

๐Ÿ” Server-to-Server Authentication

// JavaScript/Node.js
const response = await fetch('https://auth.hydrigital.com/oauth/token', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: 'your-client-id',
        client_secret: 'your-client-secret',
        scope: 'read write'
    })
});

const tokens = await response.json();
console.log('Access Token:', tokens.access_token);

5Token Management

Once you have tokens, you'll need to handle validation, refresh, and revocation operations.

๏ฟฝ Token Introspection

// Validate an access token
const introspectResponse = await fetch('https://auth.hydrigital.com/oauth/introspect', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer ' + accessToken
    },
    body: new URLSearchParams({
        'token': accessToken,
        'token_type_hint': 'access_token'
    })
});

const tokenInfo = await introspectResponse.json();
console.log('Token is active:', tokenInfo.active);

๐Ÿ”„ Token Refresh

// Refresh an access token using refresh token
const refreshResponse = await fetch('https://auth.hydrigital.com/oauth/token', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
        'grant_type': 'refresh_token',
        'refresh_token': refreshToken,
        'client_id': 'your-client-id'
    })
});

const newTokens = await refreshResponse.json();
console.log('New access token:', newTokens.access_token);

โŒ Token Revocation

// Revoke a token
const revokeResponse = await fetch('https://auth.hydrigital.com/oauth/revoke', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
        'token': accessToken,
        'token_type_hint': 'access_token',
        'client_id': 'your-client-id'
    })
});

if (revokeResponse.ok) {
    console.log('Token revoked successfully');
}

๐Ÿ”— Next Steps