diff --git a/core/modules/media_library/media_library.services.yml b/core/modules/media_library/media_library.services.yml
index 1a35b2805d111788605ffa5277674142c4f2bdcc..b2d06643b533529df5bceb0ed838a2b4f8fd0aba 100644
--- a/core/modules/media_library/media_library.services.yml
+++ b/core/modules/media_library/media_library.services.yml
@@ -1,7 +1,7 @@
 services:
   media_library.ui_builder:
     class: Drupal\media_library\MediaLibraryUiBuilder
-    arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder']
+    arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder', '@media_library.opener_resolver']
   media_library.route_subscriber:
     class: Drupal\media_library\Routing\RouteSubscriber
     tags:
@@ -12,3 +12,4 @@ services:
       - [setContainer, ['@service_container']]
   media_library.opener.field_widget:
     class: Drupal\media_library\MediaLibraryFieldWidgetOpener
+    arguments: ['@entity_type.manager']
diff --git a/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php
index 71ef10b2f265e69afe8d26558f967d12f8763328..b34b8d308b9ce346d89ad21eb909a6daefbdd086 100644
--- a/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php
+++ b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php
@@ -2,8 +2,12 @@
 
 namespace Drupal\media_library;
 
+use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\InvokeCommand;
+use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Session\AccountInterface;
 
 /**
@@ -11,11 +15,92 @@
  */
 class MediaLibraryFieldWidgetOpener implements MediaLibraryOpenerInterface {
 
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * MediaLibraryFieldWidgetOpener constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
   /**
    * {@inheritdoc}
    */
   public function checkAccess(MediaLibraryState $state, AccountInterface $account) {
-    throw new \Exception('Not yet implemented, see https://www.drupal.org/project/drupal/issues/3038254.');
+    $parameters = $state->getOpenerParameters() + ['entity_id' => NULL];
+
+    $process_result = function ($result) {
+      if ($result instanceof RefinableCacheableDependencyInterface) {
+        $result->addCacheContexts(['url.query_args']);
+      }
+      return $result;
+    };
+
+    // Forbid access if any of the required parameters are missing.
+    foreach (['entity_type_id', 'bundle', 'field_name'] as $key) {
+      if (empty($parameters[$key])) {
+        return $process_result(AccessResult::forbidden("$key parameter is missing."));
+      }
+    }
+
+    $entity_type_id = $parameters['entity_type_id'];
+    $bundle = $parameters['bundle'];
+    $field_name = $parameters['field_name'];
+
+    // Since we defer to a field to determine access, ensure we are dealing with
+    // a fieldable entity type.
+    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+    if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
+      throw new \LogicException("The media library can only be opened by fieldable entities.");
+    }
+
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
+    $access_handler = $this->entityTypeManager->getAccessControlHandler($entity_type_id);
+
+    if ($parameters['entity_id']) {
+      $entity = $storage->load($parameters['entity_id']);
+      $entity_access = $access_handler->access($entity, 'update', $account, TRUE);
+    }
+    else {
+      $entity_access = $access_handler->createAccess($bundle, $account, [], TRUE);
+    }
+
+    // If entity-level access is denied, there's no point in continuing.
+    if (!$entity_access->isAllowed()) {
+      return $process_result($entity_access);
+    }
+
+    // If the entity has not been loaded, create it in memory now.
+    if (!isset($entity)) {
+      $values = [];
+      if ($bundle_key = $entity_type->getKey('bundle')) {
+        $values[$bundle_key] = $bundle;
+      }
+      /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
+      $entity = $storage->create($values);
+    }
+
+    $items = $entity->get($field_name);
+    $field_definition = $items->getFieldDefinition();
+
+    if ($field_definition->getType() !== 'entity_reference') {
+      throw new \LogicException('Expected the media library to be opened by an entity reference field.');
+    }
+    if ($field_definition->getFieldStorageDefinition()->getSetting('target_type') !== 'media') {
+      throw new \LogicException('Expected the media library to be opened by an entity reference field that target media items.');
+    }
+
+    $field_access = $access_handler->fieldAccess('edit', $field_definition, $account, $items, TRUE);
+    return $process_result($entity_access->andIf($field_access));
   }
 
   /**
diff --git a/core/modules/media_library/src/MediaLibraryUiBuilder.php b/core/modules/media_library/src/MediaLibraryUiBuilder.php
index 56095b09a2b043587a09bbdae38d050fcb9ccd44..32262c962ddcb100684928dd6539233c48bd28b5 100644
--- a/core/modules/media_library/src/MediaLibraryUiBuilder.php
+++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
@@ -53,6 +53,13 @@ class MediaLibraryUiBuilder {
    */
   protected $viewsExecutableFactory;
 
+  /**
+   * The media library opener resolver.
+   *
+   * @var \Drupal\media_library\OpenerResolverInterface
+   */
+  protected $openerResolver;
+
   /**
    * Constructs a MediaLibraryUiBuilder instance.
    *
@@ -64,12 +71,19 @@ class MediaLibraryUiBuilder {
    *   The views executable factory.
    * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
    *   The currently active request object.
+   * @param \Drupal\media_library\OpenerResolverInterface $opener_resolver
+   *   The opener resolver.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder, OpenerResolverInterface $opener_resolver = NULL) {
     $this->entityTypeManager = $entity_type_manager;
     $this->request = $request_stack->getCurrentRequest();
     $this->viewsExecutableFactory = $views_executable_factory;
     $this->formBuilder = $form_builder;
+    if (!$opener_resolver) {
+      @trigger_error('The media_library.opener_resolver service must be passed to ' . __METHOD__ . ' and will be required before Drupal 9.0.0.', E_USER_DEPRECATED);
+      $opener_resolver = \Drupal::service('media_library.opener_resolver');
+    }
+    $this->openerResolver = $opener_resolver;
   }
 
   /**
@@ -159,7 +173,7 @@ protected function buildLibraryContent(MediaLibraryState $state) {
    * Check access to the media library.
    *
    * @param \Drupal\Core\Session\AccountInterface $account
-   *   (optional) Run access checks for this account.
+   *   Run access checks for this account.
    * @param \Drupal\media_library\MediaLibraryState $state
    *   (optional) The current state of the media library, derived from the
    *   current request.
@@ -167,10 +181,10 @@ protected function buildLibraryContent(MediaLibraryState $state) {
    * @return \Drupal\Core\Access\AccessResult
    *   The access result.
    */
-  public function checkAccess(AccountInterface $account = NULL, MediaLibraryState $state = NULL) {
+  public function checkAccess(AccountInterface $account, MediaLibraryState $state = NULL) {
     if (!$state) {
       try {
-        MediaLibraryState::fromRequest($this->request);
+        $state = MediaLibraryState::fromRequest($this->request);
       }
       catch (BadRequestHttpException $e) {
         return AccessResult::forbidden($e->getMessage());
@@ -189,8 +203,16 @@ public function checkAccess(AccountInterface $account = NULL, MediaLibraryState
       return AccessResult::forbidden('The media library widget display does not exist.')
         ->addCacheableDependency($view);
     }
-    return AccessResult::allowedIfHasPermission($account, 'view media')
+
+    // The user must at least be able to view media in order to access the media
+    // library.
+    $can_view_media = AccessResult::allowedIfHasPermission($account, 'view media')
       ->addCacheableDependency($view);
+
+    // Delegate any further access checking to the opener service nominated by
+    // the media library state.
+    return $this->openerResolver->get($state)->checkAccess($state, $account)
+      ->andIf($can_view_media);
   }
 
   /**
diff --git a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
index e612c3e4f47767f3d3f37f70d8fda03da772ec53..419cf66174471211f477e45137d931c43fc46e57 100644
--- a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
+++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
@@ -461,9 +461,17 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // This particular media library opener needs some extra metadata for its
     // \Drupal\media_library\MediaLibraryOpenerInterface::getSelectionResponse()
     // to be able to target the element whose 'data-media-library-widget-value'
-    // attribute is the same as $field_widget_id.
+    // attribute is the same as $field_widget_id. The entity ID, entity type ID,
+    // bundle, field name are used for access checking.
+    $entity = $items->getEntity();
     $state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_media_type_ids, $selected_type_id, $remaining, [
       'field_widget_id' => $field_widget_id,
+      'entity_type_id' => $entity->getEntityTypeId(),
+      'bundle' => $entity->bundle(),
+      'field_name' => $field_name,
+      // The entity ID needs to be a string to ensure that the media library
+      // state generates its tamper-proof hash in a consistent way.
+      'entity_id' => (string) $entity->id(),
     ]);
 
     // Add a button that will load the Media library in a modal using AJAX.
diff --git a/core/modules/media_library/tests/modules/media_library_test/media_library_test.module b/core/modules/media_library/tests/modules/media_library_test/media_library_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..3d9576bc1a496d632345303ee4d0989cc1e2b125
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/media_library_test.module
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Contains hook implementations for the media_library_test module.
+ */
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Implements hook_entity_field_access().
+ */
+function media_library_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+  return AccessResult::forbiddenIf($field_definition->getName() === 'field_media_no_access', 'Field access denied by test module');
+}
diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
index 999a1f552814f86e32259250a22c440cdcc4acef..cbefde5417d9ec2380c32104710c4e10fa5ae0d4 100644
--- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
+++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
@@ -332,7 +332,13 @@ public function testWidgetAccess() {
 
     // Create a working state.
     $allowed_types = ['type_one', 'type_two', 'type_three', 'type_four'];
-    $state = MediaLibraryState::create('test', $allowed_types, 'type_three', 2);
+    // The opener parameters are not relevant to the test, but the opener
+    // expects them to be there or it will deny access.
+    $state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_types, 'type_three', 2, [
+      'entity_type_id' => 'node',
+      'bundle' => 'basic_page',
+      'field_name' => 'field_unlimited_media',
+    ]);
     $url_options = ['query' => $state->all()];
 
     // Verify that unprivileged users can't access the widget view.
@@ -344,8 +350,10 @@ public function testWidgetAccess() {
     $assert_session->responseContains('Access denied');
 
     // Allow users with 'view media' permission to access the media library view
-    // and controller.
+    // and controller. Since we are using the node entity type in the state
+    // object, ensure the user also has permission to work with those.
     $this->grantPermissions($role, [
+      'create basic_page content',
       'view media',
     ]);
     $this->drupalGet('admin/content/media-widget', $url_options);
diff --git a/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
index 2b83916e56b4f466487ea047878b28fccd8df510..75d064a5e9981cf327cca7eefc6b1f01444ea6c1 100644
--- a/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
+++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
@@ -2,6 +2,12 @@
 
 namespace Drupal\Tests\media_library\Kernel;
 
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultReasonInterface;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestBundle;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\image\Entity\ImageStyle;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\media_library\MediaLibraryState;
@@ -21,8 +27,10 @@ class MediaLibraryAccessTest extends KernelTestBase {
    * {@inheritdoc}
    */
   protected static $modules = [
+    'entity_test',
     'media',
     'media_library',
+    'media_library_test',
     'file',
     'field',
     'image',
@@ -41,6 +49,7 @@ protected function setUp() {
     $this->installEntitySchema('file');
     $this->installSchema('file', 'file_usage');
     $this->installSchema('system', ['sequences', 'key_value_expire']);
+    $this->installEntitySchema('entity_test');
     $this->installEntitySchema('media');
     $this->installConfig([
       'field',
@@ -51,6 +60,23 @@ protected function setUp() {
       'media_library',
     ]);
 
+    EntityTestBundle::create(['id' => 'test'])->save();
+
+    $field_storage = FieldStorageConfig::create([
+      'type' => 'entity_reference',
+      'field_name' => 'field_test_media',
+      'entity_type' => 'entity_test',
+      'settings' => [
+        'target_type' => 'media',
+      ],
+    ]);
+    $field_storage->save();
+
+    FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => 'test',
+    ])->save();
+
     // Create an account with special UID 1.
     $this->createUser([]);
   }
@@ -72,29 +98,180 @@ public function testMediaLibraryImageStyleAccess() {
   }
 
   /**
-   * Tests the Media Library access.
+   * Tests that the field widget opener respects entity creation permissions.
    */
-  public function testMediaLibraryAccess() {
+  public function testFieldWidgetEntityCreateAccess() {
     /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */
     $ui_builder = $this->container->get('media_library.ui_builder');
 
     // Create a media library state to test access.
-    $state = MediaLibraryState::create('test', ['file', 'image'], 'file', 2);
+    $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [
+      'entity_type_id' => 'entity_test',
+      'bundle' => 'test',
+      'field_name' => 'field_test_media',
+    ]);
+
+    $access_result = $ui_builder->checkAccess($this->createUser(), $state);
+    $this->assertAccess($access_result, FALSE, "The following permissions are required: 'administer entity_test content' OR 'administer entity_test_with_bundle content' OR 'create test entity_test_with_bundle entities'.", [], ['url.query_args', 'user.permissions']);
+
+    // Create a user with the appropriate permissions and assert that access is
+    // granted.
+    $account = $this->createUser([
+      'create test entity_test_with_bundle entities',
+      'view media',
+    ]);
+    $access_result = $ui_builder->checkAccess($account, $state);
+    $this->assertAccess($access_result, TRUE, NULL, Views::getView('media_library')->storage->getCacheTags(), ['url.query_args', 'user.permissions']);
+  }
+
+  /**
+   * Tests that the field widget opener respects entity-specific access.
+   */
+  public function testFieldWidgetEntityEditAccess() {
+    /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */
+    $ui_builder = $this->container->get('media_library.ui_builder');
+
+    $forbidden_entity = EntityTest::create([
+      'type' => 'test',
+      // This label will automatically cause an access denial.
+      // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
+      'name' => 'forbid_access',
+    ]);
+    $forbidden_entity->save();
+
+    // Create a media library state to test access.
+    $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [
+      'entity_type_id' => $forbidden_entity->getEntityTypeId(),
+      'bundle' => $forbidden_entity->bundle(),
+      'field_name' => 'field_test_media',
+      'entity_id' => $forbidden_entity->id(),
+    ]);
+
+    $access_result = $ui_builder->checkAccess($this->createUser(), $state);
+    $this->assertAccess($access_result, FALSE, NULL, [], ['url.query_args']);
+
+    $neutral_entity = EntityTest::create([
+      'type' => 'test',
+      // This label will result in neutral access.
+      // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
+      'name' => $this->randomString(),
+    ]);
+    $neutral_entity->save();
+
+    $parameters = $state->getOpenerParameters();
+    $parameters['entity_id'] = $neutral_entity->id();
+    $state = MediaLibraryState::create(
+      $state->getOpenerId(),
+      $state->getAllowedTypeIds(),
+      $state->getSelectedTypeId(),
+      $state->getAvailableSlots(),
+      $parameters
+    );
+
+    $access_result = $ui_builder->checkAccess($this->createUser(), $state);
+    $this->assertTrue($access_result->isNeutral());
+    $this->assertAccess($access_result, FALSE, NULL, [], ['url.query_args', 'user.permissions']);
+
+    // Give the user permission to edit the entity and assert that access is
+    // granted.
+    $account = $this->createUser([
+      'administer entity_test content',
+      'view media',
+    ]);
+    $access_result = $ui_builder->checkAccess($account, $state);
+    $this->assertAccess($access_result, TRUE, NULL, Views::getView('media_library')->storage->getCacheTags(), ['url.query_args', 'user.permissions']);
+  }
+
+  /**
+   * Tests that the field widget opener respects entity field-level access.
+   */
+  public function testFieldWidgetEntityFieldAccess() {
+    $field_storage = FieldStorageConfig::create([
+      'type' => 'entity_reference',
+      'entity_type' => 'entity_test',
+      // The media_library_test module will deny access to this field.
+      // @see media_library_test_entity_field_access()
+      'field_name' => 'field_media_no_access',
+      'settings' => [
+        'target_type' => 'media',
+      ],
+    ]);
+    $field_storage->save();
+
+    FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => 'test',
+    ])->save();
+
+    /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */
+    $ui_builder = $this->container->get('media_library.ui_builder');
+
+    // Create an account with administrative access to the test entity type,
+    // so that we can be certain that field access is checked.
+    $account = $this->createUser(['administer entity_test content']);
+
+    // Test that access is denied even without an entity to work with.
+    $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [
+      'entity_type_id' => 'entity_test',
+      'bundle' => 'test',
+      'field_name' => $field_storage->getName(),
+    ]);
+    $access_result = $ui_builder->checkAccess($account, $state);
+    $this->assertAccess($access_result, FALSE, 'Field access denied by test module', [], ['url.query_args', 'user.permissions']);
+
+    // Assert that field access is also checked with a real entity.
+    $entity = EntityTest::create([
+      'type' => 'test',
+      'name' => $this->randomString(),
+    ]);
+    $entity->save();
+
+    $parameters = $state->getOpenerParameters();
+    $parameters['entity_id'] = $entity->id();
+
+    $state = MediaLibraryState::create(
+      $state->getOpenerId(),
+      $state->getAllowedTypeIds(),
+      $state->getSelectedTypeId(),
+      $state->getAvailableSlots(),
+      $parameters
+    );
+    $access_result = $ui_builder->checkAccess($account, $state);
+    $this->assertAccess($access_result, FALSE, 'Field access denied by test module', [], ['url.query_args', 'user.permissions']);
+  }
+
+  /**
+   * Tests that media library access respects the media_library view.
+   */
+  public function testViewAccess() {
+    /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */
+    $ui_builder = $this->container->get('media_library.ui_builder');
+
+    // Create a media library state to test access.
+    $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [
+      'entity_type_id' => 'entity_test',
+      'bundle' => 'test',
+      'field_name' => 'field_test_media',
+    ]);
 
     // Create a clone of the view so we can reset the original later.
     $view_original = clone Views::getView('media_library');
 
-    // Create our test users.
-    $forbidden_account = $this->createUser([]);
-    $allowed_account = $this->createUser(['view media']);
+    // Create our test users. Both have permission to create entity_test content
+    // so that we can specifically test Views-related access checking.
+    // @see ::testEntityCreateAccess()
+    $forbidden_account = $this->createUser([
+      'create test entity_test_with_bundle entities',
+    ]);
+    $allowed_account = $this->createUser([
+      'create test entity_test_with_bundle entities',
+      'view media',
+    ]);
 
     // Assert the 'view media' permission is needed to access the library and
     // validate the cache dependencies.
     $access_result = $ui_builder->checkAccess($forbidden_account, $state);
-    $this->assertFalse($access_result->isAllowed());
-    $this->assertSame("The 'view media' permission is required.", $access_result->getReason());
-    $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags());
-    $this->assertSame(['user.permissions'], $access_result->getCacheContexts());
+    $this->assertAccess($access_result, FALSE, "The 'view media' permission is required.", $view_original->storage->getCacheTags(), ['url.query_args', 'user.permissions']);
 
     // Assert that the media library access is denied when the view widget
     // display is deleted.
@@ -104,27 +281,42 @@ public function testMediaLibraryAccess() {
     $view_storage->set('display', $displays);
     $view_storage->save();
     $access_result = $ui_builder->checkAccess($allowed_account, $state);
-    $this->assertFalse($access_result->isAllowed());
-    $this->assertSame('The media library widget display does not exist.', $access_result->getReason());
-    $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags());
-    $this->assertSame([], $access_result->getCacheContexts());
+    $this->assertAccess($access_result, FALSE, 'The media library widget display does not exist.', $view_original->storage->getCacheTags());
 
     // Restore the original view and assert that the media library controller
     // works again.
     $view_original->storage->save();
     $access_result = $ui_builder->checkAccess($allowed_account, $state);
-    $this->assertTrue($access_result->isAllowed());
-    $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags());
-    $this->assertSame(['user.permissions'], $access_result->getCacheContexts());
+    $this->assertAccess($access_result, TRUE, NULL, $view_original->storage->getCacheTags(), ['url.query_args', 'user.permissions']);
 
     // Assert that the media library access is denied when the entire media
     // library view is deleted.
     Views::getView('media_library')->storage->delete();
     $access_result = $ui_builder->checkAccess($allowed_account, $state);
-    $this->assertFalse($access_result->isAllowed());
-    $this->assertSame('The media library view does not exist.', $access_result->getReason());
-    $this->assertSame([], $access_result->getCacheTags());
-    $this->assertSame([], $access_result->getCacheContexts());
+    $this->assertAccess($access_result, FALSE, 'The media library view does not exist.');
+  }
+
+  /**
+   * Asserts various aspects of an access result.
+   *
+   * @param \Drupal\Core\Access\AccessResult $access_result
+   *   The access result.
+   * @param bool $is_allowed
+   *   The expected access status.
+   * @param string $expected_reason
+   *   (optional) The expected reason attached to the access result.
+   * @param string[] $expected_cache_tags
+   *   (optional) The expected cache tags attached to the access result.
+   * @param string[] $expected_cache_contexts
+   *   (optional) The expected cache contexts attached to the access result.
+   */
+  private function assertAccess(AccessResult $access_result, $is_allowed, $expected_reason = NULL, array $expected_cache_tags = [], array $expected_cache_contexts = []) {
+    $this->assertSame($is_allowed, $access_result->isAllowed());
+    if ($access_result instanceof AccessResultReasonInterface && isset($expected_reason)) {
+      $this->assertSame($expected_reason, $access_result->getReason());
+    }
+    $this->assertSame($expected_cache_tags, $access_result->getCacheTags());
+    $this->assertSame($expected_cache_contexts, $access_result->getCacheContexts());
   }
 
 }