PHP code example of zoreal / oauth2

1. Go to this page and download the library: Download zoreal/oauth2 library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

zoreal / oauth2 example snippets


use Zoreal\OAuth2\Client;
use Zoreal\OAuth2\ClientAuth;

$zoreal = new Client(
    clientId: $_ENV['ZOREAL_CLIENT_ID'],                                // ast_...
    auth: ClientAuth::clientSecretBasic($_ENV['ZOREAL_CLIENT_SECRET']),
    issuer: $_ENV['ZOREAL_ISSUER'] ?? Client::DEFAULT_ISSUER,
);

$login = $zoreal->authenticate(
    code: $body['code'],
    codeVerifier: $body['code_verifier'],   // PKCE is mandatory; the SDK hands it over
    nonce: $body['nonce'],                  // binds the ID token to this login
);

$login->sub();            // "TC5X-JN7G-YTSE-6E63" -- pairwise, stable for YOUR domain
$login->acr();            // "zoreal.live" | "zoreal.device" | "zoreal.session"
$login->assurance();      // uniqueness basis, verification month, chip liveness, trust tier
$login->email();          // from /userinfo, when your client has the email scope
$login->emailVerified();
$login->name();           // from /userinfo, profile.name scope

$user = $users->findByProviderUid('zoreal', $login->sub());
if ($user === null) {
    if ($login->emailVerified()) {
        $user = $users->findByEmail($login->email());   // claim, don't collide
    }
    $user ??= $users->create(['email' => $login->email()]);
    $users->linkProvider($user, 'zoreal', $login->sub());
}

$login = $zoreal->authenticate(
    code: $body['code'],
    codeVerifier: $body['code_verifier'],
    nonce: $body['nonce'],
    acr: 'zoreal.live',   // throws VerificationError unless the signed token says so
);

$login->acr();                          // "zoreal.live" -- what actually happened
$login->live();                         // convenience: acr() === 'zoreal.live'
$login->satisfiesAcr('zoreal.device');  // true (live is stronger than device)

$login = $zoreal->authenticate(
    code: $body['code'],
    codeVerifier: $body['code_verifier'],
    nonce: $body['nonce'],
);
if (!$login->satisfiesAcr('zoreal.live')) {
    // step the user up, or refuse the sensitive action
}

use Zoreal\OAuth2\ClientAuth;

ClientAuth::none();                                       // public client: PKCE is the only proof
ClientAuth::clientSecretBasic('zcs_...');                 // the secret travels as HTTP Basic
ClientAuth::privateKeyJwt($pemPrivateKey, kid: 'key-1');  // RFC 7523, signed here per exchange
ClientAuth::tlsClientAuth('/path/client.crt', '/path/client.key');

$a = $login->assurance() ?? [];
if (($a['uniqueness'] ?? null) !== 'personal_number' || ($a['trust_tier'] ?? null) !== 'high') {
    // not strong enough for this action -- refuse or step up
}


// POST /auth/zoreal
//
// Your frontend's ZorealLogin onSuccess posts { code, code_verifier, nonce }
// here over your own TLS. Protect this endpoint with your normal CSRF /
// same-origin controls, exactly as you would any login route -- the ZOREAL
// nonce protects the token, not your endpoint.

use Zoreal\OAuth2\Client;
use Zoreal\OAuth2\ClientAuth;
use Zoreal\OAuth2\ExchangeError;
use Zoreal\OAuth2\VerificationError;
use Zoreal\OAuth2\UserinfoError;

$zoreal = new Client(
    clientId: $_ENV['ZOREAL_CLIENT_ID'],
    auth: ClientAuth::clientSecretBasic($_ENV['ZOREAL_CLIENT_SECRET']),
);

$body = json_decode((string) file_get_contents('php://input'), true) ?: [];
header('Content-Type: application/json');

try {
    $login = $zoreal->authenticate(
        code: $body['code'] ?? '',
        codeVerifier: $body['code_verifier'] ?? '',
        nonce: $body['nonce'] ?? null,
        // acr: 'zoreal.live',   // add for a step-up / high-value login
    );

    $user = $users->findByProviderUid('zoreal', $login->sub());
    if ($user === null) {
        // Claim an existing account that owns this verified email rather than
        // colliding on the unique index; otherwise create one.
        if ($login->emailVerified()) {
            $user = $users->findByEmail($login->email());
        }
        $user ??= $users->create([
            'email' => $login->email(),
            'name'  => $login->name(),
        ]);
        $users->linkProvider($user, 'zoreal', $login->sub());
    }

    session_regenerate_id(true);            // fixation defence
    $_SESSION['user_id'] = $user->id;
    echo json_encode(['ok' => true]);
} catch (ExchangeError | VerificationError $e) {
    // A spent code or a token that did not verify: the login must be restarted.
    error_log('ZOREAL login failed: ' . $e->getMessage());
    http_response_code(401);
    echo json_encode(['error' => 'sign_in_failed']);
} catch (UserinfoError $e) {
    // Personal data was unreachable. Fine for a returning user matched on sub;
    // fatal for a signup that needs the email.
    http_response_code(401);
    echo json_encode(['error' => 'sign_in_failed']);
}