/** * Authenticates the OAuth client. * * This function detects whether credentials for an OAuth client is * presented in the `Authorization` header or the POST body. */ public function initClient() { $this->logger->log(LogLevel::DEBUG, 'SimpleID\\Protocols\\OAuth\\OAuthManager->initClient'); $store = StoreManager::instance(); $request = new Request(); $header = $request->getAuthorizationHeader(true); if ($header != null && $header['#scheme'] == 'Basic') { $client_auth_method = 'client_secret_basic'; $client_id = $header['#username']; $client_secret = $header['#password']; } if (!$client_id && $this->f3->exists('POST.client_id') && $this->f3->exists('POST.client_secret')) { $client_auth_method = 'client_secret_post'; $client_id = $this->f3->get('POST.client_id'); $client_secret = $this->f3->get('POST.client_secret'); } if ($client_id) { $client = $store->loadClient($client_id, 'SimpleID\\Protocols\\OAuth\\OAuthClient'); if ($client['oauth']['client_secret'] != $client_secret) { return; } $this->client_auth_method = $client_auth_method; } else { $results = $this->mgr->invokeAll('oAuthInitClient', $request); $results = array_merge(array_diff($results, array(NULL))); if (count($results) == 1) { $client = $results[0]['#client']; $this->client_auth_method = $results[0]['#client_auth_method']; } } $this->f3->set('oauth_client', $client); $this->logger->log(LogLevel::INFO, 'OAuth client: ' . $client_id . ' [' . $this->client_auth_method . ']'); }
/** * Returns the user's OpenID 2.0 verification page. * * @see SimpleID\Base\UserModule::user() */ public function userJSON($f3, $params) { $mgr = ModuleManager::instance(); $iss = $mgr->getModule('SimpleID\\Protocols\\Connect\\ConnectModule')->getCanonicalHost(); $store = StoreManager::instance(); $user = $store->loadUser($params['uid']); if ($user != NULL) { header('Content-Type: application/json'); print json_encode(array('iss' => $iss)); } else { $this->f3->status(404); $this->fatalError($this->t('User %uid not found.', array('%uid' => $uid))); } }
/** * Attempts to automatically login using the client certificate * * @return SimpleID\Models\User the user object, or NULL */ public function autoAuthHook() { if (!$this->hasClientCert()) { return NULL; } $cert = trim($_SERVER['SSL_CLIENT_M_SERIAL']) . ';' . trim($_SERVER['SSL_CLIENT_I_DN']); $this->logger->log(LogLevel::DEBUG, 'Client SSL certificate: ' . $cert); $store = StoreManager::instance(); $user = $store->findUser('cert.certs', $cert); if ($user != NULL) { $this->logger->log(LogLevel::DEBUG, 'Client SSL certificate accepted for ' . $user['uid']); return $user; } else { $this->logger->log(LogLevel::DEBUG, 'Client SSL certificate presented, but no user with that certificate exists.'); return NULL; } }
/** * Attempts to automatically login using the auto login cookie * * @see SimpleID\API\AuthHooks::autoAuthHook() * @return SimpleID\Models\User the user object, or NULL */ public function autoAuthHook() { if (!$this->f3->exists('COOKIE.' . $this->cookie_name)) { return null; } $cookie = $this->f3->get('COOKIE.' . $this->cookie_name); $token = new SecurityToken(); $data = $token->getPayload($cookie); if ($data === null) { $this->logger->log(LogLevel::NOTICE, 'Automatic login: Invalid token - clearing'); $this->logoutHook(); return null; } if ($data['typ'] != 'rememberme') { return NULL; } $this->logger->log(LogLevel::DEBUG, 'Automatic login token detected', $data); if ($data['exp'] < time()) { // Cookie expired $this->logger->log(LogLevel::NOTICE, 'Automatic login: Expired - clearing'); $this->logoutHook(); return NULL; } if ($data['uaid'] != $this->auth->assignUAID()) { $this->logger->log(LogLevel::WARNING, 'Automatic login: User agent ID does not match - clearing'); $this->logoutHook(); return NULL; } $store = StoreManager::instance(); // Load the user, tag it as an auto log in $test_user = $store->loadUser($data['uid']); if ($test_user != NULL) { $this->logger->log(LogLevel::INFO, 'Automatic login token accepted for ' . $data['uid']); return $test_user; } else { $this->logger->log(LogLevel::WARNING, 'Automatic login token accepted for ' . $data['uid'] . ', but no such user exists'); return NULL; } }
/** * Returns the user's public page. * * @param string $uid the user ID */ function user($f3, $params) { $web = \Web::instance(); $tpl = \Template::instance(); $store = StoreManager::instance(); $mgr = ModuleManager::instance(); $this->f3->set('title', $this->t('User Page')); if (!isset($params['uid'])) { $this->f3->status(400); $this->f3->set('message', $this->t('No user specified.')); } else { $user = $store->loadUser($params['uid']); if ($user == NULL) { $this->f3->status(404); $this->f3->set('message', $this->t('User %uid not found.', array('%uid' => $params['uid']))); } else { header('Vary: Accept'); $content_type = $web->acceptable(array('text/html', 'application/xml', 'application/xhtml+xml', 'application/xrds+xml', 'application/json')); if ($content_type == 'application/xrds+xml' && $mgr->isModuleLoaded('SimpleID\\Protocols\\OpenID\\OpenIDModule')) { $mgr->getModule('SimpleID\\Protocols\\OpenID\\OpenIDModule')->userXRDS($f3, $params); return; } elseif ($content_type == 'application/json' && $mgr->isModuleLoaded('SimpleID\\Protocols\\Connect\\OpenID2MigrationModule')) { $mgr->getModule('SimpleID\\Protocols\\Connect\\OpenID2MigrationModule')->userJSON($f3, $params); } else { $xrds_location = $this->getCanonicalURL('@openid_user_xrds'); header('X-XRDS-Location: ' . $xrds_location); $this->f3->set('message', $this->t('This is the user %uid\'s SimpleID page. It contains hidden information for the use by OpenID consumers.', array('%uid' => $params['uid']))); $this->f3->set('title', $user['uid']); $this->f3->set('xrds', $xrds_location); if ($user->hasLocalOpenIDIdentity()) { $this->f3->set('local_id', $user["identity"]); } $this->f3->set('head', 'openid_head.html'); } } } print $tpl->render('page.html'); }
/** * Gets the site-specific encryption and signing key. * * If the key does not exist, it is automatically generated. * * @return string the site-specific encryption and signing key * as a base64url encoded string */ protected static function getKey() { $store = StoreManager::instance(); $key = $store->getSetting('oauth-fernet'); if ($key == NULL) { $rand = new Random(); $key = Fernet::base64url_encode($rand->bytes(32)); $store->setSetting('oauth-fernet', $key); } return $key; }
/** * Returns the OAuth authorization that generated this code. * * If the authorization has been revoked, or is otherwise invalid, since * the authorization code has been issued, this function will return * `null`. * * @return Authorization the OAuth authorization or `null`. */ public function getAuthorization() { $store = StoreManager::instance(); $authorization = $store->loadAuth($this->aid); if ($authorization == null) { return null; } if ($authorization->getAuthState() != $this->auth_state) { return null; } return $authorization; }
/** * Creates an instance of the store module. * * The default constructor registers the store module with the * store manager by calling {@link StoreManager::addStore()}. */ public function __construct() { parent::__construct(); $store_manager = StoreManager::instance(); $store_manager->addStore($this, $this->getStores()); }
/** * Verifies a set of credentials using the default user name-password authentication * method. * * @param string $uid the name of the user to verify * @param array $credentials the credentials supplied by the browser * @return bool whether the credentials supplied matches those for the specified * user */ protected function verifyCredentials($uid, $credentials) { $store = StoreManager::instance(); $test_user = $store->loadUser($uid); if ($test_user == NULL) { return false; } list($dummy, $prefix, $content) = explode('$', $test_user['password']['password'], 3); if ($prefix == null) { return false; } switch ($prefix) { case '2y': $bcrypt = Bcrypt::instance(); return $bcrypt->verify($credentials['password']['password'], $test_user['password']['password']); break; case 'pbkdf2': $params = array(); list($param_string, $hash, $salt) = explode('$', $content, 3); parse_str($param_string, $params); if (!isset($params['f'])) { $params['f'] = 'sha256'; } if (!isset($params['dk'])) { $params['dk'] = 0; } return $this->secureCompare(PBKDF2::hash($params['f'], $credentials['password']['password'], base64_decode($salt), $params['c'], $params['dk'], true), base64_decode($hash)); break; default: $this->logger->log(LogLevel::WARNING, 'Unknown password prefix: ' . $prefix); return false; } }
/** * Sets the current version of SimpleID. * * This function sets the version application setting via {@link \SimpleID\Store\StoreManager::setSetting()}. * A specific version can be specified, or it can be taken from {@link SIMPLEID_VERSION}. * * @param string $version the version to set */ public function setVersion($version = NULL) { $store = StoreManager::instance(); if ($version == NULL) { $version = SIMPLEID_VERSION; } $store->setSetting('version', $version); }
/** * Configuration endpoint */ public function get() { $this->checkHttps('error'); $client_id = $this->f3->get('PARAMS.client_id'); if (!$this->isAuthorized(self::CLIENT_REGISTRATION_ACCESS_SCOPE) || $this->getAccessToken()->getAuthorization()->getClient()->getStoreID() != $client_id) { $this->unauthorizedError('invalid_token'); return; } $store = StoreManager::instance(); $client = $store->loadClient($client_id); if ($client == NULL || !is_a($client, 'SimpleID\\Protocols\\Connect\\ConnectDynamicClient')) { $this->f3->status(404); $this->fatalError($this->t('Client not found.')); return; } header('Content-Type: application/json'); header('Content-Disposition: inline'); print json_encode($client->getDynamicClientInfo()); }
/** * Gets the site-specific encryption and signing key. * * If the key does not exist, it is automatically generated. * * @return string the site-specific encryption and signing key * as a base64url encoded string */ private static function getSiteToken() { $store = StoreManager::instance(); $site_token = $store->getSetting('site-token'); if ($site_token == NULL) { $rand = new Random(); $site_token = Fernet::base64url_encode($rand->bytes(32)); $store->setSetting('site-token', $site_token); } return $site_token; }
/** * @see SimpleID\API\OAuthHooks::oAuthGrantAuthHook() */ function oAuthGrantAuthHook($authorization, $request, $response, $scopes) { // code: ?code / id_token // id_token: #id_token // id_token token: #access_token #id_token // code id_token: #code #id_token[c_hash] / id_token // code token: #code #access_token / id_token // code id_token token: #code #access_token #id_token[c_hash, at_hash] / id_token if ($request->paramContains('scope', 'openid')) { $user = AuthManager::instance()->getUser(); $client = StoreManager::instance()->loadClient($request['client_id'], 'SimpleID\\Protocols\\OAuth\\OAuthClient'); if (isset($request['claims']) && is_string($request['claims'])) { $request['claims'] = json_decode($request['claims'], true); } // 1. Build claims $claims_requested = isset($request['claims']['id_token']) ? $request['claims']['id_token'] : null; $claims = $this->buildClaims($user, $client, 'id_token', $scopes, $claims_requested); if (isset($request['nonce'])) { $claims['nonce'] = $request['nonce']; } // 2. Encode claims as jwt if ($request->paramContains('response_type', 'id_token')) { // response_type = id_token, code id_token, id_token token, code id_token token // Response is always fragment $response->setResponseMode(Response::FRAGMENT_RESPONSE_MODE); // Build authorisation response ID token $jose = new JOSEResponse($this->getCanonicalHost(), $client, 'connect.id_token', $claims, 'RS256'); if (isset($response['code'])) { $jose->setShortHashClaim('c_hash', $response['code']); } if (isset($response['access_token'])) { $jose->setShortHashClaim('at_hash', $response['access_token']); } $response['id_token'] = $jose->buildJOSE(); } if ($request->paramContains('response_type', 'code')) { // response_type = code, code token // Save the id token for token endpoint $authorization->additional['id_token_claims'] = $claims; } // Note response_type = token is not defined // 3. Save claims for UserInfo endpoint if (isset($request['claims'])) { $authorization->additional['claims'] = $request['claims']; } } }
/** * Returns the user's public XRDS page. */ public function userXRDS($f3, $params) { $store = StoreManager::instance(); $user = $store->loadUser($params['uid']); if ($user != NULL) { $tpl = new \Template(); if ($user->hasLocalOpenIDIdentity()) { $this->f3->set('local_id', $user["identity"]); } header('Content-Disposition: inline; filename=yadis.xml'); print $tpl->render('openid_user_xrds.xml', 'application/xrds+xml'); } else { $this->f3->status(404); $this->fatalError($this->t('User %uid not found.', array('%uid' => $uid))); } }
/** @see SimpleID\API\MyHooks::revokeAppHook() */ public function revokeAppHook($cid) { $auth = AuthManager::instance(); $store = StoreManager::instance(); $user = $auth->getUser(); $client = $store->loadClient($cid, 'SimpleID\\Protocols\\OAuth\\OAuthClient'); $aid = Authorization::buildID($user, $client); $authorization = $store->loadAuth($aid); if ($authorization != null) { $authorization->revokeAllTokens(); $store->deleteAuth($authorization); } }
/** * Gets the site-specific key for generating identifiers. * * If the key does not exist, it is automatically generated. * * @return string site-specific key as a binary string */ private static function getOpaqueToken() { $store = StoreManager::instance(); $opaque_token = $store->getSetting('opaque-token'); if ($opaque_token == NULL) { $rand = new Random(); $opaque_token = $rand->bytes(16); $store->setSetting('opaque-token', base64_encode($opaque_token)); } else { $opaque_token = base64_decode($opaque_token); } return $opaque_token; }
/** * @see SimpleID\API\AuthHooks::loginHook() */ public function loginHook($user, $level, $modules, $form_state) { $auth = AuthManager::instance(); $store = StoreManager::instance(); if ($level >= AuthManager::AUTH_LEVEL_VERIFIED && isset($form_state['otp_remember']) && $form_state['otp_remember'] == 1) { $uaid = $auth->assignUAID(); if (!isset($user->auth[$uaid])) { $user->auth[$uaid] = array(); } if (!isset($user->auth[$uaid]['otp'])) { $user->auth[$uaid]['otp'] = array(); } $user->auth[$uaid]['otp']['remember'] = true; $store->saveUser($user); } }
public function delete($f3, $params) { $this->checkHttps('error', true); parse_str($this->f3->get('BODY'), $delete); header('Content-Type: application/json'); $token = new SecurityToken(); if (!isset($delete['tk']) || !$token->verify($delete['tk'], 'apps')) { $this->f3->status(401); print json_encode(array('error' => 'unauthorized', 'error_description' => $this->t('Unauthorized'))); return; } $auth = AuthManager::instance(); $user = $auth->getUser(); $prefs =& $user->clients; if (!isset($prefs[$params['cid']])) { $this->f3->status(404); print json_encode(array('error' => 'not_found', 'error_description' => $this->t('Not found'))); return; } $mgr = ModuleManager::instance(); $mgr->invokeAll('revokeApp', $params['cid']); unset($prefs[$params['cid']]); $store = StoreManager::instance(); $store->saveUser($user); print json_encode(array('result' => 'success', 'result_description' => $this->t('App has been deleted.'))); }
/** * Returns a Storable object based on a reference. * * A reference of a Storable object is its store type (from * {@link SimpleID\Store\Storable::getStoreType()}) and its ID (from * {@link SimpleID\Store\Storable::getStoreID()}), separated by a colon. * * @param string $ref the reference to the storable object * @param array $args additional parameters * @return Storable the storable object or null */ protected function getStorable($ref, $args = array()) { $store = StoreManager::instance(); $f3 = \Base::instance(); list($type, $id) = explode(':', $ref, 2); array_unshift($args, $id); return call_user_func_array(array($store, 'load' . ucfirst($f3->camelCase($type))), $args); }
/** * Creates a JRD document based on a SimpleID user. * * The JRD document created is very simple - it merely points to the * SimpleID installation as the OpenID connect provider. * * @param array $resource the resource identifier * @return array the JRD document */ protected function getJRD($resource) { $store = StoreManager::instance(); $criteria = $this->getResourceCriteria($resource); if ($criteria == null) { return null; } foreach ($criteria as $criterion => $value) { $user = $store->findUser($criterion, $value); if ($user != null) { break; } } if ($user == null) { return null; } $jrd = array('subject' => $user['identity'], 'links' => array(array('rel' => 'http://specs.openid.net/auth/2.0/provider', 'href' => rtrim($this->f3->get('config.canonical_base_path'), '/')), array('rel' => 'http://openid.net/specs/connect/1.0/issuer', 'href' => rtrim($this->f3->get('config.canonical_base_path'), '/')))); if (isset($user['aliases'])) { if (is_array($user['aliases'])) { $jrd['aliases'] = $user['aliases']; } else { $jrd['aliases'] = array($user['aliases']); } } return $jrd; }
/** * Sets the user specified by the parameter as the active user. * * @param SimpleID\Models\User $user the user to log in * @param int $level the level of authentication achieved in this * session * @param array $modules array of authentication modules used to * authenticate the user in this session * @param array $form_state */ public function login($user, $level, $modules = array(), $form_state = array()) { $store = StoreManager::instance(); if (is_string($user)) { $user = $store->loadUser($user); } $this->f3->set('user', $user); $this->auth_info['uid'] = $user['uid']; $this->auth_info['level'] = $level; $this->auth_info['modules'] = $modules; $this->auth_info['time'] = time(); if ($level >= self::AUTH_LEVEL_AUTO) { $_SESSION['auth'] = $this->auth_info; $this->cache->set(rawurlencode($user['uid']) . '.login', session_id()); $this->assignUALoginState(true); } if ($level > self::AUTH_LEVEL_AUTO) { if (!isset($form_state['auth_skip_activity'])) { $activity = array('type' => 'browser', 'level' => $level, 'modules' => $modules, 'time' => $_SESSION['auth']['time']); if ($this->f3->exists('IP')) { $activity['remote'] = $this->f3->get('IP'); } if ($this->f3->exists('HEADERS.User-Agent')) { $activity['ua'] = $this->f3->get('HEADERS.User-Agent'); } $user->addActivity($this->assignUAID(), $activity); $store->saveUser($user); } $this->logger->log(LogLevel::INFO, 'Login successful: ' . $user['uid']); } $this->mgr->invokeAll('login', $user, $level, $modules, $form_state); }