*/ class Oidc_Client { public $baseUrl; public $clientId; public $clientSecret; public $metaData; public $localEndpoint; public $lastError; public $http; // Simple_HTTP object public function __construct( $baseUrl, $clientId, $clientSecret ){ $this->baseUrl = $baseUrl; $this->clientId = $clientId; $this->clientSecret = $clientSecret; $localHost = $_SERVER['HTTP_HOST'] ?? 'localhost'; $this->localEndpoint = "https://{$localHost}/modules/auth_services/" . 'oidc_endpoint.php?sid=' . session_id(); $url = parse_url( $baseUrl ); require_once __DIR__ . '/simple_http.class.php'; $this->http = new Simple_HTTP( $url['host'], TRUE, $url['port'] ?? NULL ); } public function get_meta_data(){ $this->lastError = ''; if ( is_null( $this->metaData ) ){ $url = "{$this->baseUrl}/.well-known/openid-configuration"; $this->metaData = $this->send_request( $url ); } return $this->metaData; } public function get_authorization_url(){ $this->lastError = ''; $meta = $this->get_meta_data(); if ( empty( $meta['authorization_endpoint'] ) ){ $this->lastError = 'No authorization endpoint provided'; return FALSE; } $_SESSION['state'] = bin2hex( random_bytes(8) ); $_SESSION['code_verifier'] = bin2hex( random_bytes(50) ); $codeChallenge = hash( 'sha256', $_SESSION['code_verifier'], TRUE ); $_SESSION['auth_url'] = $meta['authorization_endpoint'] . '?' . http_build_query([ 'response_type' => 'code', 'client_id' => $this->clientId, 'redirect_uri' => $this->localEndpoint, 'state' => $_SESSION['state'], 'scope' => 'openid profile', 'code_challenge' => $this->base64_urlencode( $codeChallenge ), 'code_challenge_method' => 'S256', ]); return $_SESSION['auth_url']; } public function get_access_token( $code ){ $this->lastError = ''; $meta = $this->get_meta_data(); if ( empty( $meta['token_endpoint'] ) ){ $this->lastError = 'No token endpoint provided'; return FALSE; } $response = $this->send_request( $meta['token_endpoint'], [ 'grant_type' => 'authorization_code', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $code, 'code_verifier' => $_SESSION['code_verifier'], 'redirect_uri' => $this->localEndpoint, ] ); if ( ! isset( $response['access_token'] ) ){ $this->lastError = 'Access token missing in response'; return FALSE; } return $response['access_token']; } public function get_user_info( $accessToken ){ $this->lastError = ''; $meta = $this->get_meta_data(); if ( empty( $meta['userinfo_endpoint'] ) ){ $this->lastError = 'No userinfo endpoint provided'; return FALSE; } return $this->send_request( $this->metaData['userinfo_endpoint'], [ 'access_token' => $accessToken, ]); } public function get_logout_url( $redirectUrl = '' ){ return "{$this->baseUrl}/protocol/openid-connect/logout" . ( $redirectUrl ? '?redirect_uri=' . url_encode( $redirectUrl ) : '' ); } private function base64_urlencode( $string ){ return rtrim( strtr( base64_encode( $string ), '+/', '-_' ), '=' ); } private function send_request( $url, $data = [] ){ $url = parse_url( $url ); $path = $url['path'] ?? '/'; if ( $data ){ $content = http_build_query( $data ); $contentType = 'application/x-www-form-urlencoded'; $response = $this->http->send( 'POST', $path, $content, $contentType ); } else { $response = $this->http->send( 'GET', $path ); } if ( ! $response ){ return FALSE; } $body = json_decode( $response['body'], TRUE ); if ( $response['status'] != 200 ){ $this->lastError = $body['error'] ?? 'No error message provided'; return FALSE; } return $body; } } // end of file oidc_client.class.php