diff --git a/core/lib/Drupal/Core/Cache/Context/AccountPermissionsCacheContext.php b/core/lib/Drupal/Core/Cache/Context/AccountPermissionsCacheContext.php
index 5993424..20b2f13 100644
--- a/core/lib/Drupal/Core/Cache/Context/AccountPermissionsCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/Context/AccountPermissionsCacheContext.php
@@ -49,4 +49,14 @@ public function getContext() {
     return 'ph.' . $this->permissionsHashGenerator->generate($this->user);
   }
 
+  /**
+  * {@inheritdoc}
+  */
+  public function getCacheTags() {
+    foreach ($this->user->getRoles() as $role) {
+      $tags[] = "config:user.role.$role";
+    }
+    return $tags;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php b/core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php
index 27b012a..8d85be7 100644
--- a/core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php
+++ b/core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Cache\Context;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\CacheableMetadata;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -88,7 +89,7 @@ public function getLabels($include_calculated_cache_contexts = FALSE) {
   }
 
   /**
-   * Converts cache context tokens to cache keys.
+   * Converts cache context tokens to cache keys and gets their cacheable metadata.
    *
    * A cache context token is either:
    * - a cache context ID (if the service ID is 'cache_context.foo', then 'foo'
@@ -99,12 +100,23 @@ public function getLabels($include_calculated_cache_contexts = FALSE) {
    * @param string[] $context_tokens
    *   An array of cache context tokens.
    *
-   * @return string[]
-   *   The array of corresponding cache keys.
+   * @return mixed[]
+   *   An array containing two items; contexts and cacheable_metadata.
    *
    * @throws \InvalidArgumentException
    */
   public function convertTokensToKeys(array $context_tokens) {
+    $cacheable_metadata = new CacheableMetadata();
+
+    // Iterate through cache contexts and get their cacheable metadata.
+    foreach (static::parseTokens($context_tokens) as $context_token) {
+      list($context_id, $parameter) = $context_token;
+      $context = $this->getService($context_id);
+      if ($context instanceof CacheableCacheContextInterface || $context instanceof CacheableCalculatedCacheContextInterface) {
+        $cacheable_metadata->addCacheTags($context->getCacheableMetadata()->getCacheTags());
+      }
+    }
+
     $context_tokens = $this->optimizeTokens($context_tokens);
     sort($context_tokens);
     $keys = [];
@@ -115,7 +127,13 @@ public function convertTokensToKeys(array $context_tokens) {
       }
       $keys[] = $this->getService($context_id)->getContext($parameter);
     }
-    return $keys;
+
+    $values = [
+      'contexts' => $keys,
+      'cacheable_metadata' => $cacheable_metadata,
+    ];
+
+    return $values;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Cache/Context/CacheableCacheContextInterface.php b/core/lib/Drupal/Core/Cache/Context/CacheableCacheContextInterface.php
new file mode 100644
index 0000000..c3fd648
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Context/CacheableCacheContextInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\Context\CacheableCacheContextInterface.
+ */
+
+namespace Drupal\Core\Cache\Context;
+
+use Drupal\Core\Cache\CacheableDependencyInterface;
+
+interface CacheableCacheContextInterface extends CacheContextInterface, CacheableDependencyInterface {
+
+  /**
+   * Gets the cacheable metadata for the context based on the parameter value.
+   *
+   * @return \Drupal\Core\Cache\CacheableDependencyInterface
+   *   A cacheable dependency interface implementing object, e.g. CacheableMetadata
+   */
+  public function getCacheableMetadata();
+
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Cache/Context/CacheableCalculatedCacheContextInterface.php b/core/lib/Drupal/Core/Cache/Context/CacheableCalculatedCacheContextInterface.php
new file mode 100644
index 0000000..2507607
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Context/CacheableCalculatedCacheContextInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\Context\CacheableCacheContextInterface.
+ */
+
+namespace Drupal\Core\Cache\Context;
+
+use Drupal\Core\Cache\CacheableDependencyInterface;
+
+interface CacheableCalculatedCacheContextInterface extends CalculatedCacheContextInterface {
+
+  /**
+   * Gets the cacheable metadata for the context based on the parameter value.
+   *
+   * @param mixed $parameter
+   *   The parameter to get context values for.
+   *
+   * @return \Drupal\Core\Cache\CacheableDependencyInterface
+   *   A cacheable dependency interface implementing object, e.g. CacheableMetadata
+   */
+  public function getCacheableMetadata($parameter = NULL);
+
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Cache/Context/UserCacheContext.php b/core/lib/Drupal/Core/Cache/Context/UserCacheContext.php
index 80ad519..f883267 100644
--- a/core/lib/Drupal/Core/Cache/Context/UserCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/Context/UserCacheContext.php
@@ -7,12 +7,13 @@
 
 namespace Drupal\Core\Cache\Context;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Session\AccountInterface;
 
 /**
  * Defines the UserCacheContext service, for "per user" caching.
  */
-class UserCacheContext implements CacheContextInterface {
+class UserCacheContext implements CacheableCacheContextInterface {
 
   /**
    * Constructs a new UserCacheContext service.
@@ -38,4 +39,32 @@ public function getContext() {
     return "u." . $this->user->id();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheableMetadata() {
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Render/RenderCache.php b/core/lib/Drupal/Core/Render/RenderCache.php
index 1a8339b..dadeac4 100644
--- a/core/lib/Drupal/Core/Render/RenderCache.php
+++ b/core/lib/Drupal/Core/Render/RenderCache.php
@@ -295,7 +295,7 @@ protected function maxAgeToExpire($max_age) {
    * @return string
    *   The cache ID string, or FALSE if the element may not be cached.
    */
-  protected function createCacheID(array $elements) {
+  protected function createCacheID(array &$elements) {
     // If the maximum age is zero, then caching is effectively prohibited.
     if (isset($elements['#cache']['max-age']) && $elements['#cache']['max-age'] === 0) {
       return FALSE;
@@ -305,7 +305,11 @@ protected function createCacheID(array $elements) {
       $cid_parts = $elements['#cache']['keys'];
       if (!empty($elements['#cache']['contexts'])) {
         $contexts = $this->cacheContextsManager->convertTokensToKeys($elements['#cache']['contexts']);
-        $cid_parts = array_merge($cid_parts, $contexts);
+        $cid_parts = array_merge($cid_parts, $contexts['contexts']);
+        if (!isset($elements['#cache']['tags'])) {
+          $elements['#cache']['tags'] = [];
+        }
+        $elements['#cache']['tags'] = Cache::mergeTags($elements['#cache']['tags'], $contexts['cacheable_metadata']->getCacheTags());
       }
       return implode(':', $cid_parts);
     }
diff --git a/core/modules/system/src/Tests/Render/PermissionsCacheContextTest.php b/core/modules/system/src/Tests/Render/PermissionsCacheContextTest.php
new file mode 100644
index 0000000..71d42a6
--- /dev/null
+++ b/core/modules/system/src/Tests/Render/PermissionsCacheContextTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Render\PermissionsCacheContextTest.
+ */
+
+namespace Drupal\system\Tests\Render;
+
+use Drupal\simpletest\KernelTestBase;
+use Drupal\simpletest\UserCreationTrait;
+use Drupal\user\Entity\Role;
+
+/**
+ * Tests the permissions cache context.
+ *
+ * @group Render
+ */
+class PermissionsCacheContextTest extends KernelTestBase {
+
+  use UserCreationTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var string[]
+   */
+  public static $modules = ['user', 'system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('user');
+    $this->installConfig(['user']);
+    $this->installSchema('system', ['sequences']);
+  }
+
+  /**
+   * Ensures that permission change works with permission and user contexts.
+   */
+  public function testPermissionChange() {
+    $user1 = $this->createUser();
+    $this->assertEqual($user1->id(), 1);
+
+    $authenticated_user = $this->createUser(['administer permissions']);
+    $role = $authenticated_user->getRoles()[1];
+
+    $test_element = [
+      '#cache' => [
+        'keys' => ['test'],
+        'contexts' => ['user', 'user.permissions'],
+      ],
+    ];
+    \Drupal::service('account_switcher')->switchTo($authenticated_user);
+    $element = $test_element;
+    $element['#markup'] = 'content for authenticated users';
+    $output = \Drupal::service('renderer')->renderRoot($element);
+    $this->assertEqual($output, 'content for authenticated users');
+
+    // Verify that the render caching is working.
+    $element = $test_element;
+    $element['#markup'] = 'this should not be visible';
+    $output = \Drupal::service('renderer')->renderRoot($element);
+    $this->assertEqual($output, 'content for authenticated users');
+
+    // Verify that the element is being rendered after permissions have changed.
+    Role::load($role)
+      ->revokePermission('administer permissions')
+      ->save();
+    $element = $test_element;
+    $element['#markup'] = 'this should be visible';
+    $output = \Drupal::service('renderer')->renderRoot($element);
+    $this->assertEqual($output, 'this should be visible');
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php b/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
index 99a32b3..3641773 100644
--- a/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
@@ -95,7 +95,7 @@ public function testConvertTokensToKeys() {
       'baz.cnenzrgreO',
       'bar',
     ];
-    $this->assertEquals($expected, $new_keys);
+    $this->assertEquals($expected, $new_keys['contexts']);
   }
 
   /**
