diff --git a/src/Authorization/AbstractAuthorizationService.php b/src/Authorization/AbstractAuthorizationService.php
index 20dec23e44ca6c45c65eeca0b6bab3249dbfe497..9bc4be20ccc7784ed28ee4d34a50bd881c8f9ba7 100644
--- a/src/Authorization/AbstractAuthorizationService.php
+++ b/src/Authorization/AbstractAuthorizationService.php
@@ -7,19 +7,23 @@ namespace Dbp\Relay\CoreBundle\Authorization;
 use Dbp\Relay\CoreBundle\API\UserSessionInterface;
 use Dbp\Relay\CoreBundle\Exception\ApiError;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
 use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
+use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
+use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
 use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
 use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
 
-abstract class AbstractAuthorizationService implements ContextAwareNormalizerInterface, NormalizerAwareInterface
+abstract class AbstractAuthorizationService implements ContextAwareNormalizerInterface, NormalizerAwareInterface, ContextAwareDenormalizerInterface, DenormalizerAwareInterface
 {
     use NormalizerAwareTrait;
+    use DenormalizerAwareTrait;
 
     /* internal array keys */
     private const ENTITY_SHORT_NAME_KEY = 'short_name';
     private const ENTITY_CLASS_NAME_KEY = 'class_name';
 
-    private const ENTITY_READ_ACCESS_ATTRIBUTE_NAMES_KEY = 'read_attribute_names';
+    private const ENTITY_ATTRIBUTE_NAMES_KEY = 'attribute_names';
     private const ENTITY_OBJECT_ALIAS = 'entity';
     private const CONTEXT_GROUPS_KEY = 'groups';
 
@@ -33,11 +37,15 @@ abstract class AbstractAuthorizationService implements ContextAwareNormalizerInt
     private $config;
 
     /** @var array */
-    private $entityClassNameToAttributeNamesMapping;
+    private $entityClassNameToReadAttributeNamesMapping;
+
+    /** @var array */
+    private $entityClassNameToWriteAttributeNamesMapping;
 
     public function __construct()
     {
-        $this->entityClassNameToAttributeNamesMapping = [];
+        $this->entityClassNameToReadAttributeNamesMapping = [];
+        $this->entityClassNameToWriteAttributeNamesMapping = [];
     }
 
     /**
@@ -96,43 +104,83 @@ abstract class AbstractAuthorizationService implements ContextAwareNormalizerInt
     }
 
     /**
-     *  {@inheritDoc}
+     * {@inheritdoc}
      */
     public function normalize($object, $format = null, array $context = [])
     {
         $entityClassName = get_class($object);
-        $mapEntry = $this->entityClassNameToAttributeNamesMapping[$entityClassName];
+        $mapEntry = $this->entityClassNameToReadAttributeNamesMapping[$entityClassName];
         $entityShortName = $mapEntry[self::ENTITY_SHORT_NAME_KEY];
 
-        foreach ($mapEntry[self::ENTITY_READ_ACCESS_ATTRIBUTE_NAMES_KEY] as $attributeName) {
-            $attributeId = self::toAttributeId($entityShortName, $attributeName);
+        foreach ($mapEntry[self::ENTITY_ATTRIBUTE_NAMES_KEY] as $attributeName) {
+            $attributeId = self::toReadAttributeId($entityShortName, $attributeName);
             if ($this->isGranted($attributeId, $object, self::ENTITY_OBJECT_ALIAS)) {
                 $context[self::CONTEXT_GROUPS_KEY][] = $attributeId;
             }
         }
 
-        $context[self::getUniqueAlreadyCalledKeyForEntity($entityClassName)] = true;
+        $context[self::getUniqueNormalizerAlreadyCalledKeyForEntity($entityClassName)] = true;
 
         return $this->normalizer->normalize($object, $format, $context);
     }
 
     /**
-     *  {@inheritDoc}
+     *  {@inheritdoc}
      */
     public function supportsNormalization($data, $format = null, array $context = []): bool
     {
-        if ($this->entityClassNameToAttributeNamesMapping === null || is_object($data) === false) {
+        if ($this->entityClassNameToReadAttributeNamesMapping === null || is_object($data) === false) {
             return false;
         }
 
         $entityClassName = get_class($data);
 
         // Make sure we're not called twice
-        if (isset($context[self::getUniqueAlreadyCalledKeyForEntity($entityClassName)])) {
+        if (isset($context[self::getUniqueNormalizerAlreadyCalledKeyForEntity($entityClassName)])) {
             return false;
         }
 
-        return array_key_exists($entityClassName, $this->entityClassNameToAttributeNamesMapping);
+        return array_key_exists($entityClassName, $this->entityClassNameToReadAttributeNamesMapping);
+    }
+
+    /**
+     * @return mixed
+     */
+    public function denormalize($data, string $type, string $format = null, array $context = [])
+    {
+        $entityClassName = $type;
+        $mapEntry = $this->entityClassNameToReadAttributeNamesMapping[$entityClassName];
+        $entityShortName = $mapEntry[self::ENTITY_SHORT_NAME_KEY];
+
+        foreach ($mapEntry[self::ENTITY_ATTRIBUTE_NAMES_KEY] as $attributeName) {
+            $attributeId = self::toWriteAttributeId($entityShortName, $attributeName);
+            if ($this->isGranted($attributeId, $data, self::ENTITY_OBJECT_ALIAS)) {
+                $context[self::CONTEXT_GROUPS_KEY][] = $attributeId;
+            }
+        }
+
+        $context[self::getUniqueDenormalizerAlreadyCalledKeyForEntity($entityClassName)] = true;
+
+        return $this->denormalizer->denormalize($data, $type, $format, $context);
+    }
+
+    /**
+     *  {@inheritdoc}
+     */
+    public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool
+    {
+        if ($this->entityClassNameToWriteAttributeNamesMapping === null) {
+            return false;
+        }
+
+        $entityClassName = $type;
+
+        // Make sure we're not called twice
+        if (isset($context[self::getUniqueDenormalizerAlreadyCalledKeyForEntity($entityClassName)])) {
+            return false;
+        }
+
+        return array_key_exists($entityClassName, $this->entityClassNameToWriteAttributeNamesMapping);
     }
 
     private function loadConfig()
@@ -156,13 +204,23 @@ abstract class AbstractAuthorizationService implements ContextAwareNormalizerInt
         foreach ($entitiesConfigNode as $entityShortName => $entityNode) {
             $entityClassName = $entityNode[AuthorizationConfigDefinition::ENTITY_CLASS_NAME_CONFIG_NODE];
             $attributeNames = [];
+
             foreach ($entityNode[AuthorizationConfigDefinition::ENTITY_READ_ACCESS_CONFIG_NODE] ?? [] as $attributeName => $attributeAuthorizationExpression) {
-                $roleExpressions[self::toAttributeId($entityShortName, $attributeName)] = $attributeAuthorizationExpression;
+                $roleExpressions[self::toReadAttributeId($entityShortName, $attributeName)] = $attributeAuthorizationExpression;
                 $attributeNames[] = $attributeName;
             }
-            $this->entityClassNameToAttributeNamesMapping[$entityClassName] = [
+            $this->entityClassNameToReadAttributeNamesMapping[$entityClassName] = [
                 self::ENTITY_SHORT_NAME_KEY => $entityShortName,
-                self::ENTITY_READ_ACCESS_ATTRIBUTE_NAMES_KEY => $attributeNames,
+                self::ENTITY_ATTRIBUTE_NAMES_KEY => $attributeNames,
+            ];
+
+            foreach ($entityNode[AuthorizationConfigDefinition::ENTITY_WRITE_ACCESS_CONFIG_NODE] ?? [] as $attributeName => $attributeAuthorizationExpression) {
+                $roleExpressions[self::toWriteAttributeId($entityShortName, $attributeName)] = $attributeAuthorizationExpression;
+                $attributeNames[] = $attributeName;
+            }
+            $this->entityClassNameToWriteAttributeNamesMapping[$entityClassName] = [
+                self::ENTITY_SHORT_NAME_KEY => $entityShortName,
+                self::ENTITY_ATTRIBUTE_NAMES_KEY => $attributeNames,
             ];
         }
 
@@ -182,13 +240,23 @@ abstract class AbstractAuthorizationService implements ContextAwareNormalizerInt
         return $this->userAuthorizationChecker->isGranted($this->currentAuthorizationUser, $rightName, $object, $objectAlias);
     }
 
-    private static function toAttributeId(string $entityShortName, string $attributeName): string
+    private static function toReadAttributeId(string $entityShortName, string $attributeName): string
+    {
+        return $entityShortName.':output:'.$attributeName;
+    }
+
+    private static function toWriteAttributeId(string $entityShortName, string $attributeName): string
+    {
+        return $entityShortName.':input:'.$attributeName;
+    }
+
+    private static function getUniqueNormalizerAlreadyCalledKeyForEntity(string $entityClassName): string
     {
-        return $entityShortName.':'.$attributeName;
+        return self::class.'.normalize.'.$entityClassName;
     }
 
-    private static function getUniqueAlreadyCalledKeyForEntity(string $entityClassName): string
+    private static function getUniqueDenormalizerAlreadyCalledKeyForEntity(string $entityClassName): string
     {
-        return self::class.$entityClassName;
+        return self::class.'.denormalizer.'.$entityClassName;
     }
 }
diff --git a/src/Authorization/AuthorizationConfigDefinition.php b/src/Authorization/AuthorizationConfigDefinition.php
index 25599e466d33e8db306acb888a34c2008466449c..a101475919fa819f44b3668f52c83bca8a0016e7 100644
--- a/src/Authorization/AuthorizationConfigDefinition.php
+++ b/src/Authorization/AuthorizationConfigDefinition.php
@@ -99,6 +99,7 @@ class AuthorizationConfigDefinition
                 ->defaultValue($entityDefinition[self::ENTITY_CLASS_NAME_KEY])
                 ->info('The entity class name. There is no need to change the default value.')
                 ->end();
+
             $entityReadAccessChildBuilder = $entityChildBuilder->arrayNode(self::ENTITY_READ_ACCESS_CONFIG_NODE)
                 ->children();
             foreach ($entityDefinition[self::ENTITY_READ_ACCESS_KEY] ?? [] as $attributeName) {
@@ -108,7 +109,7 @@ class AuthorizationConfigDefinition
                     ->end();
             }
 
-            $entityWriteAccessChildBuilder = $entitiesNodeChildBuilder->arrayNode(self::ENTITY_WRITE_ACCESS_CONFIG_NODE)
+            $entityWriteAccessChildBuilder = $entityChildBuilder->arrayNode(self::ENTITY_WRITE_ACCESS_CONFIG_NODE)
                 ->children();
             foreach ($entityDefinition[self::ENTITY_WRITE_ACCESS_KEY] ?? [] as $attributeName) {
                 $entityWriteAccessChildBuilder->scalarNode($attributeName)
diff --git a/src/ExpressionLanguage/ExpressionFunctionProviders/ArrayExpressionFunctionProvider.php b/src/ExpressionLanguage/ExpressionFunctionProviders/ArrayExpressionFunctionProvider.php
index 6d4d8891e0c0ae576a1941296e26d649a1ac44d7..e519fedb1211a2392d5c0ff7f6bcc5a9c4e49a26 100644
--- a/src/ExpressionLanguage/ExpressionFunctionProviders/ArrayExpressionFunctionProvider.php
+++ b/src/ExpressionLanguage/ExpressionFunctionProviders/ArrayExpressionFunctionProvider.php
@@ -19,6 +19,13 @@ class ArrayExpressionFunctionProvider implements ExpressionFunctionProviderInter
                 function ($arguments, $varName): bool {
                     return empty($varName);
                 }),
+            new ExpressionFunction('array_key_exists',
+                function (string $keyName, string $arrayName): string {
+                    return sprintf('array_key_exists(%s, %s)', $keyName, $arrayName);
+                },
+                function ($arguments, $keyName, $arrayName): bool {
+                    return array_key_exists($keyName, $arrayName);
+                }),
         ];
     }
 }