baseUrl = str_replace('/.well-known/openid-configuration', '', $endpoint); } else { $this->baseUrl = mb_rtrim($endpoint, '/'); } $this->scopes = $scopes ?: ['openid', 'profile', 'email']; $this->loadOidcConfiguration(); } /** * Load OIDC configuration from the well-known endpoint. */ protected function loadOidcConfiguration() { try { $url = $this->baseUrl.'/.well-known/openid-configuration'; $client = app(Client::class); $response = $client->get($url); $this->oidcConfig = json_decode($response->getBody()->getContents(), true); if (! $this->oidcConfig) { throw new Exception('OIDC configuration is empty or invalid JSON'); } if (! isset($this->oidcConfig['authorization_endpoint'])) { throw new Exception('authorization_endpoint not found in OIDC configuration'); } } catch (Exception $e) { throw new Exception('Failed to load OIDC configuration: '.$e->getMessage(), $e->getCode(), $e); } } /** * Get the authentication URL for the provider. */ protected function getAuthUrl($state) { if (! $this->oidcConfig || ! isset($this->oidcConfig['authorization_endpoint'])) { throw new Exception('OIDC configuration not loaded or authorization_endpoint not found.'); } return $this->buildAuthUrlFromBase($this->oidcConfig['authorization_endpoint'], $state); } /** * Get the token URL for the provider. */ protected function getTokenUrl() { if (! $this->oidcConfig || ! isset($this->oidcConfig['token_endpoint'])) { throw new Exception('OIDC configuration not loaded or token_endpoint not found.'); } return $this->oidcConfig['token_endpoint']; } /** * Get the raw user for the given access token. */ protected function getUserByToken($token) { if (! $this->oidcConfig || ! isset($this->oidcConfig['userinfo_endpoint'])) { throw new Exception('OIDC configuration not loaded or userinfo_endpoint not found.'); } $response = $this->getHttpClient()->get($this->oidcConfig['userinfo_endpoint'], [ 'headers' => [ 'Authorization' => 'Bearer '.$token, ], ]); return json_decode($response->getBody(), true); } /** * Map the raw user array to a Socialite User instance. */ public function mapUserToObject(array $user) { return (new User)->setRaw($user)->map([ 'id' => $user['sub'], 'nickname' => $user['preferred_username'] ?? null, 'name' => $user['name'] ?? null, 'email' => $user['email'] ?? null, 'avatar' => $user['picture'] ?? null, ]); } /** * Get the access token response for the given code. */ public function getAccessTokenResponse($code) { $response = $this->getHttpClient()->post($this->getTokenUrl(), [ 'headers' => ['Accept' => 'application/json'], 'form_params' => $this->getTokenFields($code), ]); return json_decode($response->getBody(), true); } /** * Get the POST fields for the token request. */ protected function getTokenFields($code) { return array_merge(parent::getTokenFields($code), [ 'grant_type' => 'authorization_code', ]); } }