Quick access to server resources
Complete examples and code snippets for integrating with the Hydrigital OAuth2 Authentication Server.
Before integrating with the OAuth2 server, you'll need to configure your application with the following settings:
your-client-id)http://localhost:8080/callback)openid profile email)https://auth.hydrigital.comThe 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. |
The most common OAuth2 flow for web applications. This involves redirecting the user to the authorization server.
// 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
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());
?>
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}")
# 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"
For input-constrained devices like Smart TVs, IoT devices, etc. that can't easily handle web-based flows.
// 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
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";
}
?>
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}")
# 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"
# }
For server-to-server communication where no user is involved.
// 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);
Once you have tokens, you'll need to handle validation, refresh, and revocation operations.
// 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);
// 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);
// 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');
}