<?php
declare(strict_types=1);
namespace PpPhp\Generated\SocialAuth;

/**
 * LinkedIn — "Sign In with LinkedIn using OpenID Connect".
 *
 * LinkedIn replaced its old r_liteprofile/r_emailaddress API with a standard
 * OIDC product; this implements that one. The LinkedIn app must have the
 * "Sign In with LinkedIn using OpenID Connect" product enabled, otherwise the
 * authorize call fails with `unauthorized_scope_error`.
 *
 * The id_token is verified against LinkedIn's JWKS; userinfo is not required.
 */
final class LinkedInProvider extends AbstractProvider
{
    public function id(): string { return 'linkedin'; }

    public function authorizationUrl(array $t): string
    {
        return $this->query('https://www.linkedin.com/oauth/v2/authorization', [
            'response_type' => 'code',
            'client_id'     => $this->value('client_id', 'LINKEDIN_CLIENT_ID'),
            'redirect_uri'  => $this->callbackUri(),
            'scope'         => 'openid profile email',
            'state'         => $t['state'],
            'nonce'         => $t['nonce'],
        ]);
    }

    public function identity(array $r, array $t): array
    {
        $this->failOnProviderError($r);
        if (empty($r['code'])) {
            throw new \RuntimeException('LinkedIn did not return an authorization code.');
        }
        $tokens = $this->token('https://www.linkedin.com/oauth/v2/accessToken', [
            'grant_type'    => 'authorization_code',
            'code'          => $r['code'],
            'client_id'     => $this->value('client_id', 'LINKEDIN_CLIENT_ID'),
            'client_secret' => $this->value('client_secret', 'LINKEDIN_CLIENT_SECRET'),
            'redirect_uri'  => $this->callbackUri(),
        ]);

        $idToken = (string) ($tokens['id_token'] ?? '');
        if ($idToken !== '') {
            $c = JwtVerifier::verify(
                $idToken,
                'https://www.linkedin.com/oauth/openid/jwks',
                ['https://www.linkedin.com/oauth'],
                $this->value('client_id', 'LINKEDIN_CLIENT_ID'),
                // LinkedIn does not always echo the nonce; binding is still
                // provided by `state`, which SocialAuthManager validates.
                ''
            );
        } else {
            // Fallback for apps still returning only an access token.
            $access = (string) ($tokens['access_token'] ?? '');
            if ($access === '') {
                throw new \RuntimeException('LinkedIn did not return an identity token.');
            }
            $c = HttpClient::get('https://api.linkedin.com/v2/userinfo', ['Authorization: Bearer ' . $access]);
        }

        return [
            'provider'       => 'linkedin',
            'subject'        => (string) ($c['sub'] ?? ''),
            'email'          => (string) ($c['email'] ?? ''),
            'email_verified' => $this->verified($c['email_verified'] ?? false),
            'name'           => (string) ($c['name'] ?? trim(($c['given_name'] ?? '') . ' ' . ($c['family_name'] ?? ''))),
            'picture'        => (string) ($c['picture'] ?? ''),
            'raw'            => is_array($c) ? $c : [],
        ];
    }
}
