Standards-based OAuth 2.0 / OpenID Connect identity provider.
Drop a button on your site, get authenticated users.
CentroShield is an OAuth 2.0 Authorization Code Provider with OpenID Connect extensions. If you've integrated Sign in with Google, Sign in with GitHub, or any OIDC provider, you already know how this works.
Identity Gateway v2 supports standard OIDC scopes plus CentroShield-specific permissions. New integrations should request only what they need:
openid profile email — standard identity claims and an RS256-signed ID tokenwallet — CentroShield wallet addressvalidator — validator status/detailsmining:read — mining key metadata/statscentroshield — legacy compatibility scope for existing integrations; it exposes the historical combined profileemail_verified is true only after CentroShield has a recorded verification timestamp for that email.
sub (a numeric string). Username and email can change; sub never does.
https://yoursite.com/centroshield_callback.php/oauth/authorize with your client_idredirect_uri with ?code=.../oauth/token. Confidential clients authenticate with their client_secret; public clients use PKCE S256 and no secret.access_token, refresh_token, and id_tokenaccess_token as Bearer auth on /oauth/userinfo to fetch the profileUser-facing. Browser-redirects only.
| Param | Required | Notes |
|---|---|---|
response_type | yes | Must be code |
client_id | yes | From the admin panel |
redirect_uri | yes | Must exactly match a registered URI |
scope | no | Use openid profile email plus only the CentroShield scopes your app needs. Existing legacy clients may continue using centroshield. |
state | recommended | Random value, echoed back. Use to prevent CSRF. |
nonce | optional | Echoed in the id_token (OIDC) |
code_challenge | public clients | PKCE challenge using S256. Plain PKCE is not accepted. |
code_challenge_method | with PKCE | Must be S256 |
Server-to-server. Form-encoded body.
| Param | Required | Notes |
|---|---|---|
grant_type | yes | authorization_code or refresh_token |
code | yes (auth_code) | From the redirect |
refresh_token | yes (refresh) | |
redirect_uri | yes | Must match the one used at /authorize |
client_id | yes | Registered client identifier |
client_secret | confidential clients | In body or HTTP Basic auth. Public clients do not send a secret. |
code_verifier | public / PKCE clients | Original high-entropy verifier whose SHA-256 challenge was sent at authorization time. |
Bearer authenticated. Authorization: Bearer <access_token>
Revokes an access or refresh token. Body: token + client credentials.
// On your "Sign in with CentroShield" button click:
session_start();
$_SESSION['oauth_state'] = bin2hex(random_bytes(16));
$params = [
'response_type' => 'code',
'client_id' => 'YOUR_CLIENT_ID',
'redirect_uri' => 'https://yoursite.com/centroshield_callback.php',
'scope' => 'openid profile email',
'state' => $_SESSION['oauth_state'],
];
header('Location: https://centroshield.com/oauth/authorize?' . http_build_query($params));
exit;
centroshield_callback.phpsession_start();
// Defend against CSRF
if (empty($_GET['state']) || $_GET['state'] !== ($_SESSION['oauth_state'] ?? '')) {
die('Invalid state');
}
unset($_SESSION['oauth_state']);
if (empty($_GET['code'])) die('Missing code');
// Exchange code for tokens
$ch = curl_init('https://centroshield.com/oauth/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => 'https://yoursite.com/centroshield_callback.php',
'client_id' => 'YOUR_CLIENT_ID',
'client_secret' => 'YOUR_CLIENT_SECRET',
]),
]);
$tokens = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($tokens['access_token'])) {
die('Token exchange failed: ' . ($tokens['error_description'] ?? 'unknown'));
}
// Fetch the user profile
$ch = curl_init('https://centroshield.com/oauth/userinfo');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $tokens['access_token']],
]);
$user = json_decode(curl_exec($ch), true);
curl_close($ch);
// $user['sub'] — stable CentroShield user id (use as your link key)
// $user['email'] — email claim (when email scope was granted)
// $user['email_verified'] — true only when CentroShield has verified it
// $user['preferred_username'] — username
// $user['centroshield']['wallet_address'] — wallet
// $user['centroshield']['validator'] — validator info or null
// $user['centroshield']['mining'] — mining keys/stats
// Now: find-or-create a user in YOUR database, keyed off $user['sub'].
// Log them into your normal session.
The /oauth/userinfo response is filtered by the scopes actually granted. A request for openid profile email wallet validator mining:read can contain:
{
"sub": "12345",
"preferred_username": "alice",
"name": "alice",
"email": "alice@example.com",
"email_verified": true,
"centroshield": {
"user_id": 12345,
"username": "alice",
"wallet_address": "centro_abc123...",
"role": "user",
"created_at": "2024-08-15 10:23:01",
"is_validator": true,
"validator": {
"node_id": "shield_abc123",
"status": "active",
"tier": "pro",
"reputation": "87.50",
"blocks_verified": 1247,
"total_attestations": 18532,
"auto_sign": 1,
"last_heartbeat": "2025-04-28 22:14:33",
"registered_at": "2024-08-15 10:25:11"
},
"mining": {
"key_count": 2,
"keys": [
{
"api_provider": "groq",
"label": "My Groq Key",
"pool_id": "lite",
"total_tasks": 3421,
"total_earned": "12.4521",
"hashrate": "1850.20",
"last_used_at": "2025-04-28 22:10:01",
"is_active": 1
}
]
}
}
}
wallet, validator, and mining:read are separately consented. The legacy centroshield scope remains only for backward compatibility.
If you're using a generic OIDC client library (e.g. league/oauth2-client, openid-client for Node, authlib for Python), point it at:
https://centroshield.com/.well-known/openid-configuration
The discovery document advertises RS256 ID-token signing, the JWKS endpoint at /oauth/jwks, PKCE S256, supported scopes, token authentication methods, and all OAuth endpoints. Confidential clients configure a client_secret; public clients configure PKCE and no secret.
error=invalid_grant on /tokenMost common causes:
redirect_uri sent to /token doesn't exactly match the one sent to /authorizeclient_id with this codeerror=invalid_client on /tokenWrong client_secret, or the client was disabled in the admin panel.
The redirect_uri you sent must exactly match one of the URIs registered in the admin panel — including scheme, host, port, and path. Trailing slashes matter.
access_token works on /userinfo but not on partner APIsThe OAuth access token is only valid on CentroShield endpoints. To call CentroShield's partner APIs (pools, validators, etc.), you still use your partner_key — those are separate trust relationships.