<?php
/*
 * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the LICENSE file in the project root for the full license text.
 */

declare(strict_types=1);

namespace App\Auth;

/**
 * Pure helpers for digesting the verified claims object returned by
 * jumbojett/openid-connect-php. Kept separate from {@see OidcClient}
 * (a thin factory) so the rules can be unit-tested without a real
 * tenant.
 */
final class OidcClaims
{
    /**
     * Resolve the audit-trail email for an OIDC user.
     *
     * R01-N18 — in some Entra tenant configurations `preferred_username`
     * is NOT a verified email; it can be controlled by the end user. The
     * audit log uses the email column as the human-readable actor, so an
     * attacker could otherwise spoof their identity in the forensic
     * trail. The user *identity* itself is keyed by `oid`/`sub` and is
     * unaffected by this helper.
     *
     * Rules:
     *  1. `preferred_username` is never used as a fallback — the only
     *     trusted email source is `claims.email`.
     *  2. If `claims.email_verified === false` we treat `claims.email`
     *     as untrusted (the issuer explicitly told us so).
     *  3. Entra v2.0 work/school accounts do not emit `email_verified`;
     *     the `email` they ship is sourced from the directory and is
     *     server-controlled. We treat a missing `email_verified` as
     *     "no negative signal" and trust `email` in that case.
     *  4. When the email is missing or explicitly unverified, fall
     *     back to a documented immutable label `entra:<oid>` so the
     *     audit row still has a stable, non-spoofable actor.
     */
    public static function resolveEmail(object $claims, string $oid): string
    {
        $rawEmail = (isset($claims->email) && is_scalar($claims->email))
            ? trim((string) $claims->email)
            : '';

        $explicitlyUnverified = (($claims->email_verified ?? null) === false);

        if ($rawEmail !== '' && !$explicitlyUnverified) {
            return $rawEmail;
        }

        return 'entra:' . $oid;
    }
}
