司南 → created an issue.
司南 → created an issue.
Why is it not possible to refresh the token for any grant type, it is only applicable for the "Authorization Code"?
Is there any oauth2 protocol pointting this?
Consumer created by the lines:
// Create client of oauth2 service.
Consumer::create([
'client_id' => 'xxx_app',
'label' => 'XXX App',
'description' => 'Oauth2 client for XXX.',
'is_default' => FALSE,
'grant_types' => [
'authorization_code',
'refresh_token',
'password',
'sms',
],
'secret' => '123',
'confidential' => TRUE,
'redirect' => 'https://app.xxx.cn/',
'access_token_expiration' => 300,
'refresh_token_expiration' => 1209600,
])->save();
namespace Drupal\xxx\Plugin\Oauth2Grant;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\consumers\Entity\Consumer;
use Drupal\simple_oauth\Plugin\Oauth2GrantBase;
use League\OAuth2\Server\Grant\GrantTypeInterface;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @Oauth2Grant(
* id = "password",
* label = @Translation("Password")
* )
*/
class Password extends Oauth2GrantBase implements ContainerFactoryPluginInterface {
/**
* @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
*/
protected $userRepository;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
*/
protected $refreshTokenRepository;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Class constructor.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, UserRepositoryInterface $user_repository, RefreshTokenRepositoryInterface $refresh_token_repository, ConfigFactoryInterface $config_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->userRepository = $user_repository;
$this->refreshTokenRepository = $refresh_token_repository;
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user_phone.oauth2.repositories.user'),
$container->get('simple_oauth.repositories.refresh_token'),
$container->get('config.factory')
);
}
/**
* {@inheritdoc}
*/
public function getGrantType(Consumer $client): GrantTypeInterface {
$refresh_token_enabled = $this->isRefreshTokenEnabled($client);
/** @var \Drupal\simple_oauth\Repositories\OptionalRefreshTokenRepositoryInterface $refresh_token_repository */
$refresh_token_repository = $this->refreshTokenRepository;
if (!$refresh_token_enabled) {
$refresh_token_repository->disableRefreshToken();
}
$grant_type = $this->createGrantType();
if ($refresh_token_enabled) {
$refresh_token = !$client->get('refresh_token_expiration')->isEmpty ? $client->get('refresh_token_expiration')->value : 1209600;
$refresh_token_ttl = new \DateInterval(
sprintf('PT%dS', $refresh_token)
);
$grant_type->setRefreshTokenTTL($refresh_token_ttl);
}
return $grant_type;
}
/**
* Create Gran type object.
*
* @return \League\OAuth2\Server\Grant\GrantTypeInterface
* The created object.
*/
protected function createGrantType(): GrantTypeInterface {
return new PasswordGrant($this->userRepository, $this->refreshTokenRepository);
}
/**
* Checks if refresh token is enabled on the client.
*
* @param \Drupal\consumers\Entity\Consumer $client
* The consumer entity.
*
* @return bool
* Returns boolean.
*/
protected function isRefreshTokenEnabled(Consumer $client): bool {
foreach ($client->get('grant_types')->getValue() as $grant_type) {
if ($grant_type['value'] === 'refresh_token') {
return TRUE;
}
}
return FALSE;
}
}
namespace Drupal\user_phone\Plugin\Oauth2Grant;
use Drupal\user_phone\Oauth2\Grant\SMSGrant;
use League\OAuth2\Server\Grant\GrantTypeInterface;
/**
* Add a custom grant type to allow PhoneNumber + SMS message authentication.
*
* @Oauth2Grant(
* id = "sms",
* label = @Translation("SMS")
* )
*/
class SMS extends Password {
/**
* {@inheritDoc}
*/
protected function createGrantType(): GrantTypeInterface {
return new SMSGrant(
$this->userRepository,
$this->refreshTokenRepository,
);
}
}
namespace Drupal\xxx\Oauth2\Grant;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Extension\MissingDependencyException;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\simple_oauth\Entities\UserEntity;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\RequestEvent;
use Psr\Http\Message\ServerRequestInterface;
/**
* SMS grant class.
*/
class SMSGrant extends PasswordGrant {
use StringTranslationTrait;
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Extension\MissingDependencyException
* User module must be enabled.
*/
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client): UserEntityInterface {
$country = $this->getRequestParameter('country', $request);
if (is_null($country)) {
throw OAuthServerException::invalidRequest('country');
}
$number = $this->getRequestParameter('number', $request);
if (is_null($number)) {
throw OAuthServerException::invalidRequest('number');
}
$code = $this->getRequestParameter('code', $request);
if (is_null($code)) {
throw OAuthServerException::invalidRequest('code');
}
/** @var \Drupal\mobile_number\MobileNumberUtilInterface $util */
$util = \Drupal::service('mobile_number.util');
$mobileNumber = $util->getMobileNumber($number, $country);
if ($mobileNumber) {
// Check whether the phone number have been registered.
try {
$users = \Drupal::entityTypeManager()
->getStorage('user')
->loadByProperties(['phone' => $util->getCallableNumber($mobileNumber)]);
}
catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
throw new MissingDependencyException('Can not find user entity storage, user module of drupal core is necessary.');
}
if (count($users) > 1) {
// Multiple user have been found.
throw OAuthServerException::invalidRequest('number',
'Multi accounts found for the mobile number, please contact administrator.');
}
elseif (count($users) === 0) {
// No user can be found.
throw OAuthServerException::invalidRequest('number',
'Phone number not exist.');
}
$user = new UserEntity();
$user->setIdentifier(reset($users)->id());
/** @var \Drupal\user_phone\SmsCodeVerifierInterface $phone_verify */
$phone_verify = \Drupal::service('user_phone.sms_code_verifier');
if ($phone_verify->verify($util->getCallableNumber($mobileNumber), $code)) {
return $user;
}
}
$this->getEmitter()
->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}
/**
* {@inheritdoc}
*/
public function getIdentifier(): string {
return 'sms';
}
}
Scope config entity I use:
langcode: en
status: true
dependencies: { }
id: role_anonymous
name: role_anonymous
description: 'Role scope for anonymous.'
grant_types:
refresh_token:
status: true
description: ''
authorization_code:
status: true
description: ''
client_credentials:
status: true
description: ''
password:
status: true
description: ''
sms:
status: true
description: ''
umbrella: false
parent: _none
granularity_id: role
granularity_configuration:
role: anonymous
langcode: en
status: true
dependencies: { }
id: role_authenticated
name: role_authenticated
description: 'Role scope for authenticated.'
grant_types:
refresh_token:
status: true
description: ''
authorization_code:
status: true
description: ''
client_credentials:
status: true
description: ''
password:
status: true
description: ''
sms:
status: true
description: ''
umbrella: false
parent: _none
granularity_id: role
granularity_configuration:
role: authenticated
I use a custom grant type which like the deprecated grant type password
.
Feel free to use this patch.
司南 → created an issue.
Please see #13 🐛 Entity ID only records node Active .
Feel free to use this patch.
Add schema for this plugin:
field.formatter.settings.like_entity_id:
type: mapping
label: 'Like entity id formatter settings'
mapping:
link:
type: boolean
label: 'Whether is link'
Enable module of this branch in Drupal 11 occurs error: Route "commerce_stock.admin_settings" does not exist.
I found that this issues is not only on user entity, status field of any entity type can not be filter.
司南 → created an issue.
The correct way is to combin these inc file into .module files.
hook implements in these inc files will be ignored, if \Drupal::hasContainer() return false when the .module file is load by drupal core.
Hey bro, any update for this issues?
Feel free to use this patch with composer.
司南 → created an issue.
We notice that, changing entity_id field from entity_reference type to integer type doesn't bring database field type change, they both are an int database field.
So it doesn't need a hook_update_N() implementation.
There are two task here.
First, "entity_id field missing target_type setting default to node", and it also make jsonapi of core functionality not work. unless we change the entity_id field from entity_reference to integer. This is a bug fix for current branch 2.x.
Second, if we want to make entity_id field a reference field, we need to use dynamic_entity_reference, but it bring much change, so we should create a new branch for this.
Tested it works fine with commerce 3.x.
This change need a hook_update_N() implementation to migrate an exist site:
1. backup the data of entity_id field.
2. uninstall the entity_id field of entity_reference type.
3. create a new entity_id field with integer field type.
4. import backup data to the new field.
In fact, entity_reference field type need a fixed entity type to be configured, that is, entity_reference doesn't support dynamic entity type, contrib module dynamic_entity_reference → provide a field type which support dynamic entity type.
But using instead of dynamic_entity_reference will make big change to current branch code. if consider to use dynamic_entity_reference, we can create a new branch 3.x.
as we can see, change entity_reference to integer is a compatible solution can directlly aplly to branch 2.x, it won't break any current design, and just a bug fix.
司南 → created an issue.
司南 → created an issue.
One of the most important modules need to be port to Drupal 10, counting on it.