Open Herz3h opened 7 years ago
Here is an attempt to do what i want:
if ($requestedScope) {
186 // validate the client has access to this scope
187 if ($clientScope = $this->clientStorage->getClientScope($clientId)) {
188 if (!$this->scopeUtil->checkScope($requestedScope, $clientScope)) {
189 $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this client');
190
191 return false;
192 }
193 } elseif (!$this->scopeUtil->scopeExists($requestedScope)) {
194 $response->setError(400, 'invalid_scope', 'An unsupported scope was requested');
195
196 return null;
197 }
198 /* // validate the requested scope */
199 /* if ($availableScope) { */
200 /* if (!$this->scopeUtil->checkScope($requestedScope, $availableScope)) { */
201 /* $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this request' . $availableScope); */
202
203 /* return null; */
204 /* } */
205 /* } else { */
206 /* } */
207 }
Not sure what to think about this...
Hi @Herz3h , I also had this problem and found that scopes did not provide the level of granularity I needed. Since I am using doctrine2 as my DBAL, Instead I ended up implementing separate ACL functionality using https://github.com/myclabs/ACL. I then use my own custom mapping to tie user scopes to individual ACLs. I hope this helps.
Hey @clubery,
Thanks for the reply. But i don't see how does this integrate with oauth2 and scopes :/
The thing is i started implementing oauth2, thinking scopes are a way of telling what a user can do (in context of user) and what an app requires for someone to use it (in case of a client) but now i realize scope are used to let user limit what a client can do.
Maybe i'm using oauth2 and it doesn't fit my problem (not sure about this, maybe there is solution to what i need with oauth2) and in such case what alternative would solve my need ?
@Herz3h It doesn't integrate, you have to do it yourself. What I did was create a hash containing my scopes that map to the names of specific ACLs in my application. Upon successful authorization I then retrieve the users scopes and it gives me a set of ACLs that user has access to, from then on for the lifetime of the request, I never check the user scopes and refer to the ACLs for permission checks.
I happen to be doing this in an API where I expose the given ACLs to the user after authentication. This allows me to then utilise this control on the front end and since I am using angular, i modify the data structure from my API and pass them directly into angular-permission https://github.com/Narzerus/angular-permission which then allows me to show different elements of the page based on the users permission.
This is not simple and took me about a week to implement.
So from what i understand any user can authenticate, and its only after retrieving its scopes in the resource server that you tell wether he can access a specific resource by using Scope <-> ACL hash, right ?
What i need is to check permission in the authorization server, so that only a user who has same scope as client scope (understand here: right to use a client) can get a token....but then this probably doesn't conform to oauth2 scopes way of working i guess..
So from what i understand any user can authenticate, and its only after retrieving its scopes in the resource server that you tell wether he can access a specific resource by using Scope <-> ACL hash, right ?
Almost correct, the scopes won't come from the resource server, they will come from the oauth server when you issue the access token.
What i need is to check permission in the authorization server, so that only a user who has same scope as client scope (understand here: right to use a client) can get a token....but then this probably doesn't conform to oauth2 scopes way of working i guess..
The oauth server should only allow requested scopes that are associated with the client anyway, that behaviour comes out of the box
That's strange then. I have this layout atm:
ClientA with scope A
UserBob with scope B
If i try to get a token using password (user credentials) grant_type using UserBob username and password and sending the scope A i get this :
{"error":"invalid_scope","error_description":"The scope requested is invalid for this request"}
And if i try with UserBob (like before) by sending the scope B i get a token which doesn't make sense then 😮
This is using clientA client_id and client_secret in both cases of course.
So I would expect here that the actual clientA has been configured to allow scope A and also has the 'password' grant_type configured.
I did this in the table that holds my oauth client data. I haven't done anything special to get this to work
Yes clientA has only the "password" grant and the "scope A" in scopes. So i'm confused now, is what i described in previous post normal behaviour then?
I have a separate class in my application that implements some of the required interfaces I need to get the oauth server to work. It also has code in there to deal with my own API which you can ignore but here it is:
<?php
namespace MMMusic;
use Exception;
use DateTime;
use DateInterval;
use Utils;
use Store;
use OAuthAccessToken;
use OAuthRefreshToken;
use OAuthClient;
use User as OAuthUser;
use OAuthScope;
use OAuthAuthorizationCode;
use OAuthClientKeys as KeyPair;
use OAuth2\Storage\AccessTokenInterface;
use OAuth2\Storage\ClientCredentialsInterface;
use OAuth2\Storage\UserCredentialsInterface;
use OAuth2\Storage\PublicKeyInterface;
use OAuth2\Storage\RefreshTokenInterface;
use OAuth2\OpenID\Storage\UserClaimsInterface;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface;
use OAuth2\ScopeInterface;
use OAuth2\RequestInterface;
use OAuth2\HttpFoundationBridge\Request as BridgeRequest;
use Exceptions\AccountBlockedException;
use Exceptions\AccountNotVerifiedException;
class OAuth implements
AccessTokenInterface,
ClientCredentialsInterface,
AuthorizationCodeInterface,
UserCredentialsInterface,
PublicKeyInterface,
ScopeInterface,
RefreshTokenInterface,
UserClaimsInterface
{
private $store;
private $log;
private $oauth_user;
public function setLogger($logger) {
$this->log = $logger;
}
public function getLogger() {
return $this->log;
}
/**
* The default scope to use in the event the client
* does not request one. By returning "false", a
* request_error is returned by the server to force a
* scope request by the client. By returning "null",
* opt out of requiring scopes
*
* @param $client_id
* An optional client id that can be used to return customized default scopes.
*
* @return
* string representation of default scope, null if
* scopes are not defined, or false to force scope
* request by the client
*
* ex:
* 'default'
* ex:
* null
*/
public function getDefaultScope($client_id = null) {
$scopes = $this->store()->entityManager()->getRepository('OAuthScope')->findBy(['isDefault' => 1]);
$_default_scopes = array_map(function($scope) {
return $scope->getScope();
}, $scopes);
$default_scope = implode(' ', $_default_scopes);
return $default_scope;
}
/**
* Check if the provided scope exists.
*
* @param $scope
* A space-separated string of scopes.
*
* @return
* TRUE if it exists, FALSE otherwise.
*/
public function scopeExists($scope) {
$scopecheck = explode(' ', $scope);
$qb = $this->store()->entityManager()->createQueryBuilder();
$query = $qb->select($qb->expr()->count('s.scope'))
->from('OAuthScope', 's')
->andWhere('s.scope IN (:scopecheck)')
->setParameter('scopecheck', $scopecheck);
$count = $query->getQuery()->getSingleScalarResult();
return ($count == count($scopecheck));
}
/**
* Check if everything in required scope is contained in available scope.
*
* @param $required_scope
* A space-separated string of scopes.
*
* @return
* TRUE if everything in required scope is contained in available scope,
* and FALSE if it isn't.
*
* @see http://tools.ietf.org/html/rfc6749#section-7
*
* @ingroup oauth2_section_7
*/
public function checkScope($required_scope, $available_scope) {
$_required_scopes = explode(" ", $required_scope);
$_allowed_scopes = explode(" ", $available_scope);
foreach($_required_scopes as $r_scope) {
if(!in_array($r_scope, $_allowed_scopes))
return false;
}
return true;
}
/**
* Return scope info from request
*
* @param OAuth2\RequestInterface
* Request object to check
*
* @return
* string representation of requested scope
*/
public function getScopeFromRequest(RequestInterface $req) {
return $req->query->get('scope')?: ( $req->request->get('scope')?: null );
}
public function __construct(Store $_store, $logger = null) {
$this->setLogger($logger);
$this->store($_store);
}
private function store($store = null) {
if($store)
$this->store = $store;
return $this->store;
}
/// CLIENT STUFF
public function registerClient(
$user_id,
$email,
$password,
$redirect_uri,
$description,
$client_name,
$client_id,
$client_secret,
$grant_types = NULL,
$scope,
$autoflush = true
) {
if($scope && (!$this->scopeExists($scope)))
throw new Exception("Client registration with invalid scope: $scope");
$_OAuthClient = $this->_getClient($client_id);
if($_OAuthClient)
throw new Exception("OAuth Client with id '$client_id' already exists");
// this will throw an exception if it fails
// we need a way of marking this user as the admin user
$_OAuthUser = $this->createUser($user_id, $email, $password, null, null, $scope, false);
$_OAuthClient = new OAuthClient();
$_OAuthClient->setClientName($client_name);
$_OAuthClient->setClientId($client_id);
$_OAuthClient->setClientSecret($client_secret);
$_OAuthClient->setRedirectUri($redirect_uri);
$_OAuthClient->setDescription($description);
if($scope)
$_OAuthClient->setScope($scope);
if($grant_types)
$_OAuthClient->setGrantTypes($grant_types);
// associate new user with this client
$_OAuthUser->addClient($_OAuthClient);
$this->store()->entityManager()->persist($_OAuthClient);
$this->store()->entityManager()->persist($_OAuthUser);
if($autoflush)
$this->store()->entityManager()->flush();
return $_OAuthClient;
}
public function checkClientCredentials($client_id, $client_secret = NULL) {
$_OAuthClient = $this->_getClient($client_id);
return ($_OAuthClient && $_OAuthClient->getClientSecret() == $client_secret);
}
public function checkRestrictedGrantType($client_id, $grant_type) {
$_OAuthClient = $this->_getClient($client_id);
if(!$_OAuthClient)
return false;
$_grant_types = $_OAuthClient->getGrantTypes();
if(isset($_grant_types)) {
$grant_types = explode(' ', $_grant_types);
return in_array($grant_type, (array) $grant_types);
}
// if grant_types are not defined, then none are restricted
return true;
}
public function getClientDetails($client_id, $all = false) {
$_OAuthClient = $this->_getClient($client_id);
if(!$_OAuthClient)
return NULL;
// use
// this ALL needs to change to use the user client association
// but what user are you getting this user info for?
// should we be going for the default user in the user client association?
// or can we get away with not passing back the specific user details at all
// and then change all calls to this method to rely on getting user details
// from the user object, this is the preferred method
$client_details = [
'app_name' => $_OAuthClient->getName(),
'redirect_uri' => $_OAuthClient->getRedirectUri(),
'client_id' => $_OAuthClient->getClientId(),
'grant_types' => $_OAuthClient->getGrantTypes(),
'user_id' => $_OAuthClient->getUserId(), // this should always be null since we aint using it no mo
'scope' => $_OAuthClient->getScope(),
'description' => $_OAuthClient->getDescription()
];
if($all) {
$client_details['client_secret'] = $_OAuthClient->getClientSecret();
$client_details['email'] = $_OAuthClient->getEmail();
$client_details['password'] = $_OAuthClient->getPassword();
}
return $client_details;
}
public function updateAppSettings(OAuthClient $client, $app_settings) {
if(isset($app_settings['app_name']))
$client->setName($app_settings['app_name']);
if(isset($app_settings['redirect_uri']))
$client->setRedirectUri($app_settings['redirect_uri']);
if(isset($app_settings['description']))
$client->setDescription($app_settings['description']);
// throw new Exception("shitter - " . $app_settings['redirect_uri']);
$this->store()->entityManager()->persist($client);
$this->store()->entityManager()->flush();
}
public function updateLastLogin($username) {
$user = $this->getUserByUsername($username);
if(!$user)
throw new Exception("Could not load user '$username' by username");
$user->setDateTimeLastLogin(new DateTime());
$this->store()->entityManager()->persist($user);
$this->store()->entityManager()->flush();
}
public function getUserByUsername($username) {
//fwrite (STDERR, "getting xxxuserdetails for $username \n");
return $this->store()->entityManager()->getRepository('User')->findOneBy(['username' => $username]);
}
public function getUserByEmail($email) {
return $this->store()->entityManager()->getRepository('User')->findOneBy(['email' => $email]);
}
public function createUser($username, $email, $password, $first_name = null, $last_name = null, $scope = null, $autoflush = true) {
$qb = $this->store()->entityManager()->getRepository('User')->createQueryBuilder('u');
$qb->select('u')
->where($qb->expr()->orX(
$qb->expr()->eq('u.username', ':username'),
$qb->expr()->eq('u.email', ':email')
))
->setParameter('username', $username)
->setParameter('email', $email);
$query = $qb->getQuery();
$existing_user = $query->getResult();
if($existing_user)
throw new Exception("User with username:$username or email:$email already exists");
$_OAuthUser = new OAuthUser();
$_OAuthUser->setUsername($username);
$_OAuthUser->setEmail($email);
$_OAuthUser->setPassword($password, false, true);
$_OAuthUser->setFirstName($first_name);
$_OAuthUser->setLastName($last_name);
$_OAuthUser->setScope($scope);
$_OAuthUser->setIsVerified(false);
$_OAuthUser->setCreated(new DateTime());
$this->store()->entityManager()->persist($_OAuthUser);
if($autoflush)
$this->store()->entityManager()->flush();
return $_OAuthUser;
}
private function _getClient($client_id) {
return $this->store()->entityManager()->find('OAuthClient', $client_id);
}
public function isPublicClient($client_id) {
$_OAuthClient = $this->_getClient($client_id);
// return false as the client with this id doesn't exist
if(! $_OAuthClient)
return false;
return empty($_OAuthClient->getClientSecret());
}
public function getClientScope($client_id) {
$_OAuthClient = $this->_getClient($client_id);
// return false as the client with this id doesn't exist
if(! $_OAuthClient)
return false;
$scope = $_OAuthClient->getScope();
if(isset($scope))
return $scope;
return null;
}
// ACCESS TOKEN STUFF
public function _getAccessToken($auth_token, $user = null) {
//return $this->store()->entityManager()->getRepository('OAuthAccessToken')->findOneBy(['accessToken' => $auth_token]);
if(!$user) {
return $this->store()
->entityManager()
->createQuery('SELECT t FROM OAuthAccessToken t WHERE t.accessToken = :access_token AND t.revoked IS NULL')
->setParameter('access_token', $auth_token)
->getOneOrNullResult();
}
else {
return $this->store()
->entityManager()
->createQuery('SELECT t FROM OAuthAccessToken t WHERE t.accessToken = :access_token AND t.userId = :username AND t.revoked IS NULL')
->setParameter('access_token', $auth_token)
->setParameter('username', $user)
->getOneOrNullResult();
}
}
// ACCESS TOKEN STUFF
public function _getUserAccessTokens($user) {
return $this->store()
->entityManager()
->createQuery('SELECT t FROM OAuthAccessToken t WHERE t.userId = :username AND t.revoked IS NULL')
->setParameter('username', $user)
// ->getQuery()
->getResult();
}
public function getAccessToken($auth_token) {
$_OAuthAccessToken = $this->_getAccessToken($auth_token);
if(! $_OAuthAccessToken)
return NULL;
return [
'client_id' => $_OAuthAccessToken->getClientId(),
'user_id' => $_OAuthAccessToken->getuserId(),
'expires' => $_OAuthAccessToken->getExpires()->getTimestamp(), //DateTime object
'scope' => $_OAuthAccessToken->getScope(),
];
}
public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = NULL) {
$oAuthClient = $this->_getClient($client_id);
if(!$oAuthClient)
throw new Exception("Invalid client_id: $client_id");
$_OAuthAccessToken = $this->_getAccessToken($oauth_token, $user_id);
if(!$_OAuthAccessToken) {
$_OAuthAccessToken = new OAuthAccessToken();
$_OAuthAccessToken->setAccessToken($oauth_token);
}
//throw new Exception("setting access token for $user_id");
$this->log->addInfo("Setting access token for user: $user_id");
$expires_datetime = new DateTime();
$expires_datetime->setTimestamp($expires);
$realtime = time();
$realtime_datetime = new DateTime();
$realtime_datetime->setTimestamp($realtime);
$_OAuthAccessToken->setClient($oAuthClient);
$_OAuthAccessToken->setUserId($user_id);
$_OAuthAccessToken->setScope($scope);
$_OAuthAccessToken->setExpires($expires_datetime);
$this->store()->entityManager()->persist($_OAuthAccessToken);
$this->store()->entityManager()->flush();
}
public function expireAccessToken($oauth_token) {
$_OAuthAccessToken = $this->_getAccessToken($oauth_token);
if($_OAuthAccessToken) {
$_OAuthAccessToken->setExpires((new Datetime("now"))->sub(new DateInterval("P1D")));
$this->store()->entityManager()->persist($_OAuthAccessToken);
$this->store()->entityManager()->flush();
}
}
private function _revokeAccessToken(OAuthAccessToken $accessToken) {
$accessToken->setExpires((new Datetime("now"))->sub(new DateInterval("P1D")));
$accessToken->setRevoked(new Datetime("now"));
$this->store()->entityManager()->persist($accessToken);
return true;
}
public function revokeAccessToken($oauth_token, $user) {
$_OAuthAccessToken = $this->_getAccessToken($oauth_token, $user);
if(!$_OAuthAccessToken)
return false;
$this->_revokeAccessToken($_OAuthAccessToken);
$this->store()->entityManager()->flush();
return true;
}
public function revokeAllUserAccessTokens($user) {
$userAccessTokens = $this->_getUserAccessTokens($user);
foreach($userAccessTokens AS $accessToken) {
$this->_revokeAccessToken($accessToken);
}
$this->store()->entityManager()->flush();
return true;
}
// AUTHORIZATION CODE STUFF
private function _getAuthorizationCode($code) {
return $this->store()->entityManager()->find('OAuthAuthorizationCode', $code);
}
public function getAuthorizationCode($code) {
$_OAuthAuthorizationCode = $this->_getAuthorizationCode($code);
if(! $_OAuthAuthorizationCode)
return NULL;
return [
'client_id' => $_OAuthAuthorizationCode->getClientId(),
'user_id' => $_OAuthAuthorizationCode->getUserId(),
'expires' => $_OAuthAuthorizationCode->getExpires()->getTimestamp(),
'redirect_uri' => $_OAuthAuthorizationCode->getRedirectUri(),
'scope' => $_OAuthAuthorizationCode->getScope()
];
}
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = NULL, $id_token = null) {
$client = $this->_getClient($client_id);
if(!$client)
throw new Exception("Invalid client_id: $client_id");
$_OAuthAuthorizationCode = $this->_getAuthorizationCode($code);
if(!$_OAuthAuthorizationCode) {
$_OAuthAuthorizationCode = new OAuthAuthorizationCode();
$_OAuthAuthorizationCode->setAuthorizationCode($code);
}
$_OAuthAuthorizationCode->setClientId($client_id);
$_OAuthAuthorizationCode->setUserId($user_id);
$_OAuthAuthorizationCode->setRedirectUri($redirect_uri);
$_OAuthAuthorizationCode->setScope($scope);
$expires_datetime = new DateTime();
$expires_datetime->setTimestamp($expires);
$_OAuthAuthorizationCode->setExpires($expires_datetime);
$this->store()->entityManager()->persist($_OAuthAuthorizationCode);
$this->store()->entityManager()->flush();
}
public function expireAuthorizationCode($code) {
$_OAuthAuthorizationCode = $this->_getAuthorizationCode($code);
if($_OAuthAuthorizationCode) {
$this->store()->entityManager()->remove($_OAuthAuthorizationCode);
$this->store()->entityManager()->flush();
}
}
public function checkUserCredentials($username, $password, $is_cryptedpassword = false) {
if(filter_var($username, FILTER_VALIDATE_EMAIL)) {
$this->oauth_user = $this->getUserByEmail($username);
if(!$this->oauth_user)
return false;
$username = $this->oauth_user->getUsername();
}
$this->log->addInfo("Checking user credentials for '$username' password crypted: " . ($is_cryptedpassword?'yes':'no'));
$crypted_password = $is_cryptedpassword ? $password : hash('sha256', $password );
$this->log->addInfo("Encrypted password $is_cryptedpassword: " . $crypted_password);
$valid_credentials = $this->store()
->entityManager()
->getRepository('User')
->findOneBy([
'username' => $username,
'password' => $crypted_password
]);
if($valid_credentials) {
$this->log->addInfo("Supplied account credentials for '$username' are valid!");
} else {
$this->log->addInfo("Supplied account credentials for '$username' were invalid! login failure.");
}
if($valid_credentials && $valid_credentials->getIsBlocked()) {
$this->log->addWarning("Account for '$username' is blocked!");
throw new AccountBlockedException("account is blocked");
}
if($valid_credentials && (!$valid_credentials->getIsVerified())) {
$this->log->addWarning("Account for '$username' is not verified!");
throw new AccountNotVerifiedException("account not verified");
}
return (bool)$valid_credentials;
}
public function getUserDetails($username) {
$this->log->addInfo("Getting userdetails for: $username");
$oauth_user = filter_var($username, FILTER_VALIDATE_EMAIL) ?
$this->oauth_user = $this->getUserByEmail($username) :
$this->getUserByUsername($username);
if(!$oauth_user)
return NULL;
return [
'user_id' => $oauth_user->getUsername(),
'scope' => $oauth_user->getScope(),
];
}
public function setClientKeys(OAuthClient $client, $private_key, $public_key, $algorithm) {
$keypair = new KeyPair();
$keypair->setClientId( $client->getClientId() );
$keypair->setEncryptionAlgorithm( $algorithm );
$keypair->setPublicKey( $public_key );
$keypair->setPrivateKey( $private_key );
$this->store()->entityManager()->persist($keypair);
$this->store()->entityManager()->flush();
}
private function _getClientKeys($client_id) {
return $this->store()->entityManager()->find('OAuthClientKeys', $client_id);
}
public function getPublicKey($client_id = null) {
$_OAuthClientKeys = $this->_getClientKeys($client_id);
if(!$_OAuthClientKeys)
return NULL;
return $_OAuthClientKeys->getPublicKey();
}
public function getPrivateKey($client_id = null) {
$_OAuthClientKeys = $this->_getClientKeys($client_id);
if(!$_OAuthClientKeys)
return NULL;
return $_OAuthClientKeys->getPrivateKey();
}
public function getEncryptionAlgorithm($client_id = null) {
$_OAuthClientKeys = $this->_getClientKeys($client_id);
if(!$_OAuthClientKeys)
return NULL;
return $_OAuthClientKeys->getEncryptionAlgorithm();
}
public function getUserClientSettings(OAuthUser $user) {
$app_settings = [];
$user_clients_collection = $user->getClients();
foreach($user_clients_collection AS $client) {
$app_settings[ $client->getClientId() ] = [
'app_name' => $client->getName(),
'redirect_uri' => $client->getRedirectUri(),
'description' => $client->getDescription(),
'grant_types' => $client->getGrantTypes(),
'scope' => $client->getScope()
];
}
return $app_settings;
}
public function getPendingClientSecret(OAuthClient $client) {
// use the criteria to get this out the client object, this is just a lazy shortcut
}
public function addPendingClientSecret(OAuthClient $client, $new_client_secret) {
// use the collection on the client and store it, lazy shortcut
return true;
}
public function activatePendingClientSecret(OAuthPendingClientSecret $pendingClientSecret) {
// activate then...
$this->purgePendingClientSecrets($pendingClientSecret->getClient());
return true;
}
public function purgePendingClientSecrets(OAuthClient $client) {
// use the criteria to purge the clients
return true;
}
/**
* Grant refresh access tokens.
*
* Retrieve the stored data for the given refresh token.
*
* Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN.
*
* @param $refresh_token
* Refresh token to be check with.
*
* @return
* An associative array as below, and NULL if the refresh_token is
* invalid:
* - refresh_token: Refresh token identifier.
* - client_id: Client identifier.
* - user_id: User identifier.
* - expires: Expiration unix timestamp, or 0 if the token doesn't expire.
* - scope: (optional) Scope values in space-separated string.
*
* @see http://tools.ietf.org/html/rfc6749#section-6
*
* @ingroup oauth2_section_6
*/
public function getRefreshToken($refresh_token) {
$_OAuthRefreshToken = $this->_getRefreshToken($refresh_token);
if(!$_OAuthRefreshToken) {
return NULL;
}
return [
'refresh_token' => $_OAuthRefreshToken->getRefreshToken(),
'client_id' => $_OAuthRefreshToken->getClientId(),
'user_id' => $_OAuthRefreshToken->getUserId(),
'expires' => $_OAuthRefreshToken->getExpires()->getTimestamp(),
'scope' => $_OAuthRefreshToken->getScope(),
];
}
/**
* Take the provided refresh token values and store them somewhere.
*
* This function should be the storage counterpart to getRefreshToken().
*
* If storage fails for some reason, we're not currently checking for
* any sort of success/failure, so you should bail out of the script
* and provide a descriptive fail message.
*
* Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN.
*
* @param $refresh_token
* Refresh token to be stored.
* @param $client_id
* Client identifier to be stored.
* @param $user_id
* User identifier to be stored.
* @param $expires
* Expiration timestamp to be stored. 0 if the token doesn't expire.
* @param $scope
* (optional) Scopes to be stored in space-separated string.
*
* @ingroup oauth2_section_6
*/
public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) {
$oAuthClient = $this->_getClient($client_id);
if(!$oAuthClient)
throw new Exception("Invalid client_id: $client_id");
$_OAuthRefreshToken = new OAuthRefreshToken();
$_OAuthRefreshToken->setRefreshToken($refresh_token);
$expires_datetime = new DateTime();
$expires_datetime->setTimestamp($expires);
$_OAuthRefreshToken->setClient($oAuthClient);
$_OAuthRefreshToken->setUserId($user_id);
$_OAuthRefreshToken->setScope($scope);
$_OAuthRefreshToken->setExpires($expires_datetime);
$this->store()->entityManager()->persist($_OAuthRefreshToken);
$this->store()->entityManager()->flush();
}
/**
* Expire a used refresh token.
*
* This is not explicitly required in the spec, but is almost implied.
* After granting a new refresh token, the old one is no longer useful and
* so should be forcibly expired in the data store so it can't be used again.
*
* If storage fails for some reason, we're not currently checking for
* any sort of success/failure, so you should bail out of the script
* and provide a descriptive fail message.
*
* @param $refresh_token
* Refresh token to be expirse.
*
* @ingroup oauth2_section_6
*/
public function unsetRefreshToken($refresh_token) {
$_OAuthRefreshToken = $this->_getRefreshToken($refresh_token);
if($_OAuthRefreshToken) {
$this->store()->entityManager()->remove($_OAuthRefreshToken);
$this->store()->entityManager()->flush();
}
}
public function _getRefreshToken($refresh_token) {
return $this->store()
->entityManager()
->createQuery('SELECT t FROM OAuthRefreshToken t WHERE t.refreshToken = :refresh_token')
->setParameter('refresh_token', $refresh_token)
->getOneOrNullResult();
}
public function getUserClaims($userId, $scope) {
throw new \Exception("getUserClaims: $userId - $scope");
}
}
Another significant thing I did was to separate the users table from the oauth_client table. This class helps me handle this
I've checked the class you posted (pretty long stuff to read), but doesn't seem to contain what i need (aka only allow user who have same scope as client scope in order to get a token).
I want to customize the /token endpoint in order to do what i said above but then i don't know if this is a good idea or maybe oauth2 isn't suited for this need. Because if i start to customize how scope work at the server level i'm afraid i break compatibility with clients who don't actually about this change ?
Need more inputs on this please
Something that seems to suit more my need is keycloak
I have the same problem. We have multiple apps for our customers to login and use. We only want certain users to be able to login through certain apps. Behind it all, we have one REST API using this library for authentication.
So, I did what Herz3h did. I setup a client with grant_type = password and scope = test123.
I then created a user which has scope=test456 and I was able to get a token when logging in with this user.
Is there a setting that we are missing? It would be great if the system works this way.
This could easily be fixed if Storage->checkUserCredentials was passed the client id.
That way we could use client info checks against users, including checking client scopes against user scopes, ourselves.
So I'm proposing changing 'checkUserCredentials($username, $password)' to 'checkUserCredentials($username, $password, $client_id)'
Would that be possible?
Hello,
I would like to, say, I have a client which has scope : productA
And if user doesn't have scope productA, he can't use that product...
I'm not sure if scope is the right solution for this ? And i don't know any other thing that seems to be permission related in oauth2 other than scope.
How would one implement stuff like this ??