diff --git a/scheduler.cron.inc b/scheduler.cron.inc
index 511e979..863a662 100644
--- a/scheduler.cron.inc
+++ b/scheduler.cron.inc
@@ -38,7 +38,7 @@ function _scheduler_publish() {
   $nodes = Node::loadMultiple($nids);
   foreach ($nodes as $nid => $node) {
     // Check that other modules allow the action on this node.
-    if (!_scheduler_allow($node, $action)) {
+    if (!\Drupal::service('scheduler.authorizer')->allow($node, $action)) {
       continue;
     }
 
@@ -120,7 +120,7 @@ function _scheduler_unpublish() {
   $nodes = Node::loadMultiple($nids);
   foreach ($nodes as $nid => $node) {
     // Check that other modules allow the action on this node.
-    if (!_scheduler_allow($node, $action)) {
+    if (!\Drupal::service('scheduler.authorizer')->allow($node, $action)) {
       continue;
     }
 
diff --git a/scheduler.module b/scheduler.module
index 2a49736..1bdccc2 100644
--- a/scheduler.module
+++ b/scheduler.module
@@ -314,7 +314,7 @@ function scheduler_node_presave(EntityInterface $node) {
   $date_formatter = \Drupal::service('date.formatter');
   if ($node->publish_on->value > 0) {
     // Check that other modules allow the action on this node.
-    $publication_allowed = _scheduler_allow($node, 'publish');
+    $publication_allowed = \Drupal::service('scheduler.authorizer')->allow($node, 'publish');
 
     // Publish the node immediately if the publication date is in the past.
     $entity = $node->type->entity;
diff --git a/scheduler.services.yml b/scheduler.services.yml
new file mode 100644
index 0000000..4589c4f
--- /dev/null
+++ b/scheduler.services.yml
@@ -0,0 +1,3 @@
+services:
+  scheduler.authorizer:
+    class: Drupal\scheduler\Authorizer
diff --git a/src/Authorizer.php b/src/Authorizer.php
new file mode 100644
index 0000000..91ab62f
--- /dev/null
+++ b/src/Authorizer.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\scheduler\Authorizer.
+ */
+
+namespace Drupal\scheduler;
+
+use Drupal\node\NodeInterface;
+
+/**
+ * Node publication authorization service.
+ */
+class Authorizer {
+
+  /**
+   * Checks whether a scheduled action on a node is allowed.
+   *
+   * This provides a way for other modules to prevent scheduled publishing or
+   * unpublishing, by implementing hook_scheduler_allow_publishing() or
+   * hook_scheduler_allow_unpublishing().
+   *
+   * @see hook_scheduler_allow_publishing()
+   * @see hook_scheduler_allow_unpublishing()
+   *
+   * @param \Drupal\node\NodeInterface $node
+   *   The node on which the action is to be performed.
+   * @param string $action
+   *   The action that needs to be checked. Can be 'publish' or 'unpublish'.
+   *
+   * @return bool
+   *   TRUE if the action is allowed, FALSE if not.
+   */
+  public function allow(NodeInterface $node, $action) {
+    // Default to TRUE.
+    $result = TRUE;
+    // Check that other modules allow the action.
+    $hook = 'scheduler_allow_' . $action . 'ing';
+    foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) {
+      $function = $module . '_' . $hook;
+      $result &= $function($node);
+    }
+
+    return $result;
+  }
+
+}
