diff --git a/modules/authX509/dictionaries/X509warning.definition.json b/modules/authX509/dictionaries/X509warning.definition.json
new file mode 100644
index 0000000000000000000000000000000000000000..e74d3077517bc60b78b8ff54d9f4baef80114fa4
--- /dev/null
+++ b/modules/authX509/dictionaries/X509warning.definition.json
@@ -0,0 +1,17 @@
+{
+  "warning": {
+    "en": "Your certificate will expire in %days% days."
+  },
+  "warning_header": {
+    "en": "Your certificate is about to expire."
+  },
+  "renew": {
+    "en": "Please renew your certificate in time."
+  },
+  "renew_url": {
+  "en": "Please  <a href='%renewurl%'>renew<\/a> your certificate in time."
+},
+  "proceed": {
+    "en": "Proceed"
+  }
+}
diff --git a/modules/authX509/dictionaries/X509warning.translation.json b/modules/authX509/dictionaries/X509warning.translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..9f42e72d39f5fc8d715a96c4da23c445eb97a91c
--- /dev/null
+++ b/modules/authX509/dictionaries/X509warning.translation.json
@@ -0,0 +1,32 @@
+{
+  "warning": {
+    "nl": "Je certificaat verloopt over %days% dagen.",
+    "no": "Sertifikatet ditt vil utløpe om %days% dager.",
+    "da": "Dit certifikat udløber om %days% dage.",
+    "es": "Su certificado caduca en %days% dĂ­as."
+  },
+  "warning_header": {
+    "nl": "Je certificaat verloopt binnenkort.",
+    "no": "Sertifikatet ditt vil snart utløpe.",
+    "da": "Dit certifikat udløber om kort tid",
+    "es": "Su certificado está a punto de caducar."
+  },
+  "renew": {
+    "nl": "Vervang tijdig je certificaat.",
+    "no": "Vennligst forny sertifikatet ditt før det utløper.",
+    "da": "Forny venligst dit certifikat i tide.",
+    "es": "Por favor, renueve su certificado a tiempo."
+  },
+  "renew_url": {
+    "nl": "<a href='%renewurl%'>Vervang<\/a> tijdig je certificaat.",
+    "no": "Vennligst <a href=‘%renewurl%’>forny<\/a> sertifikatet ditt før det utløper.",
+    "da": "<a href='%renewurl%'>Forny<\/a>, venligst dit certifikat før det udløber.",
+    "es": "Por favor, <a href=‘%renewurl%’>renueve<\/a> su certificado a tiempo."
+  },
+  "proceed": {
+    "nl": "Verder",
+    "no": "Fortsett",
+    "da": "Fortsæt",
+    "es": "Continuar"
+  }
+}
diff --git a/modules/authX509/docs/authX509.txt b/modules/authX509/docs/authX509.txt
index 8cb3ff352fe044ccd0e4a1d7bdf0f009e1afc188..0f3d12621ebf35e5eab41d73e82684e9616d2053 100644
--- a/modules/authX509/docs/authX509.txt
+++ b/modules/authX509/docs/authX509.txt
@@ -106,3 +106,21 @@ can hack your metadata/saml20-idp-hosted.php file that way:
                             'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
     )
 
+Checking certificate expiry
+===========================
+
+To issue warnings to users whos certificate is about to expire, configure an authproc filter.
+
+Example:
+
+     10 => array(
+         'class' => 'authX509:ExpiryWarning',
+         'warndaysbefore' => '30',
+         'renewurl' => 'https://myca.com/renew',
+     ),
+
+Parameter `warndaysbefore` specifies the number of days the user's certificate needs to be valid before a warning is
+issued. The default is 30.
+
+Parameter `renewurl` specifies the URL of your Certification Authority. If specified, the user is suggested to renew the
+certificate immediately.
diff --git a/modules/authX509/lib/Auth/Process/ExpiryWarning.php b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
new file mode 100644
index 0000000000000000000000000000000000000000..259228e77062ac5531d0e62ce81c84208992dd5a
--- /dev/null
+++ b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Filter which shows a warning if the user's client certificate is about to expire.
+ *
+ ** <code>
+ * // show about2xpire warning if client certificate is about to expire
+ * 10 => array(
+ *     'class' => 'authX509:ExpiryWarning',
+ *     'warndaysbefore' => '30',
+ * ),
+ * </code>
+ *
+ * @author Joost van Dijk, SURFnet. <Joost.vanDijk@surfnet.nl>
+ * @package simpleSAMLphp
+ */
+class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_ProcessingFilter {
+
+    private $warndaysbefore = 30;
+    private $renewurl = null;
+
+    /**
+     * Initialize this filter.
+     *
+     * @param array $config  Configuration information about this filter.
+     * @param mixed $reserved  For future use.
+     */
+    public function __construct($config, $reserved) {
+        parent::__construct($config, $reserved);
+
+        assert('is_array($config)');
+
+        if (array_key_exists('warndaysbefore', $config)) {
+            $this->warndaysbefore = $config['warndaysbefore'];
+            if (!is_string($this->warndaysbefore)) {
+                throw new Exception('Invalid value for \'warndaysbefore\'-option to authX509::ExpiryWarning filter.');
+            }
+        }
+
+        if (array_key_exists('renewurl', $config)) {
+            $this->renewurl = $config['renewurl'];
+            if (!is_string($this->renewurl)) {
+                throw new Exception('Invalid value for \'renewurl\'-option to authX509::ExpiryWarning filter.');
+            }
+        }
+    }
+
+    /**
+     * Process an authentication response.
+     *
+     * This function saves the state, and if necessary redirects the user to the page where the user
+     * is informed about the expiry date of his/her certificate.
+     *
+     * @param array $state  The state of the response.
+     */
+    public function process(&$state) {
+        assert('is_array($state)');
+
+        if (isset($state['isPassive']) && $state['isPassive'] === TRUE) {
+            /* We have a passive request. Skip the warning. */
+            return;
+        }
+
+        if (!isset($_SERVER['SSL_CLIENT_CERT']) ||
+            ($_SERVER['SSL_CLIENT_CERT'] == '')) {
+            return;
+        }
+
+        $client_cert = $_SERVER['SSL_CLIENT_CERT'];
+        $client_cert_data = openssl_x509_parse($client_cert);
+        if ($client_cert_data == FALSE) {
+            SimpleSAML_Logger::error('authX509: invalid cert');
+            return;
+        }
+        $validTo = $client_cert_data['validTo_time_t'];
+        $now = time();
+        $daysleft = (int)(($validTo - $now) / (24*60*60));
+        if ($daysleft > $this->warndaysbefore) {
+            /* We have a certificate that will be valid for some time. Skip the warning. */
+            return;
+        }
+
+        SimpleSAML_Logger::warning('authX509: user certificate expires in ' . $daysleft . ' days');
+        $state['daysleft'] = $daysleft;
+        $state['renewurl'] = $this->renewurl;
+
+        /* Save state and redirect. */
+        $id = SimpleSAML_Auth_State::saveState($state, 'warning:expire');
+        $url = SimpleSAML_Module::getModuleURL('authX509/expirywarning.php');
+        \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id));
+    }
+
+}
diff --git a/modules/authX509/templates/X509warning.php b/modules/authX509/templates/X509warning.php
new file mode 100644
index 0000000000000000000000000000000000000000..496775065adcf52bca2be4486f0d65bf361fc924
--- /dev/null
+++ b/modules/authX509/templates/X509warning.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Template form for X509 warnings.
+ *
+ * Parameters:
+ * - 'target': Target URL for the continue-button.
+ * - 'data': Parameters which should be included in the request.
+ *
+ * @package simpleSAMLphp
+ */
+
+$warning = $this->t('{authX509:X509warning:warning}', array(
+    '%days%' => htmlspecialchars($this->data['daysleft']),
+));
+
+if( $this->data['renewurl']) {
+    $warning .= " " . $this->t('{authX509:X509warning:renew_url}', array(
+        '%renewurl%' => $this->data['renewurl'],
+    ));
+} else {
+    $warning .= " " . $this->t('{authX509:X509warning:renew}');
+}
+
+$this->data['header'] = $this->t('{authX509:X509warning:warning_header}');
+$this->data['autofocus'] = 'proceedbutton';
+
+$this->includeAtTemplateBase('includes/header.php');
+
+?>
+
+<form style="display: inline; margin: 0px; padding: 0px" action="<?php echo htmlspecialchars($this->data['target']); ?>">
+
+    <?php
+        // Embed hidden fields...
+        foreach ($this->data['data'] as $name => $value) {
+            echo('<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />');
+        }
+    ?>
+    <p><?php echo $warning; ?></p>
+
+    <input type="submit" name="proceed" id="proceedbutton" value="<?php echo htmlspecialchars($this->t('{authX509:X509warning:proceed}')) ?>" />
+
+</form>
+
+
+<?php
+$this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/authX509/www/expirywarning.php b/modules/authX509/www/expirywarning.php
new file mode 100644
index 0000000000000000000000000000000000000000..200b76c9f3c10cf36214e381af26eeab2f8ff96b
--- /dev/null
+++ b/modules/authX509/www/expirywarning.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * This script warns a user that his/her certificate is about to expire.
+ *
+ * @package simpleSAMLphp
+ */
+
+SimpleSAML_Logger::info('AuthX509 - Showing expiry warning to user');
+
+if (!array_key_exists('StateId', $_REQUEST)) {
+    throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.');
+}
+$id = $_REQUEST['StateId'];
+$state = SimpleSAML_Auth_State::loadState($id, 'warning:expire');
+
+
+if (array_key_exists('proceed', $_REQUEST)) {
+    /* The user has pressed the proceed-button. */
+    SimpleSAML_Auth_ProcessingChain::resumeProcessing($state);
+}
+
+$globalConfig = SimpleSAML_Configuration::getInstance();
+
+$t = new SimpleSAML_XHTML_Template($globalConfig, 'authX509:X509warning.php');
+$t->data['target'] = SimpleSAML_Module::getModuleURL('authX509/expirywarning.php');
+$t->data['data'] = array('StateId' => $id);
+$t->data['daysleft'] = $state['daysleft'];
+$t->data['renewurl'] = $state['renewurl'];
+$t->show();