├── .gitignore ├── .scrutinizer.yml ├── .styleci.yml ├── LICENSE.md ├── README.md ├── composer.json ├── docs └── examples │ ├── Broker.php │ └── Server.php └── src ├── Exceptions └── SSOServerException.php ├── Interfaces ├── SSOBrokerInterface.php └── SSOServerInterface.php ├── SSOBroker.php └── SSOServer.php /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | composer.lock 3 | vendor -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: [docs/*] 3 | 4 | checks: 5 | php: 6 | remove_extra_empty_lines: true 7 | remove_php_closing_tag: true 8 | remove_trailing_whitespace: true 9 | fix_use_statements: 10 | remove_unused: true 11 | preserve_multiple: false 12 | preserve_blanklines: true 13 | order_alphabetically: true 14 | fix_php_opening_tag: true 15 | fix_linefeed: true 16 | fix_line_ending: true 17 | fix_identation_4spaces: true 18 | fix_doc_comments: true -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Martynas Žaliaduonis 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Simple SSO skeleton 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/zefy/php-simple-sso/v/stable)](https://packagist.org/packages/zefy/php-simple-sso) [![Total Downloads](https://poser.pugx.org/zefy/php-simple-sso/downloads)](https://packagist.org/packages/zefy/php-simple-sso) [![Latest Unstable Version](https://poser.pugx.org/zefy/php-simple-sso/v/unstable)](https://packagist.org/packages/zefy/php-simple-sso) [![License](https://poser.pugx.org/zefy/php-simple-sso/license)](https://packagist.org/packages/zefy/php-simple-sso) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/zefy/php-simple-sso/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/zefy/php-simple-sso/?branch=master) [![Build Status](https://scrutinizer-ci.com/g/zefy/php-simple-sso/badges/build.png?b=master)](https://scrutinizer-ci.com/g/zefy/php-simple-sso/build-status/master) [![Code Intelligence Status](https://scrutinizer-ci.com/g/zefy/php-simple-sso/badges/code-intelligence.svg?b=master)](https://scrutinizer-ci.com/code-intelligence) 4 | 5 | 6 | ### Words meanings 7 | * ***SSO*** - Single Sign-On. 8 | * ***Server*** - page which works as SSO server, handles authentications, stores all sessions data. 9 | * ***Broker*** - your page which is used visited by clients/users. 10 | * ***Client/User*** - your every visitor. 11 | 12 | ### How it works? 13 | Client visits Broker and unique token is generated. When new token is generated we need to attach Client session to his session in Broker so he will be redirected to Server and back to Broker at this moment new session in Server will be created and associated with Client session in Broker's page. When Client visits other Broker same steps will be done except that when Client will be redirected to Server he already use his old session and same session id which associated with Broker#1. 14 | 15 | # Usage 16 | This is only a skeleton package so you need to write a little bit extra code to get it working. 17 | 18 | You can see [examples in docs/examples directory](docs/examples) and try to use it for your project. Also there is available [Laravel package](https://github.com/zefy/laravel-sso) which is written based on this package. 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zefy/php-simple-sso", 3 | "description": "Simple PHP SSO", 4 | "keywords": ["sso", "authentication", "login"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Martynas Žaliaduonis", 9 | "email": "m.zaliaduonis@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.1.3" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Zefy\\SimpleSSO\\": "src/" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/examples/Broker.php: -------------------------------------------------------------------------------- 1 | ssoServerUrl = null; 37 | $this->brokerName = null; 38 | $this->brokerSecret = null; 39 | 40 | if (!$this->ssoServerUrl || !$this->brokerName || !$this->brokerSecret) { 41 | throw new Exception('Missing configuration values.'); 42 | } 43 | } 44 | 45 | /** 46 | * Somehow save random token for client. 47 | * 48 | * @return void 49 | */ 50 | protected function saveToken() 51 | { 52 | if (isset($this->token) && $this->token) { 53 | return; 54 | } 55 | 56 | if ($this->token = $this->getCookie($this->getCookieName())) { 57 | return; 58 | } 59 | 60 | // If cookie token doesn't exist, we need to create it with unique token... 61 | $this->token = base_convert(md5(uniqid(rand(), true)), 16, 36); 62 | setcookie($this->getCookieName(), $this->token, time() + 60 * 60 * 12, '/'); 63 | 64 | // ... and attach it to broker session in SSO server. 65 | $this->attach(); 66 | } 67 | 68 | /** 69 | * Delete saved token. 70 | * 71 | * @return void 72 | */ 73 | protected function deleteToken() 74 | { 75 | $this->token = null; 76 | setcookie($this->getCookieName(), null, -1, '/'); 77 | } 78 | 79 | /** 80 | * Make request to SSO server. 81 | * 82 | * @param string $method Request method 'post' or 'get'. 83 | * @param string $command Request command name. 84 | * @param array $parameters Parameters for URL query string if GET request and form parameters if it's POST request. 85 | * 86 | * @return array 87 | */ 88 | protected function makeRequest(string $method, string $command, array $parameters = []) 89 | { 90 | $commandUrl = $this->generateCommandUrl($command); 91 | $headers = [ 92 | 'Accept' => 'application/json', 93 | 'Authorization' => 'Bearer '. $this->getSessionId(), 94 | ]; 95 | switch ($method) { 96 | case 'POST': 97 | $body = ['form_params' => $parameters]; 98 | break; 99 | case 'GET': 100 | $body = ['query' => $parameters]; 101 | break; 102 | default: 103 | $body = []; 104 | break; 105 | } 106 | $client = new GuzzleHttp\Client; 107 | $response = $client->request($method, $commandUrl, $body + ['headers' => $headers]); 108 | return json_decode($response->getBody(), true); 109 | } 110 | 111 | /** 112 | * Redirect client to specified url. 113 | * 114 | * @param string $url URL to be redirected. 115 | * @param array $parameters HTTP query string. 116 | * @param int $httpResponseCode HTTP response code for redirection. 117 | * 118 | * @return void 119 | */ 120 | protected function redirect(string $url, array $parameters = [], int $httpResponseCode = 307) 121 | { 122 | $query = ''; 123 | // Making URL query string if parameters given. 124 | if (!empty($parameters)) { 125 | $query = '?'; 126 | if (parse_url($url, PHP_URL_QUERY)) { 127 | $query = '&'; 128 | } 129 | $query .= http_build_query($parameters); 130 | } 131 | 132 | header('Location: ' . $url . $query, true, $httpResponseCode); 133 | exit; 134 | } 135 | 136 | /** 137 | * Getting current url which can be used as return to url. 138 | * 139 | * @return string 140 | */ 141 | protected function getCurrentUrl() 142 | { 143 | $protocol = !empty($_SERVER['HTTPS']) ? 'https://' : 'http://'; 144 | 145 | return $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 146 | } 147 | 148 | /** 149 | * Cookie name in which we save unique client token. 150 | * 151 | * @return string 152 | */ 153 | protected function getCookieName() 154 | { 155 | // Cookie name based on broker's name because there can be some brokers on same domain 156 | // and we need to prevent duplications. 157 | return 'sso_token_' . preg_replace('/[_\W]+/', '_', strtolower($this->brokerName)); 158 | } 159 | 160 | /** 161 | * Get COOKIE value by it's name. 162 | * 163 | * @param string $cookieName 164 | * 165 | * @return string|null 166 | */ 167 | protected function getCookie(string $cookieName) 168 | { 169 | if (isset($_COOKIE[$cookieName])) { 170 | return $_COOKIE[$cookieName]; 171 | } 172 | 173 | return null; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /docs/examples/Server.php: -------------------------------------------------------------------------------- 1 | [ 14 | 'secret' => 's3cr3th4sh', 15 | ], 16 | ]; 17 | 18 | /** 19 | * All available users. 20 | * 21 | * @var array 22 | */ 23 | protected $users = [ 24 | 'user1' => [ 25 | 'password1' 26 | ], 27 | ]; 28 | 29 | /** 30 | * Redirect to provided URL with query string. 31 | * 32 | * If $url is null, redirect to url which given in 'return_url'. 33 | * 34 | * @param string|null $url URL to be redirected. 35 | * @param array $parameters HTTP query string. 36 | * @param int $httpResponseCode HTTP response code for redirection. 37 | * 38 | * @return void 39 | */ 40 | protected function redirect(?string $url = null, array $parameters = [], int $httpResponseCode = 307) 41 | { 42 | if (!$url) { 43 | $url = urldecode($_GET['return_url']); 44 | } 45 | $query = ''; 46 | // Making URL query string if parameters given. 47 | if (!empty($parameters)) { 48 | $query = '?'; 49 | if (parse_url($url, PHP_URL_QUERY)) { 50 | $query = '&'; 51 | } 52 | $query .= http_build_query($parameters); 53 | } 54 | header('Location: ' . $url . $query); 55 | exit; 56 | } 57 | 58 | /** 59 | * Returning json response for the broker. 60 | * 61 | * @param null|array $response Response array which will be encoded to json. 62 | * @param int $httpResponseCode HTTP response code. 63 | * 64 | * @return string 65 | */ 66 | protected function returnJson(?array $response = null, int $httpResponseCode = 200) 67 | { 68 | header('Content-Type: application/json'); 69 | return json_encode($response); 70 | } 71 | 72 | /** 73 | * Authenticate using user credentials 74 | * 75 | * @param string $username 76 | * @param string $password 77 | * 78 | * @return bool 79 | */ 80 | protected function authenticate(string $username, string $password) 81 | { 82 | if (!isset($this->users[$username]) || $this->users[$username]['password'] != $password) { 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | /** 90 | * Get the secret key and other info of a broker 91 | * 92 | * @param string $brokerId 93 | * 94 | * @return null|array 95 | */ 96 | protected function getBrokerInfo(string $brokerId) 97 | { 98 | if (!isset($this->brokers[$brokerId])) { 99 | return null; 100 | } 101 | 102 | return $this->brokers[$brokerId]; 103 | } 104 | 105 | /** 106 | * Get the information about a user 107 | * 108 | * @param string $username 109 | * 110 | * @return array|object|null 111 | */ 112 | protected function getUserInfo(string $username) 113 | { 114 | if (!isset($this->users[$username])) { 115 | return null; 116 | } 117 | 118 | return $this->users[$username]; 119 | } 120 | 121 | /** 122 | * Returning user info for broker. Should return json or something like that. 123 | * 124 | * @param array|object $user Can be user object or array. 125 | * 126 | * @return mixed 127 | */ 128 | protected function returnUserInfo($user) 129 | { 130 | return json_encode($user); 131 | } 132 | 133 | /** 134 | * Return session id sent from broker. 135 | * 136 | * @return null|string 137 | */ 138 | 139 | protected function getBrokerSessionId() 140 | { 141 | $headers = getallheaders(); 142 | if (isset($headers['Authorization']) && strpos($headers['Authorization'], 'Bearer') === 0) { 143 | $headers['Authorization'] = substr($headers['Authorization'], 7); 144 | 145 | return $headers['Authorization']; 146 | } 147 | 148 | return null; 149 | } 150 | 151 | /** 152 | * Start new session when user visits server. 153 | * 154 | * @return void 155 | */ 156 | protected function startUserSession() 157 | { 158 | if (session_status() !== PHP_SESSION_ACTIVE) { 159 | session_start(); 160 | } 161 | } 162 | 163 | /** 164 | * Set session data 165 | * 166 | * @param string $key 167 | * @param null|string $value 168 | * 169 | * @return void 170 | */ 171 | protected function setSessionData(string $key, ?string $value = null) 172 | { 173 | if (!$value) { 174 | unset($_SESSION['key']); 175 | return; 176 | } 177 | 178 | $_SESSION[$key] = $value; 179 | } 180 | 181 | /** 182 | * Get data saved in session. 183 | * 184 | * @param string $key 185 | * 186 | * @return null|string 187 | */ 188 | protected function getSessionData(string $key) 189 | { 190 | if ($key === 'id') { 191 | return session_id(); 192 | } 193 | 194 | if (!isset($_SESSION[$key])) { 195 | return null; 196 | } 197 | 198 | return $_SESSION[$key]; 199 | } 200 | 201 | /** 202 | * Start new session with specific session id. 203 | * 204 | * @param $sessionId 205 | * 206 | * @return void 207 | */ 208 | protected function startSession(string $sessionId) 209 | { 210 | session_id($sessionId); 211 | session_start(); 212 | } 213 | 214 | /** 215 | * Save broker session data to cache. 216 | * 217 | * @param string $brokerSessionId 218 | * @param string $sessionData 219 | * 220 | * @return void 221 | */ 222 | protected function saveBrokerSessionData(string $brokerSessionId, string $sessionData) 223 | { 224 | /** This is basic example and you should do something better. */ 225 | 226 | $cacheFile = fopen('broker_session_' . $brokerSessionId, 'w'); 227 | fwrite($cacheFile, $sessionData); 228 | fclose($cacheFile); 229 | } 230 | 231 | /** 232 | * Get broker session data from cache. 233 | * 234 | * @param string $brokerSessionId 235 | * 236 | * @return null|string 237 | */ 238 | protected function getBrokerSessionData(string $brokerSessionId) 239 | { 240 | /** This is basic example and you should do something better. */ 241 | 242 | $cacheFileName = 'broker_session_' . $brokerSessionId; 243 | 244 | if (!file_exists($cacheFileName)) { 245 | return null; 246 | } 247 | 248 | if (time() - 3600 > filemtime($cacheFileName)) { 249 | unlink($cacheFileName); 250 | 251 | return null; 252 | } 253 | 254 | echo file_get_contents($cacheFileName); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/Exceptions/SSOServerException.php: -------------------------------------------------------------------------------- 1 | setOptions(); 55 | $this->saveToken(); 56 | } 57 | 58 | /** 59 | * Attach client session to broker session in SSO server. 60 | * 61 | * @return void 62 | */ 63 | public function attach() 64 | { 65 | $parameters = [ 66 | 'return_url' => $this->getCurrentUrl(), 67 | 'broker' => $this->brokerName, 68 | 'token' => $this->token, 69 | 'checksum' => hash('sha256', 'attach' . $this->token . $this->brokerSecret) 70 | ]; 71 | 72 | $attachUrl = $this->generateCommandUrl('attach', $parameters); 73 | 74 | $this->redirect($attachUrl); 75 | } 76 | 77 | /** 78 | * Getting user info from SSO based on client session. 79 | * 80 | * @return array 81 | */ 82 | public function getUserInfo() 83 | { 84 | if (!isset($this->userInfo) || empty($this->userInfo)) { 85 | $this->userInfo = $this->makeRequest('GET', 'userInfo'); 86 | } 87 | 88 | return $this->userInfo; 89 | } 90 | 91 | /** 92 | * Login client to SSO server with user credentials. 93 | * 94 | * @param string $username 95 | * @param string $password 96 | * 97 | * @return bool 98 | */ 99 | public function login(string $username, string $password) 100 | { 101 | $this->userInfo = $this->makeRequest('POST', 'login', compact('username', 'password')); 102 | 103 | if (!isset($this->userInfo['error']) && isset($this->userInfo['data']['id'])) { 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | /** 111 | * Logout client from SSO server. 112 | * 113 | * @return void 114 | */ 115 | public function logout() 116 | { 117 | $this->makeRequest('POST', 'logout'); 118 | } 119 | 120 | /** 121 | * Generate request url. 122 | * 123 | * @param string $command 124 | * @param array $parameters 125 | * 126 | * @return string 127 | */ 128 | protected function generateCommandUrl(string $command, array $parameters = []) 129 | { 130 | $query = ''; 131 | if (!empty($parameters)) { 132 | $query = '?' . http_build_query($parameters); 133 | } 134 | 135 | return $this->ssoServerUrl . '/sso/' . $command . $query; 136 | } 137 | 138 | /** 139 | * Generate session key with broker name, broker secret and unique client token. 140 | * 141 | * @return string 142 | */ 143 | protected function getSessionId() 144 | { 145 | $checksum = hash('sha256', 'session' . $this->token . $this->brokerSecret); 146 | return "SSO-{$this->brokerName}-{$this->token}-$checksum"; 147 | } 148 | 149 | /** 150 | * Set base class options (sso server url, broker name and secret, etc). 151 | * 152 | * @return void 153 | */ 154 | abstract protected function setOptions(); 155 | 156 | /** 157 | * Somehow save random token for client. 158 | * 159 | * @return void 160 | */ 161 | abstract protected function saveToken(); 162 | 163 | /** 164 | * Delete saved token. 165 | * 166 | * @return void 167 | */ 168 | abstract protected function deleteToken(); 169 | 170 | /** 171 | * Make request to SSO server. 172 | * 173 | * @param string $method Request method 'post' or 'get'. 174 | * @param string $command Request command name. 175 | * @param array $parameters Parameters for URL query string if GET request and form parameters if it's POST request. 176 | * 177 | * @return array 178 | */ 179 | abstract protected function makeRequest(string $method, string $command, array $parameters = []); 180 | 181 | /** 182 | * Redirect client to specified url. 183 | * 184 | * @param string $url URL to be redirected. 185 | * @param array $parameters HTTP query string. 186 | * @param int $httpResponseCode HTTP response code for redirection. 187 | * 188 | * @return void 189 | */ 190 | abstract protected function redirect(string $url, array $parameters = [], int $httpResponseCode = 307); 191 | 192 | /** 193 | * Getting current url which can be used as return to url. 194 | * 195 | * @return string 196 | */ 197 | abstract protected function getCurrentUrl(); 198 | } 199 | -------------------------------------------------------------------------------- /src/SSOServer.php: -------------------------------------------------------------------------------- 1 | fail('No broker id specified.', true); 35 | } 36 | 37 | if (!$token) { 38 | $this->fail('No token specified.', true); 39 | } 40 | 41 | if (!$checksum || $checksum != $this->generateAttachChecksum($broker, $token)) { 42 | $this->fail('Invalid checksum.', true); 43 | } 44 | 45 | $this->startUserSession(); 46 | $sessionId = $this->generateSessionId($broker, $token); 47 | 48 | $this->saveBrokerSessionData($sessionId, $this->getSessionData('id')); 49 | } catch (SSOServerException $e) { 50 | return $this->redirect(null, ['sso_error' => $e->getMessage()]); 51 | } 52 | 53 | $this->attachSuccess(); 54 | } 55 | 56 | /** 57 | * @param null|string $username 58 | * @param null|string $password 59 | * 60 | * @return string 61 | */ 62 | public function login(?string $username, ?string $password) 63 | { 64 | try { 65 | $this->startBrokerSession(); 66 | 67 | if (!$username || !$password) { 68 | $this->fail('No username and/or password provided.'); 69 | } 70 | 71 | if (!$this->authenticate($username, $password)) { 72 | $this->fail('User authentication failed.'); 73 | } 74 | } catch (SSOServerException $e) { 75 | return $this->returnJson(['error' => $e->getMessage()]); 76 | } 77 | 78 | $this->setSessionData('sso_user', $username); 79 | 80 | return $this->userInfo(); 81 | } 82 | 83 | /** 84 | * Logging user out. 85 | * 86 | * @return string 87 | */ 88 | public function logout() 89 | { 90 | try { 91 | $this->startBrokerSession(); 92 | $this->setSessionData('sso_user', null); 93 | } catch (SSOServerException $e) { 94 | return $this->returnJson(['error' => $e->getMessage()]); 95 | } 96 | 97 | return $this->returnJson(['success' => 'User has been successfully logged out.']); 98 | } 99 | 100 | /** 101 | * Returning user info for the broker. 102 | * 103 | * @return string 104 | */ 105 | public function userInfo() 106 | { 107 | try { 108 | $this->startBrokerSession(); 109 | 110 | $username = $this->getSessionData('sso_user'); 111 | 112 | if (!$username) { 113 | $this->fail('User not authenticated. Session ID: ' . $this->getSessionData('id')); 114 | } 115 | 116 | if (!$user = $this->getUserInfo($username)) { 117 | $this->fail('User not found.'); 118 | } 119 | } catch (SSOServerException $e) { 120 | return $this->returnJson(['error' => $e->getMessage()]); 121 | } 122 | 123 | return $this->returnUserInfo($user); 124 | } 125 | 126 | /** 127 | * Resume broker session if saved session id exist. 128 | * 129 | * @throws SSOServerException 130 | * 131 | * @return void 132 | */ 133 | protected function startBrokerSession() 134 | { 135 | if (isset($this->brokerId)) { 136 | return; 137 | } 138 | 139 | $sessionId = $this->getBrokerSessionId(); 140 | 141 | if (!$sessionId) { 142 | $this->fail('Missing session key from broker.'); 143 | } 144 | 145 | $savedSessionId = $this->getBrokerSessionData($sessionId); 146 | 147 | if (!$savedSessionId) { 148 | $this->fail('There is no saved session data associated with the broker session id.'); 149 | } 150 | 151 | $this->startSession($savedSessionId); 152 | 153 | $this->brokerId = $this->validateBrokerSessionId($sessionId); 154 | } 155 | 156 | /** 157 | * Check if broker session is valid. 158 | * 159 | * @param string $sessionId Session id from the broker. 160 | * 161 | * @throws SSOServerException 162 | * 163 | * @return string 164 | */ 165 | protected function validateBrokerSessionId(string $sessionId) 166 | { 167 | $matches = null; 168 | 169 | if (!preg_match('/^SSO-(\w*+)-(\w*+)-([a-z0-9]*+)$/', $this->getBrokerSessionId(), $matches)) { 170 | $this->fail('Invalid session id'); 171 | } 172 | 173 | if ($this->generateSessionId($matches[1], $matches[2]) != $sessionId) { 174 | $this->fail('Checksum failed: Client IP address may have changed'); 175 | } 176 | 177 | return $matches[1]; 178 | } 179 | 180 | /** 181 | * Generate session id from session token. 182 | * 183 | * @param string $brokerId 184 | * @param string $token 185 | * 186 | * @throws SSOServerException 187 | * 188 | * @return string 189 | */ 190 | protected function generateSessionId(string $brokerId, string $token) 191 | { 192 | $broker = $this->getBrokerInfo($brokerId); 193 | 194 | if (!$broker) { 195 | $this->fail('Provided broker does not exist.'); 196 | } 197 | 198 | return 'SSO-' . $brokerId . '-' . $token . '-' . hash('sha256', 'session' . $token . $broker['secret']); 199 | } 200 | 201 | /** 202 | * Generate session id from session token. 203 | * 204 | * @param string $brokerId 205 | * @param string $token 206 | * 207 | * @throws SSOServerException 208 | * 209 | * @return string 210 | */ 211 | protected function generateAttachChecksum($brokerId, $token) 212 | { 213 | $broker = $this->getBrokerInfo($brokerId); 214 | 215 | if (!$broker) { 216 | $this->fail('Provided broker does not exist.'); 217 | } 218 | 219 | return hash('sha256', 'attach' . $token . $broker['secret']); 220 | } 221 | 222 | /** 223 | * Do things if attaching was successful. 224 | * 225 | * @return void 226 | */ 227 | protected function attachSuccess() 228 | { 229 | $this->redirect(); 230 | } 231 | 232 | /** 233 | * If something failed, throw an Exception or redirect. 234 | * 235 | * @param null|string $message 236 | * @param bool $isRedirect 237 | * @param null|string $url 238 | * 239 | * @throws SSOServerException 240 | * 241 | * @return void 242 | */ 243 | protected function fail(?string $message, bool $isRedirect = false, ?string $url = null) 244 | { 245 | if (!$isRedirect) { 246 | throw new SSOServerException($message); 247 | } 248 | 249 | $this->redirect($url, ['sso_error' => $message]); 250 | } 251 | 252 | /** 253 | * Redirect to provided URL with query string. 254 | * 255 | * If $url is null, redirect to url which given in 'return_url'. 256 | * 257 | * @param string|null $url URL to be redirected. 258 | * @param array $parameters HTTP query string. 259 | * @param int $httpResponseCode HTTP response code for redirection. 260 | * 261 | * @return mixed 262 | */ 263 | abstract protected function redirect(?string $url = null, array $parameters = [], int $httpResponseCode = 307); 264 | 265 | /** 266 | * Returning json response for the broker. 267 | * 268 | * @param null|array $response Response array which will be encoded to json. 269 | * @param int $httpResponseCode HTTP response code. 270 | * 271 | * @return string 272 | */ 273 | abstract protected function returnJson(?array $response = null, int $httpResponseCode = 204); 274 | 275 | /** 276 | * Authenticate using user credentials 277 | * 278 | * @param string $username 279 | * @param string $password 280 | * 281 | * @return bool|array 282 | */ 283 | abstract protected function authenticate(string $username, string $password); 284 | 285 | /** 286 | * Get the secret key and other info of a broker 287 | * 288 | * @param string $brokerId 289 | * 290 | * @return null|array 291 | */ 292 | abstract protected function getBrokerInfo(string $brokerId); 293 | 294 | /** 295 | * Get the information about a user 296 | * 297 | * @param string $username 298 | * 299 | * @return array|object|null 300 | */ 301 | abstract protected function getUserInfo(string $username); 302 | 303 | /** 304 | * Returning user info for broker. Should return json or something like that. 305 | * 306 | * @param array|object $user Can be user object or array. 307 | * 308 | * @return mixed 309 | */ 310 | abstract protected function returnUserInfo($user); 311 | 312 | /** 313 | * Return session id sent from broker. 314 | * 315 | * @return null|string 316 | */ 317 | abstract protected function getBrokerSessionId(); 318 | 319 | /** 320 | * Start new session when user visits server. 321 | * 322 | * @return void 323 | */ 324 | abstract protected function startUserSession(); 325 | 326 | /** 327 | * Set session data 328 | * 329 | * @param string $key 330 | * @param null|string $value 331 | * 332 | * @return void 333 | */ 334 | abstract protected function setSessionData(string $key, ?string $value = null); 335 | 336 | /** 337 | * Get data saved in session. 338 | * 339 | * @param string $key 340 | * 341 | * @return null|string 342 | */ 343 | abstract protected function getSessionData(string $key); 344 | 345 | /** 346 | * Start new session with specific session id. 347 | * 348 | * @param $sessionId 349 | * 350 | * @return void 351 | */ 352 | abstract protected function startSession(string $sessionId); 353 | 354 | /** 355 | * Save broker session data to cache. 356 | * 357 | * @param string $brokerSessionId 358 | * @param string $sessionData 359 | * 360 | * @return void 361 | */ 362 | abstract protected function saveBrokerSessionData(string $brokerSessionId, string $sessionData); 363 | 364 | /** 365 | * Get broker session data from cache. 366 | * 367 | * @param string $brokerSessionId 368 | * 369 | * @return null|string 370 | */ 371 | abstract protected function getBrokerSessionData(string $brokerSessionId); 372 | } 373 | --------------------------------------------------------------------------------