diff --git a/jsonapi.services.yml b/jsonapi.services.yml
index 8b1bf70..c111a8e 100644
--- a/jsonapi.services.yml
+++ b/jsonapi.services.yml
@@ -9,6 +9,11 @@ services:
     arguments: ['@current_user']
     tags:
       - { name: normalizer, priority: 2 }
+  serializer.normalizer.entity_access_exception.jsonapi:
+    class: Drupal\jsonapi\Normalizer\EntityAccessDeniedHttpExceptionNormalizer
+    arguments: ['@current_user']
+    tags:
+      - { name: normalizer, priority: 2 }
   serializer.normalizer.scalar.jsonapi:
     class: Drupal\jsonapi\Normalizer\ScalarNormalizer
     tags:
diff --git a/src/Controller/EntityResource.php b/src/Controller/EntityResource.php
index 224294f..2eb4fe0 100644
--- a/src/Controller/EntityResource.php
+++ b/src/Controller/EntityResource.php
@@ -4,6 +4,7 @@ namespace Drupal\jsonapi\Controller;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Core\Access\AccessibleInterface;
+use Drupal\Core\Access\AccessResultReasonInterface;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
@@ -15,6 +16,7 @@ use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\jsonapi\Exception\EntityAccessDeniedHttpException;
 use Drupal\jsonapi\Resource\EntityCollection;
 use Drupal\jsonapi\Resource\JsonApiDocumentTopLevel;
 use Drupal\jsonapi\ResourceType\ResourceType;
@@ -117,7 +119,7 @@ class EntityResource {
   public function getIndividual(EntityInterface $entity, Request $request, $response_code = 200) {
     $entity_access = $entity->access('view', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
-      throw new SerializableHttpException(403, 'The current user is not allowed to GET the selected resource.');
+      throw new EntityAccessDeniedHttpException($entity, $entity_access, '/data', 'The current user is not allowed to GET the selected resource.');
     }
     $response = $this->buildWrappedResponse($entity, $response_code);
     return $response;
@@ -169,7 +171,7 @@ class EntityResource {
     $entity_access = $entity->access('create', NULL, TRUE);
 
     if (!$entity_access->isAllowed()) {
-      throw new SerializableHttpException(403, 'The current user is not allowed to POST the selected resource.');
+      throw new EntityAccessDeniedHttpException($entity, $entity_access, '/data', 'The current user is not allowed to POST the selected resource.');
     }
     $this->validate($entity);
     $entity->save();
@@ -192,7 +194,7 @@ class EntityResource {
   public function patchIndividual(EntityInterface $entity, EntityInterface $parsed_entity, Request $request) {
     $entity_access = $entity->access('update', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
-      throw new SerializableHttpException(403, 'The current user is not allowed to PATCH the selected resource.');
+      throw new EntityAccessDeniedHttpException($entity, $entity_access, '/data', 'The current user is not allowed to PATCH the selected resource.');
     }
     $body = Json::decode($request->getContent());
     $data = $body['data'];
@@ -229,7 +231,7 @@ class EntityResource {
   public function deleteIndividual(EntityInterface $entity, Request $request) {
     $entity_access = $entity->access('delete', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
-      throw new SerializableHttpException(403, 'The current user is not allowed to DELETE the selected resource.');
+      throw new EntityAccessDeniedHttpException($entity, $entity_access, '/data', 'The current user is not allowed to DELETE the selected resource.');
     }
     $entity->delete();
     return new ResourceResponse(NULL, 204);
@@ -386,7 +388,8 @@ class EntityResource {
 
     $field_access = $field_list->access('edit', NULL, TRUE);
     if (!$field_access->isAllowed()) {
-      throw new SerializableHttpException(403, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_list->getName()));
+      $field_name = $field_list->getName();
+      throw new EntityAccessDeniedHttpException($entity, $field_access, '/data/relationships/' . $field_name, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
     }
     // Time to save the relationship.
     foreach ($parsed_field_list as $field_item) {
@@ -464,7 +467,7 @@ class EntityResource {
     $field_name = $parsed_field_list->getName();
     $field_access = $parsed_field_list->access('edit', NULL, TRUE);
     if (!$field_access->isAllowed()) {
-      throw new SerializableHttpException(403, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
+      throw new EntityAccessDeniedHttpException($entity, $field_access, '/data/relationships/' . $field_name, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
     }
     $entity->{$field_name} = $parsed_field_list;
   }
@@ -501,7 +504,7 @@ class EntityResource {
     $field_name = $parsed_field_list->getName();
     $field_access = $parsed_field_list->access('edit', NULL, TRUE);
     if (!$field_access->isAllowed()) {
-      throw new SerializableHttpException(403, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
+      throw new EntityAccessDeniedHttpException($entity, $field_access, '/data/relationships/' . $field_name, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
     }
     /* @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field_list */
     $field_list = $entity->{$related_field};
@@ -623,7 +626,8 @@ class EntityResource {
     /* @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $parsed_field_list */
     $entity_access = $entity->access('update', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
-      throw new SerializableHttpException(403, 'The current user is not allowed to update the selected resource.');
+      // @todo Is this really the right path?
+      throw new EntityAccessDeniedHttpException($entity, $entity_access, $related_field, 'The current user is not allowed to update the selected resource.');
     }
     if (!($field_list = $entity->get($related_field)) || !$this->isRelationshipField($field_list)) {
       throw new SerializableHttpException(404, sprintf('The relationship %s is not present in this resource.', $related_field));
@@ -655,7 +659,7 @@ class EntityResource {
       if ($destination_field_list->getValue() != $origin_field_list->getValue()) {
         $field_access = $destination_field_list->access('edit', NULL, TRUE);
         if (!$field_access->isAllowed()) {
-          throw new SerializableHttpException(403, sprintf('The current user is not allowed to PATCH the selected field (%s).', $destination_field_list->getName()));
+          throw new EntityAccessDeniedHttpException($destination, $field_access, '/data/attributes/' . $field_name, sprintf('The current user is not allowed to PATCH the selected field (%s).', $field_name));
         }
         $destination->{$field_name} = $origin->get($field_name);
       }
@@ -726,11 +730,7 @@ class EntityResource {
     ];
     if ($entity instanceof AccessibleInterface && !$access->isAllowed()) {
       // Pass an exception to the list of things to normalize.
-      $output['entity'] = new SerializableHttpException(403, sprintf(
-        'Access checks failed for entity %s:%s.',
-        $entity->getEntityTypeId(),
-        $entity->id()
-      ));
+      $output['entity'] = new EntityAccessDeniedHttpException($entity, $access, '/data', 'The current user is not allowed to GET the selected resource.');
     }
 
     return $output;
diff --git a/src/Exception/EntityAccessDeniedHttpException.php b/src/Exception/EntityAccessDeniedHttpException.php
new file mode 100644
index 0000000..4db9698
--- /dev/null
+++ b/src/Exception/EntityAccessDeniedHttpException.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\jsonapi\Exception;
+
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Access\AccessResultReasonInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+class EntityAccessDeniedHttpException extends SerializableHttpException {
+
+  /**
+   * The error which caused the 403.
+   *
+   * The error contains:
+   *   - entity: The entity which the current user doens't have access to.
+   *   - pointer: A path in the JSON API response structure pointing to the
+   *     entity.
+   *   - reason: (Optional) An optional reason for this failure.
+   *
+   * @var array
+   */
+  protected $error = [];
+
+  /**
+   * EntityAccessDeniedHttpException constructor.
+   *
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param \Drupal\Core\Access\AccessResultInterface $entity_access
+   *   The access result.
+   * @param string $pointer
+   *   (optional) The pointer.
+   * @param string $messsage
+   *   (Optional) The display to display.
+   * @param \Exception|null $previous
+   *   The previous exception
+   * @param array $headers
+   *   The headers.
+   * @param int $code
+   *   The code.
+   */
+  public function __construct(EntityInterface $entity, AccessResultInterface $entity_access, $pointer, $messsage = 'The current user is not allowed to GET the selected resource.', \Exception $previous = NULL, array $headers = array(), $code = 0) {
+    parent::__construct(403, $messsage, $previous, $headers, $code);
+
+    $error = [
+      'entity' => $entity,
+      'pointer' => $pointer,
+      'reason' => NULL,
+    ];
+    if ($entity_access instanceof AccessResultReasonInterface) {
+      $error['reason'] = $entity_access->getReason();
+    }
+    $this->error = $error;
+  }
+
+  /**
+   * Returns the error.
+   *
+   * @return array
+   */
+  public function getError() {
+    return $this->error;
+  }
+
+}
diff --git a/src/Normalizer/EntityAccessDeniedHttpExceptionNormalizer.php b/src/Normalizer/EntityAccessDeniedHttpExceptionNormalizer.php
new file mode 100644
index 0000000..9626f74
--- /dev/null
+++ b/src/Normalizer/EntityAccessDeniedHttpExceptionNormalizer.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\jsonapi\Normalizer;
+
+use Drupal\jsonapi\Exception\EntityAccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+
+/**
+ * Normalizes an EntityAccessDeniedException object for JSON output which
+ * complies with the JSON API specification. A source pointer is added to help
+ * client applications report to know which entity was access denied.
+ *
+ * @see http://jsonapi.org/format/#error-objects
+ */
+class EntityAccessDeniedHttpExceptionNormalizer extends HttpExceptionNormalizer {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = EntityAccessDeniedHttpException::class;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildErrorObjects(HttpException $exception) {
+    $errors = parent::buildErrorObjects($exception);
+
+    if ($exception instanceof EntityAccessDeniedHttpException) {
+      $error = $exception->getError();
+      /** @var \Drupal\Core\Entity\EntityInterface $entity */
+      $entity = $error['entity'];
+      $pointer = $error['pointer'];
+      $reason = $error['reason'];
+
+      $errors[0]['id'] = sprintf(
+        '/%s--%s/%s',
+        $entity->getEntityTypeId(),
+        $entity->bundle(),
+        $entity->uuid()
+      );
+      $errors[0]['source']['pointer'] = $pointer;
+
+      if ($reason) {
+        $errors[0]['detail']['message'] = $reason;
+      }
+    }
+
+    return $errors;
+  }
+
+}
diff --git a/tests/src/Functional/JsonApiFunctionalTest.php b/tests/src/Functional/JsonApiFunctionalTest.php
index 02e8517..6c28a2b 100644
--- a/tests/src/Functional/JsonApiFunctionalTest.php
+++ b/tests/src/Functional/JsonApiFunctionalTest.php
@@ -15,7 +15,10 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
    * Test the GET method.
    */
   public function testRead() {
-    $this->createDefaultContent(60, 5, TRUE, TRUE, static::IS_NOT_MULTILINGUAL);
+    $this->createDefaultContent(61, 5, TRUE, TRUE, static::IS_NOT_MULTILINGUAL);
+    // Unpublish the last entity, so we can check access.
+    $this->nodes[60]->setUnpublished()->save();
+
     // 1. Load all articles (1st page).
     $collection_output = Json::decode($this->drupalGet('/jsonapi/node/article'));
     $this->assertSession()->statusCodeEquals(200);
@@ -53,6 +56,14 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $this->assertSession()->statusCodeEquals(200);
     $this->assertArrayHasKey('type', $single_output['data']);
     $this->assertEquals($this->nodes[0]->getTitle(), $single_output['data']['attributes']['title']);
+
+    // 5.1 Single article with access denied.
+    $single_output = Json::decode($this->drupalGet('/jsonapi/node/article/' . $this->nodes[60]->uuid()));
+    $this->assertSession()->statusCodeEquals(403);
+
+    $this->assertEquals('/data', $single_output['errors'][0]['source']['pointer']);
+    $this->assertEquals('/node--article/' . $this->nodes[60]->uuid(), $single_output['errors'][0]['id']);
+
     // 6. Single relationship item.
     $single_output = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid . '/relationships/type'));
     $this->assertSession()->statusCodeEquals(200);
@@ -127,6 +138,8 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $this->assertEquals(1, count($single_output['data']));
     $this->assertEquals(1, count($single_output['meta']['errors']));
     $this->assertEquals(403, $single_output['meta']['errors'][0]['status']);
+    $this->assertEquals('/node--article/' . $this->nodes[1]->uuid(), $single_output['meta']['errors'][0]['id']);
+    $this->assertFalse(empty($single_output['meta']['errors'][0]['source']['pointer']));
     $this->nodes[1]->set('status', TRUE);
     $this->nodes[1]->save();
     // 13. Test filtering when using short syntax.
@@ -294,6 +307,7 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $this->assertArrayHasKey('uuid', $created_response['data']['attributes']);
     $uuid = $created_response['data']['attributes']['uuid'];
     $this->assertEquals(2, count($created_response['data']['relationships']['field_tags']['data']));
+
     // 2. Authorization error.
     $response = $this->request('POST', $collection_url, [
       'body' => Json::encode($body),
@@ -303,6 +317,18 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $this->assertEquals(403, $response->getStatusCode());
     $this->assertNotEmpty($created_response['errors']);
     $this->assertEquals('Forbidden', $created_response['errors'][0]['title']);
+
+    // 2.1 Authorization error with a user without create permissions.
+    $response = $this->request('POST', $collection_url, [
+      'body' => Json::encode($body),
+      'auth' => [$this->userCanViewProfiles->getUsername(), $this->userCanViewProfiles->pass_raw],
+      'headers' => ['Content-Type' => 'application/vnd.api+json'],
+    ]);
+    $created_response = Json::decode($response->getBody()->__toString());
+    $this->assertEquals(403, $response->getStatusCode());
+    $this->assertNotEmpty($created_response['errors']);
+    $this->assertEquals('Forbidden', $created_response['errors'][0]['title']);
+
     // 3. Missing Content-Type error.
     $response = $this->request('POST', $collection_url, [
       'body' => Json::encode($body),
@@ -365,6 +391,25 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $updated_response = Json::decode($response->getBody()->__toString());
     $this->assertEquals(200, $response->getStatusCode());
     $this->assertEquals('My updated title', $updated_response['data']['attributes']['title']);
+
+    // 7.1 Unsuccessful PATCH due to access restrictions.
+    $body = [
+      'data' => [
+        'id' => $uuid,
+        'type' => 'node--article',
+        'attributes' => ['title' => 'My updated title'],
+      ],
+    ];
+    $individual_url = Url::fromRoute('jsonapi.node--article.individual', [
+      'node' => $uuid,
+    ]);
+    $response = $this->request('PATCH', $individual_url, [
+      'body' => Json::encode($body),
+      'auth' => [$this->userCanViewProfiles->getUsername(), $this->userCanViewProfiles->pass_raw],
+      'headers' => ['Content-Type' => 'application/vnd.api+json'],
+    ]);
+    $this->assertEquals(403, $response->getStatusCode());
+
     // 8. Field access forbidden check.
     $body = [
       'data' => [
@@ -384,6 +429,7 @@ class JsonApiFunctionalTest extends JsonApiFunctionalTestBase {
     $updated_response = Json::decode($response->getBody()->__toString());
     $this->assertEquals(403, $response->getStatusCode());
     $this->assertEquals('The current user is not allowed to PATCH the selected field (status).', $updated_response['errors'][0]['detail']);
+
     $node = \Drupal::entityManager()->loadEntityByUuid('node', $uuid);
     $this->assertEquals(1, $node->get('status')->value, 'Node status was not changed.');
     // 9. Successful POST to related endpoint.
diff --git a/tests/src/Kernel/Controller/EntityResourceTest.php b/tests/src/Kernel/Controller/EntityResourceTest.php
index 93e8609..d37dfd3 100644
--- a/tests/src/Kernel/Controller/EntityResourceTest.php
+++ b/tests/src/Kernel/Controller/EntityResourceTest.php
@@ -241,7 +241,8 @@ class EntityResourceTest extends JsonapiKernelTestBase {
       $this->container->get('jsonapi.query_builder'),
       $field_manager,
       $current_context,
-      $this->container->get('plugin.manager.field.field_type')
+      $this->container->get('plugin.manager.field.field_type'),
+      $this->container->get('entity.repository')
     );
 
     // Get the response.
@@ -294,7 +295,8 @@ class EntityResourceTest extends JsonapiKernelTestBase {
       $this->container->get('jsonapi.query_builder'),
       $field_manager,
       $current_context,
-      $this->container->get('plugin.manager.field.field_type')
+      $this->container->get('plugin.manager.field.field_type'),
+      $this->container->get('entity.repository')
     );
 
     // Get the response.
@@ -348,7 +350,8 @@ class EntityResourceTest extends JsonapiKernelTestBase {
       $this->container->get('jsonapi.query_builder'),
       $field_manager,
       $current_context,
-      $this->container->get('plugin.manager.field.field_type')
+      $this->container->get('plugin.manager.field.field_type'),
+      $this->container->get('entity.repository')
     );
 
     // Get the response.
@@ -851,7 +854,8 @@ class EntityResourceTest extends JsonapiKernelTestBase {
       $this->container->get('jsonapi.query_builder'),
       $this->container->get('entity_field.manager'),
       $current_context,
-      $this->container->get('plugin.manager.field.field_type')
+      $this->container->get('plugin.manager.field.field_type'),
+      $this->container->get('entity.repository')
     );
   }
 
diff --git a/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php b/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php
index 9316bcf..f896ab5 100644
--- a/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php
+++ b/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php
@@ -220,7 +220,7 @@ class JsonApiDocumentTopLevelNormalizerTest extends JsonapiKernelTestBase {
       ],
     ], $normalized['data']['relationships']['uid']);
     $this->assertEquals(
-      'Access checks failed for entity user:' . $this->user->id() . '.',
+      'The current user is not allowed to GET the selected resource.',
       $normalized['meta']['errors'][0]['detail']
     );
     $this->assertEquals(403, $normalized['meta']['errors'][0]['status']);
