Sign in with CentroShield

Standards-based OAuth 2.0 / OpenID Connect identity provider.
Drop a button on your site, get authenticated users.

Overview

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:

email_verified is true only after CentroShield has a recorded verification timestamp for that email.

Stable identifier: always key your user records off sub (a numeric string). Username and email can change; sub never does.

1. Register your app

  1. Sign in to CentroShield admin → OAuth Apps
  2. Click Register New App
  3. Provide:
    • App name (shown on consent screen)
    • Logo URL (optional)
    • Redirect URI(s) — exact match enforced. https://yoursite.com/centroshield_callback.php
  4. Choose a client type: confidential for server-side apps, or public + PKCE for browser/native apps.
  5. For confidential clients, save the client_secret immediately — it's shown only once. Public clients receive no secret.

2. The flow

  1. User clicks "Sign in with CentroShield" on your site
  2. You redirect them to /oauth/authorize with your client_id
  3. If not logged into CentroShield, they log in there
  4. They see a consent screen ("YourApp wants to access...")
  5. They click Allow → redirected back to your redirect_uri with ?code=...
  6. Your app POSTs that code to /oauth/token. Confidential clients authenticate with their client_secret; public clients use PKCE S256 and no secret.
  7. You get back access_token, refresh_token, and id_token
  8. Use the access_token as Bearer auth on /oauth/userinfo to fetch the profile

3. Endpoints

GEThttps://centroshield.com/oauth/authorize

User-facing. Browser-redirects only.

ParamRequiredNotes
response_typeyesMust be code
client_idyesFrom the admin panel
redirect_uriyesMust exactly match a registered URI
scopenoUse openid profile email plus only the CentroShield scopes your app needs. Existing legacy clients may continue using centroshield.
staterecommendedRandom value, echoed back. Use to prevent CSRF.
nonceoptionalEchoed in the id_token (OIDC)
code_challengepublic clientsPKCE challenge using S256. Plain PKCE is not accepted.
code_challenge_methodwith PKCEMust be S256
POSThttps://centroshield.com/oauth/token

Server-to-server. Form-encoded body.

ParamRequiredNotes
grant_typeyesauthorization_code or refresh_token
codeyes (auth_code)From the redirect
refresh_tokenyes (refresh)
redirect_uriyesMust match the one used at /authorize
client_idyesRegistered client identifier
client_secretconfidential clientsIn body or HTTP Basic auth. Public clients do not send a secret.
code_verifierpublic / PKCE clientsOriginal high-entropy verifier whose SHA-256 challenge was sent at authorization time.
GEThttps://centroshield.com/oauth/userinfo

Bearer authenticated. Authorization: Bearer <access_token>

POSThttps://centroshield.com/oauth/revoke

Revokes an access or refresh token. Body: token + client credentials.

4. PHP example

Step 1 — Send the user to /oauth/authorize

// 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;

Step 2 — Handle the callback at centroshield_callback.php

session_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.

5. User profile shape

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
        }
      ]
    }
  }
}
Privacy note: request the narrowest scopes your app needs. wallet, validator, and mining:read are separately consented. The legacy centroshield scope remains only for backward compatibility.

6. OIDC discovery

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.

7. Troubleshooting

error=invalid_grant on /token

Most common causes:

error=invalid_client on /token

Wrong client_secret, or the client was disabled in the admin panel.

"redirect_uri does not match" on /authorize

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 APIs

The 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.