Loading README.md +5 −2 Original line number Diff line number Diff line Loading @@ -23,8 +23,11 @@ Go to /admin/config/system/jwt to pick the key to be used. When creating a JWT to send, the iat and exp claims should be included. The namespaced claim drupal / uid is used by jwt_auth_consumer to determine the user account to be used when authenticated. The namespaced claim "drupal / uid" is used by jwt_auth_consumer to determine the user account to be used when authenticated. You can also use a user uuid or username with claims "drupal / uuid" or "drupal / name". The claims are checked in the order listed here, and the first one that's populated is used to determine the user. ## Request Header Loading modules/jwt_auth_consumer/src/EventSubscriber/JwtAuthConsumerSubscriber.php +53 −17 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\jwt\Authentication\Event\JwtAuthValidateEvent; use Drupal\jwt\Authentication\Event\JwtAuthValidEvent; use Drupal\jwt\Authentication\Event\JwtAuthEvents; use Drupal\jwt\JsonWebToken\JsonWebTokenInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** Loading Loading @@ -41,28 +42,65 @@ class JwtAuthConsumerSubscriber implements EventSubscriberInterface { } /** * Validates that a uid is present in the JWT. * Find and load the user for a JWT. * * This validates the format of the JWT and validate the uid is a * valid uid in the system. * @param \Drupal\jwt\JsonWebToken\JsonWebTokenInterface $token * The JWT. * * @return array * The user and reason if no user was loaded. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function loadUserForJwt(JsonWebTokenInterface $token): array { foreach (['uid', 'uuid', 'name'] as $id_type) { $id = $token->getClaim(['drupal', $id_type]); if ($id !== NULL) { break; } } if ($id === NULL) { return [ NULL, 'No Drupal uid, uuid, or name was provided in the JWT payload.', ]; } $user_storage = $this->entityTypeManager->getStorage('user'); if ($id_type === 'uid') { $user = $user_storage->load($id); } else { $user = current($user_storage->loadByProperties([$id_type => $id])); } if (!$user) { return [NULL, 'User does not exist.']; } if ($user->isBlocked()) { return [NULL, 'User is blocked.']; } return [$user, NULL]; } /** * Validates that a uid, uuid, or name is present in the JWT. * * This validates the format of the JWT and validate the uid, uuid, or name * corresponds to a valid user in the system. * * @param \Drupal\jwt\Authentication\Event\JwtAuthValidateEvent $event * A JwtAuth event. */ public function validate(JwtAuthValidateEvent $event) { $token = $event->getToken(); $uid = $token->getClaim(['drupal', 'uid']); if ($uid === NULL) { $event->invalidate('No Drupal uid was provided in the JWT payload.'); return; } $user = $this->entityTypeManager->getStorage('user')->load($uid); if ($user === NULL) { $event->invalidate('No UID exists.'); return; [$user, $reason] = $this->loadUserForJwt($token); if (!$user) { $event->invalidate($reason); } if ($user->isBlocked()) { $event->invalidate('User is blocked.'); elseif (!$token->getClaim(['drupal', 'uid'])) { // Set the uid claim to simplify the code path in other subscribers and // to make the loadUser step more efficient. $token->setClaim(['drupal', 'uid'], (int) $user->id()); } } Loading @@ -74,9 +112,7 @@ class JwtAuthConsumerSubscriber implements EventSubscriberInterface { */ public function loadUser(JwtAuthValidEvent $event) { $token = $event->getToken(); $user_storage = $this->entityTypeManager->getStorage('user'); $uid = $token->getClaim(['drupal', 'uid']); $user = $user_storage->load($uid); [$user] = $this->loadUserForJwt($token); $event->setUser($user); } Loading modules/users_jwt/README.md +5 −2 Original line number Diff line number Diff line Loading @@ -12,8 +12,11 @@ string associated with a Drupal user account. When creating a JWT, the iat and exp claims must be included. The exp cannot be more than 24 hours later than the iat value. Like the main jwt module, the namespaced claim drupal / uid is used to indicate the user account to be used when authenticated. This must match the uid associated with the key ID. Like the jwt_auth_consumer module, the namespaced claim drupal / uid is used to determine the user account to be used when authenticated. You can also use a user uuid or username with claims "drupal / uuid" or "drupal / name". The claims are checked in the order listed here, and the first one that's populated is used to determine the user. This user uid must match the uid associated with the key ID. ## Request Header Loading modules/users_jwt/src/Authentication/Provider/UsersJwtAuth.php +51 −8 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Firebase\JWT\JWT; /** * Class UsersJwtAuth. * Authentication provider UsersJwtAuth. */ class UsersJwtAuth implements AuthenticationProviderInterface { Loading Loading @@ -109,15 +109,58 @@ class UsersJwtAuth implements AuthenticationProviderInterface { if ($header->alg !== $key->alg) { return $this->debugLog('Bad header alg', NULL, $payload, $key); } if (empty($payload->drupal->uid) || (int) $payload->drupal->uid !== $key->uid) { return $this->debugLog('Bad uid claim', NULL, $payload, $key); } /** @var \Drupal\user\UserInterface $user */ $user = $this->entityTypeManager->getStorage('user')->load($key->uid); if ($user && !$user->isBlocked()) { [$user, $reason] = $this->loadUserForJwt($payload); if (!$user) { return $this->debugLog($reason, NULL, $payload, $key); } if ($key->uid !== (int) $user->id()) { return $this->debugLog('User uid does not match key uid', NULL, $payload, $key, $user); } return $user; } return $this->debugLog('Bad user', NULL, $payload, $key, $user); /** * Find and load the user for a JWT. * * @todo Unify this code and the code in JwtAuthConsumerSubscriber. * * @param object $payload * The JWT claims. * * @return array * The user and reason if no user was loaded. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function loadUserForJwt(object $payload): array { foreach (['uid', 'uuid', 'name'] as $id_type) { if (isset($payload->drupal->{$id_type})) { $id = $payload->drupal->{$id_type}; break; } } if ($id === NULL) { return [ NULL, 'No Drupal uid, uuid, or name was provided in the JWT payload.', ]; } $user_storage = $this->entityTypeManager->getStorage('user'); if ($id_type === 'uid') { $user = $user_storage->load($id); } else { $user = current($user_storage->loadByProperties([$id_type => $id])); } if (!$user) { return [NULL, 'User does not exist.']; } if ($user->isBlocked()) { return [NULL, 'User is blocked.']; } return [$user, NULL]; } /** Loading modules/users_jwt/tests/src/Functional/FormsTest.php +77 −51 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ class FormsTest extends BrowserTestBase { * * @var array */ public static $modules = ['users_jwt', 'block']; protected static $modules = ['users_jwt', 'block']; /** * {@inheritdoc} Loading @@ -42,10 +42,11 @@ class FormsTest extends BrowserTestBase { /** * {@inheritdoc} */ protected function setUp() { protected function setUp(): void { parent::setUp(); $this->drupalPlaceBlock('local_actions_block'); $this->adminUser = $this->drupalCreateUser(['administer site configuration', 'administer users']); $perms = ['administer site configuration', 'administer users']; $this->adminUser = $this->drupalCreateUser($perms); $this->user = $this->drupalCreateUser(['access content']); $this->drupalLogin($this->user); } Loading @@ -56,20 +57,21 @@ class FormsTest extends BrowserTestBase { public function testForms() { // Loading another user's page should fail. $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->adminUser->id()])); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()])); $this->assertResponse(200); $this->assertText('No keys found.'); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->pageTextContains('No keys found.'); $this->clickLink('Generate Key'); $this->assertText('When you click the button, a new key will be generated'); $this->assertSession()->pageTextContains('When you click the button, a new key will be generated'); $this->submitForm([], 'Generate'); // The test browser sees the response content. $generated_private_key = $this->getSession()->getPage()->getContent(); self::assertNotFalse(\mb_strpos($generated_private_key, '-----BEGIN PRIVATE KEY-----')); $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()])); $this->assertNoText('No keys found.'); $this->assertText('-----BEGIN PUBLIC KEY-----'); $this->assertCacheTag('users_jwt:' . $this->user->id()); $this->assertSession()->pageTextnotContains('No keys found.'); $this->assertSession()->pageTextContains('-----BEGIN PUBLIC KEY-----'); $expected_cache_tag = 'users_jwt:' . $this->user->id(); $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', $expected_cache_tag); /** @var \Drupal\users_jwt\UsersJwtKeyRepositoryInterface $key_repository */ $key_repository = $this->container->get('users_jwt.key_repository'); $keys = $key_repository->getUsersKeys($this->user->id()); Loading @@ -78,17 +80,20 @@ class FormsTest extends BrowserTestBase { // Sleep to make sure the time changes for the next key ID. sleep(1); $this->clickLink('Add Key'); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa1-public.pem'; /** @var \Drupal\Core\Extension\ExtensionPathResolver $path_resolver */ $path_resolver = $this->container->get('extension.path.resolver'); $module_path = $path_resolver->getPath('module', 'users_jwt'); $path = $module_path . '/tests/fixtures/users_jwt_rsa1-public.pem'; $public_key = file_get_contents($path); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa1-private.pem'; $path = $module_path . '/tests/fixtures/users_jwt_rsa1-private.pem'; $private_key1 = file_get_contents($path); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa2-private.pem'; $path = $module_path . '/tests/fixtures/users_jwt_rsa2-private.pem'; $private_key2 = file_get_contents($path); $edit = [ 'pubkey' => $this->randomString(), ]; $this->submitForm($edit, 'Save'); $this->assertText('This does not look like a PEM formatted RSA public key'); $this->assertSession()->pageTextContains('This does not look like a PEM formatted RSA public key'); $edit = [ 'pubkey' => $public_key, ]; Loading @@ -102,14 +107,25 @@ class FormsTest extends BrowserTestBase { // Allowed to access the normal user's keys page. $url = Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()]); $this->drupalGet($url); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); $this->drupalLogout(); $iat = \Drupal::time()->getCurrentTime(); $variants = [ 'uid' => $this->user->id(), 'uuid' => $this->user->uuid(), 'name' => $this->user->getAccountName(), ]; $extra = [ 'uid' => ['uuid', 'name'], 'uuid' => ['name', 'anything'], 'name' => ['something'], ]; foreach ($variants as $id_type => $id_value) { $iat = \Drupal::time()->getRequestTime(); $good_payload = [ 'iat' => $iat, 'exp' => $iat + 1000, 'drupal' => [ 'uid' => $this->user->id(), $id_type => $id_value, ], ]; // Verify requests work with the generated/submitted keys. Loading @@ -121,26 +137,36 @@ class FormsTest extends BrowserTestBase { self::assertNotEmpty($token); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); $token = JWT::encode($good_payload, $private_key1, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); // Add extra claims that should be ignored. $extra_payload = $good_payload; foreach ($extra[$id_type] as $key) { $extra_payload['drupal'][$key] = $this->randomMachineName(); } $token = JWT::encode($extra_payload, $generated_private_key, 'RS256', $generated_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertSession()->statusCodeEquals(200); // Invalid key ID. $token = JWT::encode($good_payload, $private_key1, 'RS256', 'wxyz'); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); // Invalid private key. $token = JWT::encode($good_payload, $private_key2, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); // Invalid private key, public page. $token = JWT::encode($good_payload, $private_key2, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet('<front>', [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); } } } Loading Loading
README.md +5 −2 Original line number Diff line number Diff line Loading @@ -23,8 +23,11 @@ Go to /admin/config/system/jwt to pick the key to be used. When creating a JWT to send, the iat and exp claims should be included. The namespaced claim drupal / uid is used by jwt_auth_consumer to determine the user account to be used when authenticated. The namespaced claim "drupal / uid" is used by jwt_auth_consumer to determine the user account to be used when authenticated. You can also use a user uuid or username with claims "drupal / uuid" or "drupal / name". The claims are checked in the order listed here, and the first one that's populated is used to determine the user. ## Request Header Loading
modules/jwt_auth_consumer/src/EventSubscriber/JwtAuthConsumerSubscriber.php +53 −17 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\jwt\Authentication\Event\JwtAuthValidateEvent; use Drupal\jwt\Authentication\Event\JwtAuthValidEvent; use Drupal\jwt\Authentication\Event\JwtAuthEvents; use Drupal\jwt\JsonWebToken\JsonWebTokenInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** Loading Loading @@ -41,28 +42,65 @@ class JwtAuthConsumerSubscriber implements EventSubscriberInterface { } /** * Validates that a uid is present in the JWT. * Find and load the user for a JWT. * * This validates the format of the JWT and validate the uid is a * valid uid in the system. * @param \Drupal\jwt\JsonWebToken\JsonWebTokenInterface $token * The JWT. * * @return array * The user and reason if no user was loaded. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function loadUserForJwt(JsonWebTokenInterface $token): array { foreach (['uid', 'uuid', 'name'] as $id_type) { $id = $token->getClaim(['drupal', $id_type]); if ($id !== NULL) { break; } } if ($id === NULL) { return [ NULL, 'No Drupal uid, uuid, or name was provided in the JWT payload.', ]; } $user_storage = $this->entityTypeManager->getStorage('user'); if ($id_type === 'uid') { $user = $user_storage->load($id); } else { $user = current($user_storage->loadByProperties([$id_type => $id])); } if (!$user) { return [NULL, 'User does not exist.']; } if ($user->isBlocked()) { return [NULL, 'User is blocked.']; } return [$user, NULL]; } /** * Validates that a uid, uuid, or name is present in the JWT. * * This validates the format of the JWT and validate the uid, uuid, or name * corresponds to a valid user in the system. * * @param \Drupal\jwt\Authentication\Event\JwtAuthValidateEvent $event * A JwtAuth event. */ public function validate(JwtAuthValidateEvent $event) { $token = $event->getToken(); $uid = $token->getClaim(['drupal', 'uid']); if ($uid === NULL) { $event->invalidate('No Drupal uid was provided in the JWT payload.'); return; } $user = $this->entityTypeManager->getStorage('user')->load($uid); if ($user === NULL) { $event->invalidate('No UID exists.'); return; [$user, $reason] = $this->loadUserForJwt($token); if (!$user) { $event->invalidate($reason); } if ($user->isBlocked()) { $event->invalidate('User is blocked.'); elseif (!$token->getClaim(['drupal', 'uid'])) { // Set the uid claim to simplify the code path in other subscribers and // to make the loadUser step more efficient. $token->setClaim(['drupal', 'uid'], (int) $user->id()); } } Loading @@ -74,9 +112,7 @@ class JwtAuthConsumerSubscriber implements EventSubscriberInterface { */ public function loadUser(JwtAuthValidEvent $event) { $token = $event->getToken(); $user_storage = $this->entityTypeManager->getStorage('user'); $uid = $token->getClaim(['drupal', 'uid']); $user = $user_storage->load($uid); [$user] = $this->loadUserForJwt($token); $event->setUser($user); } Loading
modules/users_jwt/README.md +5 −2 Original line number Diff line number Diff line Loading @@ -12,8 +12,11 @@ string associated with a Drupal user account. When creating a JWT, the iat and exp claims must be included. The exp cannot be more than 24 hours later than the iat value. Like the main jwt module, the namespaced claim drupal / uid is used to indicate the user account to be used when authenticated. This must match the uid associated with the key ID. Like the jwt_auth_consumer module, the namespaced claim drupal / uid is used to determine the user account to be used when authenticated. You can also use a user uuid or username with claims "drupal / uuid" or "drupal / name". The claims are checked in the order listed here, and the first one that's populated is used to determine the user. This user uid must match the uid associated with the key ID. ## Request Header Loading
modules/users_jwt/src/Authentication/Provider/UsersJwtAuth.php +51 −8 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Firebase\JWT\JWT; /** * Class UsersJwtAuth. * Authentication provider UsersJwtAuth. */ class UsersJwtAuth implements AuthenticationProviderInterface { Loading Loading @@ -109,15 +109,58 @@ class UsersJwtAuth implements AuthenticationProviderInterface { if ($header->alg !== $key->alg) { return $this->debugLog('Bad header alg', NULL, $payload, $key); } if (empty($payload->drupal->uid) || (int) $payload->drupal->uid !== $key->uid) { return $this->debugLog('Bad uid claim', NULL, $payload, $key); } /** @var \Drupal\user\UserInterface $user */ $user = $this->entityTypeManager->getStorage('user')->load($key->uid); if ($user && !$user->isBlocked()) { [$user, $reason] = $this->loadUserForJwt($payload); if (!$user) { return $this->debugLog($reason, NULL, $payload, $key); } if ($key->uid !== (int) $user->id()) { return $this->debugLog('User uid does not match key uid', NULL, $payload, $key, $user); } return $user; } return $this->debugLog('Bad user', NULL, $payload, $key, $user); /** * Find and load the user for a JWT. * * @todo Unify this code and the code in JwtAuthConsumerSubscriber. * * @param object $payload * The JWT claims. * * @return array * The user and reason if no user was loaded. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function loadUserForJwt(object $payload): array { foreach (['uid', 'uuid', 'name'] as $id_type) { if (isset($payload->drupal->{$id_type})) { $id = $payload->drupal->{$id_type}; break; } } if ($id === NULL) { return [ NULL, 'No Drupal uid, uuid, or name was provided in the JWT payload.', ]; } $user_storage = $this->entityTypeManager->getStorage('user'); if ($id_type === 'uid') { $user = $user_storage->load($id); } else { $user = current($user_storage->loadByProperties([$id_type => $id])); } if (!$user) { return [NULL, 'User does not exist.']; } if ($user->isBlocked()) { return [NULL, 'User is blocked.']; } return [$user, NULL]; } /** Loading
modules/users_jwt/tests/src/Functional/FormsTest.php +77 −51 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ class FormsTest extends BrowserTestBase { * * @var array */ public static $modules = ['users_jwt', 'block']; protected static $modules = ['users_jwt', 'block']; /** * {@inheritdoc} Loading @@ -42,10 +42,11 @@ class FormsTest extends BrowserTestBase { /** * {@inheritdoc} */ protected function setUp() { protected function setUp(): void { parent::setUp(); $this->drupalPlaceBlock('local_actions_block'); $this->adminUser = $this->drupalCreateUser(['administer site configuration', 'administer users']); $perms = ['administer site configuration', 'administer users']; $this->adminUser = $this->drupalCreateUser($perms); $this->user = $this->drupalCreateUser(['access content']); $this->drupalLogin($this->user); } Loading @@ -56,20 +57,21 @@ class FormsTest extends BrowserTestBase { public function testForms() { // Loading another user's page should fail. $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->adminUser->id()])); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()])); $this->assertResponse(200); $this->assertText('No keys found.'); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->pageTextContains('No keys found.'); $this->clickLink('Generate Key'); $this->assertText('When you click the button, a new key will be generated'); $this->assertSession()->pageTextContains('When you click the button, a new key will be generated'); $this->submitForm([], 'Generate'); // The test browser sees the response content. $generated_private_key = $this->getSession()->getPage()->getContent(); self::assertNotFalse(\mb_strpos($generated_private_key, '-----BEGIN PRIVATE KEY-----')); $this->drupalGet(Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()])); $this->assertNoText('No keys found.'); $this->assertText('-----BEGIN PUBLIC KEY-----'); $this->assertCacheTag('users_jwt:' . $this->user->id()); $this->assertSession()->pageTextnotContains('No keys found.'); $this->assertSession()->pageTextContains('-----BEGIN PUBLIC KEY-----'); $expected_cache_tag = 'users_jwt:' . $this->user->id(); $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', $expected_cache_tag); /** @var \Drupal\users_jwt\UsersJwtKeyRepositoryInterface $key_repository */ $key_repository = $this->container->get('users_jwt.key_repository'); $keys = $key_repository->getUsersKeys($this->user->id()); Loading @@ -78,17 +80,20 @@ class FormsTest extends BrowserTestBase { // Sleep to make sure the time changes for the next key ID. sleep(1); $this->clickLink('Add Key'); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa1-public.pem'; /** @var \Drupal\Core\Extension\ExtensionPathResolver $path_resolver */ $path_resolver = $this->container->get('extension.path.resolver'); $module_path = $path_resolver->getPath('module', 'users_jwt'); $path = $module_path . '/tests/fixtures/users_jwt_rsa1-public.pem'; $public_key = file_get_contents($path); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa1-private.pem'; $path = $module_path . '/tests/fixtures/users_jwt_rsa1-private.pem'; $private_key1 = file_get_contents($path); $path = drupal_get_path('module', 'users_jwt') . '/tests/fixtures/users_jwt_rsa2-private.pem'; $path = $module_path . '/tests/fixtures/users_jwt_rsa2-private.pem'; $private_key2 = file_get_contents($path); $edit = [ 'pubkey' => $this->randomString(), ]; $this->submitForm($edit, 'Save'); $this->assertText('This does not look like a PEM formatted RSA public key'); $this->assertSession()->pageTextContains('This does not look like a PEM formatted RSA public key'); $edit = [ 'pubkey' => $public_key, ]; Loading @@ -102,14 +107,25 @@ class FormsTest extends BrowserTestBase { // Allowed to access the normal user's keys page. $url = Url::fromRoute('users_jwt.key_list', ['user' => $this->user->id()]); $this->drupalGet($url); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); $this->drupalLogout(); $iat = \Drupal::time()->getCurrentTime(); $variants = [ 'uid' => $this->user->id(), 'uuid' => $this->user->uuid(), 'name' => $this->user->getAccountName(), ]; $extra = [ 'uid' => ['uuid', 'name'], 'uuid' => ['name', 'anything'], 'name' => ['something'], ]; foreach ($variants as $id_type => $id_value) { $iat = \Drupal::time()->getRequestTime(); $good_payload = [ 'iat' => $iat, 'exp' => $iat + 1000, 'drupal' => [ 'uid' => $this->user->id(), $id_type => $id_value, ], ]; // Verify requests work with the generated/submitted keys. Loading @@ -121,26 +137,36 @@ class FormsTest extends BrowserTestBase { self::assertNotEmpty($token); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); $token = JWT::encode($good_payload, $private_key1, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); // Add extra claims that should be ignored. $extra_payload = $good_payload; foreach ($extra[$id_type] as $key) { $extra_payload['drupal'][$key] = $this->randomMachineName(); } $token = JWT::encode($extra_payload, $generated_private_key, 'RS256', $generated_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertSession()->statusCodeEquals(200); // Invalid key ID. $token = JWT::encode($good_payload, $private_key1, 'RS256', 'wxyz'); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); // Invalid private key. $token = JWT::encode($good_payload, $private_key2, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet($url, [], $headers); $this->assertResponse(403); $this->assertSession()->statusCodeEquals(403); // Invalid private key, public page. $token = JWT::encode($good_payload, $private_key2, 'RS256', $submitted_key->id); $headers = [$header_name => 'UsersJwt ' . $token]; $this->drupalGet('<front>', [], $headers); $this->assertResponse(200); $this->assertSession()->statusCodeEquals(200); } } } Loading