You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2023/03/17 15:02:29 UTC

[syncope] branch 3_0_X updated: [SYNCOPE-1743] Ticket Expiration Policy (#428)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 3_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/3_0_X by this push:
     new 69ca0fe57a [SYNCOPE-1743] Ticket Expiration Policy (#428)
69ca0fe57a is described below

commit 69ca0fe57a19daa7ea38bd518923c93c94430a23
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Fri Mar 17 16:02:21 2023 +0100

    [SYNCOPE-1743] Ticket Expiration Policy (#428)
---
 .../clientapps/ClientAppModalPanelBuilder.java     |  21 +++-
 .../console/commons/AMPolicyTabProvider.java       |  16 ++-
 .../console/commons/AMRealmPolicyProvider.java     |  12 +++
 .../TicketExpirationPolicyDirectoryPanel.java      |  72 ++++++++++++++
 .../policies/TicketExpirationPolicyModalPanel.java | 108 +++++++++++++++++++++
 .../clientapps/ClientAppDirectoryPanel.properties  |   1 +
 .../ClientAppDirectoryPanel_fr_CA.properties       |   1 +
 .../ClientAppDirectoryPanel_it.properties          |   1 +
 .../ClientAppDirectoryPanel_ja.properties          |   1 +
 .../ClientAppDirectoryPanel_pt_BR.properties       |   1 +
 .../ClientAppDirectoryPanel_ru.properties          |   1 +
 .../policies/TicketExpirationPolicyModalPanel.html |  30 ++++++
 .../console/commons/IdMPolicyTabProvider.java      |   5 -
 .../console/commons/IdRepoPolicyTabProvider.java   |   5 -
 .../client/console/commons/PolicyTabProvider.java  |   3 +-
 .../syncope/client/console/pages/Policies.java     |   3 +-
 .../client/console/pages/Policies.properties       |   1 +
 .../client/console/pages/Policies_it.properties    |   1 +
 .../client/console/pages/Policies_ja.properties    |   1 +
 .../client/console/pages/Policies_pt_BR.properties |   1 +
 .../client/console/pages/Policies_ru.properties    |   1 +
 .../client/console/panels/RealmDetails.properties  |   1 +
 .../console/panels/RealmDetails_fr_CA.properties   |   1 +
 .../console/panels/RealmDetails_it.properties      |   1 +
 .../console/panels/RealmDetails_ja.properties      |   1 +
 .../console/panels/RealmDetails_pt_BR.properties   |   1 +
 .../console/panels/RealmDetails_ru.properties      |   1 +
 .../policies/PolicyDirectoryPanel.properties       |   7 +-
 .../policies/PolicyDirectoryPanel_fr_CA.properties |   7 +-
 .../policies/PolicyDirectoryPanel_it.properties    |   7 +-
 .../policies/PolicyDirectoryPanel_ja.properties    |   7 +-
 .../policies/PolicyDirectoryPanel_pt_BR.properties |   7 +-
 .../policies/PolicyDirectoryPanel_ru.properties    |   7 +-
 .../policy/DefaultTicketExpirationPolicyConf.java  | 102 +++++++++++++++++++
 .../lib/policy/TicketExpirationPolicyConf.java     |  14 +--
 .../lib/policy/TicketExpirationPolicyTO.java       |  49 ++++++++++
 .../apache/syncope/common/lib/to/ClientAppTO.java  |  60 +++++++-----
 .../apache/syncope/common/lib/wa/WAClientApp.java  |  11 +++
 .../org/apache/syncope/common/lib/to/RealmTO.java  |  12 +++
 .../syncope/common/lib/types/PolicyType.java       |   4 +
 .../org/apache/syncope/core/logic/PolicyLogic.java |  19 ++--
 .../syncope/core/persistence/api/entity/Realm.java |  11 ++-
 .../core/persistence/api/entity/am/ClientApp.java  |   5 +
 .../api/entity/policy/TicketExpirationPolicy.java  |  14 ++-
 .../persistence/jpa/dao/AbstractClientAppDAO.java  |   3 +
 .../core/persistence/jpa/dao/JPAPolicyDAO.java     |   9 ++
 .../core/persistence/jpa/dao/JPARealmDAO.java      |   3 +
 .../persistence/jpa/entity/JPAEntityFactory.java   |   4 +
 .../core/persistence/jpa/entity/JPARealm.java      |  16 +++
 .../jpa/entity/am/AbstractClientApp.java           |  16 +++
 .../jpa/entity/policy/JPAPolicyUtils.java          |   4 +
 .../jpa/entity/policy/JPAPolicyUtilsFactory.java   |   6 ++
 .../entity/policy/JPATicketExpirationPolicy.java   |  51 ++++++++++
 .../jpa/inner/AbstractClientAppTest.java           |  19 ++++
 .../core/persistence/jpa/inner/PolicyTest.java     |  34 +++++++
 .../core/persistence/jpa/outer/PolicyTest.java     |  20 +++-
 .../java/data/ClientAppDataBinderImpl.java         |  39 ++++++--
 .../java/data/PolicyDataBinderImpl.java            |  20 +++-
 .../java/data/RealmDataBinderImpl.java             |  47 +++++++--
 .../java/data/wa/WAClientAppDataBinderImpl.java    |   8 ++
 .../org/apache/syncope/fit/AbstractITCase.java     |  19 ++++
 .../apache/syncope/fit/console/PoliciesITCase.java |  11 ++-
 .../org/apache/syncope/fit/core/PolicyITCase.java  |  54 +++++++++++
 .../syncope/fit/core/wa/WAClientAppITCase.java     |  11 +++
 pom.xml                                            |   4 +-
 .../concepts/clientapplications.adoc               |   1 +
 .../reference-guide/concepts/policies.adoc         |   9 ++
 .../syncope/wa/starter/config/WAContext.java       |  37 +++++--
 .../starter/mapping/AbstractClientAppMapper.java   |  43 +++++---
 .../wa/starter/mapping/CASSPClientAppTOMapper.java |  13 ++-
 .../wa/starter/mapping/ClientAppMapper.java        |  10 +-
 .../mapping/DefaultTicketExpirationMapper.java     |  92 ++++++++++++++++++
 .../starter/mapping/OIDCRPClientAppTOMapper.java   |  13 ++-
 .../starter/mapping/RegisteredServiceMapper.java   |  35 ++++++-
 .../starter/mapping/SAML2SPClientAppTOMapper.java  |  13 ++-
 .../wa/starter/mapping/TicketExpirationMapFor.java |  18 ++--
 .../wa/starter/mapping/TicketExpirationMapper.java |  36 +++++++
 .../syncope/wa/starter/WAServiceRegistryTest.java  |  12 +++
 78 files changed, 1216 insertions(+), 149 deletions(-)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
index 62a31da3a5..36f1620a89 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
@@ -112,6 +112,17 @@ public class ClientAppModalPanelBuilder<T extends ClientAppTO> extends AbstractM
         }
     };
 
+    private final IModel<Map<String, String>> ticketExpirationPolicies = new LoadableDetachableModel<>() {
+
+        private static final long serialVersionUID = -2012833443695917883L;
+
+        @Override
+        protected Map<String, String> load() {
+            return PolicyRestClient.list(PolicyType.TICKET_EXPIRATION).stream().
+                    collect(Collectors.toMap(PolicyTO::getKey, PolicyTO::getName, (v1, v2) -> v1, LinkedHashMap::new));
+        }
+    };
+
     private final BaseModal<T> modal;
 
     private final ClientAppType type;
@@ -220,10 +231,16 @@ public class ClientAppModalPanelBuilder<T extends ClientAppTO> extends AbstractM
                     "field", "authPolicy", new PropertyModel<>(clientAppTO, "authPolicy"), false);
             authPolicy.setChoiceRenderer(new PolicyRenderer(authPolicies.getObject()));
             authPolicy.setChoices(new ArrayList<>(authPolicies.getObject().keySet()));
-            authPolicy.setRequired(true);
-            ((AbstractSingleSelectChoice<?>) authPolicy.getField()).setNullValid(true);
             fields.add(authPolicy);
 
+            AjaxDropDownChoicePanel<String> ticketExpirationPolicy = new AjaxDropDownChoicePanel<>(
+                    "field", "ticketExpirationPolicy",
+                    new PropertyModel<>(clientAppTO, "ticketExpirationPolicy"), false);
+            ticketExpirationPolicy.setChoiceRenderer(new PolicyRenderer(ticketExpirationPolicies.getObject()));
+            ticketExpirationPolicy.setChoices(new ArrayList<>(ticketExpirationPolicies.getObject().keySet()));
+            ((AbstractSingleSelectChoice<?>) ticketExpirationPolicy.getField()).setNullValid(true);
+            fields.add(ticketExpirationPolicy);
+
             switch (type) {
                 case CASSP:
                     fields.add(new AjaxTextFieldPanel(
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMPolicyTabProvider.java b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMPolicyTabProvider.java
index 488548123a..8fdb177220 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMPolicyTabProvider.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMPolicyTabProvider.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.syncope.client.console.policies.AccessPolicyDirectoryPanel;
 import org.apache.syncope.client.console.policies.AttrReleasePolicyDirectoryPanel;
 import org.apache.syncope.client.console.policies.AuthPolicyDirectoryPanel;
+import org.apache.syncope.client.console.policies.TicketExpirationPolicyDirectoryPanel;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
@@ -33,11 +34,6 @@ public class AMPolicyTabProvider implements PolicyTabProvider {
 
     private static final long serialVersionUID = 2822554006571803418L;
 
-    @Override
-    public int getOrder() {
-        return 200;
-    }
-
     @Override
     public List<ITab> buildTabList(final PageReference pageRef) {
         List<ITab> tabs = new ArrayList<>();
@@ -72,6 +68,16 @@ public class AMPolicyTabProvider implements PolicyTabProvider {
             }
         });
 
+        tabs.add(new AbstractTab(new ResourceModel("policy.ticketExpiration")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new TicketExpirationPolicyDirectoryPanel(panelId, pageRef);
+            }
+        });
+
         return tabs;
     }
 }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMRealmPolicyProvider.java b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMRealmPolicyProvider.java
index bd358e2c47..f58f87a00f 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMRealmPolicyProvider.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMRealmPolicyProvider.java
@@ -76,5 +76,17 @@ public class AMRealmPolicyProvider extends IdRepoRealmPolicyProvider {
         authPolicy.setChoices(authPolicies.keySet().stream().collect(Collectors.toList()));
         ((AbstractSingleSelectChoice<?>) authPolicy.getField()).setNullValid(true);
         view.add(authPolicy);
+
+        Map<String, String> ticketExpirationPolicies = PolicyRestClient.list(PolicyType.TICKET_EXPIRATION).stream().
+                collect(Collectors.toMap(PolicyTO::getKey, PolicyTO::getName, (v1, v2) -> v1, LinkedHashMap::new));
+        AjaxDropDownChoicePanel<String> ticketExpirationPolicy = new AjaxDropDownChoicePanel<>(
+                view.newChildId(),
+                new ResourceModel("ticketExpirationPolicy", "ticketExpirationPolicy").getObject(),
+                new PropertyModel<>(realmTO, "ticketExpirationPolicy"),
+                false);
+        ticketExpirationPolicy.setChoiceRenderer(new PolicyRenderer(ticketExpirationPolicies));
+        ticketExpirationPolicy.setChoices(ticketExpirationPolicies.keySet().stream().collect(Collectors.toList()));
+        ((AbstractSingleSelectChoice<?>) ticketExpirationPolicy.getField()).setNullValid(true);
+        view.add(ticketExpirationPolicy);
     }
 }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyDirectoryPanel.java
new file mode 100644
index 0000000000..f4199fe7bb
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyDirectoryPanel.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.policies;
+
+import org.apache.syncope.client.console.rest.PolicyRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class TicketExpirationPolicyDirectoryPanel extends PolicyDirectoryPanel<TicketExpirationPolicyTO> {
+
+    private static final long serialVersionUID = 4984337552918213290L;
+
+    public TicketExpirationPolicyDirectoryPanel(final String id, final PageReference pageRef) {
+        super(id, PolicyType.TICKET_EXPIRATION, pageRef);
+
+        TicketExpirationPolicyTO defaultItem = new TicketExpirationPolicyTO();
+
+        this.addNewItemPanelBuilder(
+                new PolicyModalPanelBuilder<>(PolicyType.TICKET_EXPIRATION, defaultItem, modal, pageRef), true);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, IdRepoEntitlement.POLICY_CREATE);
+
+        initResultTable();
+    }
+
+    @Override
+    protected void addCustomActions(
+            final ActionsPanel<TicketExpirationPolicyTO> panel,
+            final IModel<TicketExpirationPolicyTO> model) {
+
+        panel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final TicketExpirationPolicyTO ignore) {
+                model.setObject(PolicyRestClient.read(type, model.getObject().getKey()));
+                if (model.getObject().getConf() == null) {
+                    model.getObject().setConf(new DefaultTicketExpirationPolicyConf());
+                }
+                target.add(policySpecModal.setContent(
+                        new TicketExpirationPolicyModalPanel(policySpecModal, model, pageRef)));
+                policySpecModal.header(new Model<>(getString("ticketExpirationPolicyConf.title", model)));
+                policySpecModal.show(true);
+            }
+        }, ActionLink.ActionType.CHANGE_VIEW, IdRepoEntitlement.POLICY_UPDATE);
+    }
+}
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.java
new file mode 100644
index 0000000000..1431c66287
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.policies;
+
+import java.io.Serializable;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.panels.BeanPanel;
+import org.apache.syncope.client.console.rest.PolicyRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+
+public class TicketExpirationPolicyModalPanel extends AbstractModalPanel<TicketExpirationPolicyTO> {
+
+    private static final long serialVersionUID = 2668291404983623500L;
+
+    private final IModel<TicketExpirationPolicyTO> model;
+
+    public TicketExpirationPolicyModalPanel(
+            final BaseModal<TicketExpirationPolicyTO> modal,
+            final IModel<TicketExpirationPolicyTO> model,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.model = model;
+
+        conf("tgtConf", new DefaultTicketExpirationPolicyConf.TGTConf());
+        conf("stConf", new DefaultTicketExpirationPolicyConf.STConf());
+        conf("proxyTgtConf", new DefaultTicketExpirationPolicyConf.TGTConf());
+        conf("proxyStConf", new DefaultTicketExpirationPolicyConf.STConf());
+    }
+
+    private <T extends Serializable> void conf(final String field, final T newInstance) {
+        PropertyModel<T> beanPanelModel = new PropertyModel<>(model.getObject(), "conf." + field);
+
+        AjaxCheckBoxPanel enable = new AjaxCheckBoxPanel("enable." + field, "enable." + field, new IModel<Boolean>() {
+
+            private static final long serialVersionUID = -7126718045816207110L;
+
+            @Override
+            public Boolean getObject() {
+                return beanPanelModel.getObject() != null;
+            }
+
+            @Override
+            public void setObject(final Boolean object) {
+                // nothing to do
+            }
+        });
+        enable.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                if (beanPanelModel.getObject() == null) {
+                    beanPanelModel.setObject(newInstance);
+                } else {
+                    beanPanelModel.setObject(null);
+                }
+                target.add(TicketExpirationPolicyModalPanel.this);
+            }
+        });
+        add(enable);
+
+        add(new BeanPanel<>("bean." + field, beanPanelModel).setRenderBodyOnly(true));
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target) {
+        try {
+            PolicyRestClient.update(PolicyType.TICKET_EXPIRATION, model.getObject());
+
+            SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+            modal.close(target);
+        } catch (Exception e) {
+            LOG.error("While updating Ticket Expiration Policy {}", model.getObject().getKey(), e);
+            SyncopeConsoleSession.get().onException(e);
+        }
+        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+    }
+}
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
index c238d9877b..aa30bbf83c 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.properties
@@ -64,3 +64,4 @@ informationUrl=Information URL
 privacyUrl=Privacy URL
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
index a32e6c0626..cc70107032 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_fr_CA.properties
@@ -64,3 +64,4 @@ informationUrl=Information URL
 privacyUrl=Privacy URL
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
index 61a171ccef..d8257a201b 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_it.properties
@@ -64,3 +64,4 @@ informationUrl=URL informativa
 privacyUrl=URL privacy
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider per ${name}
+ticketExpirationPolicy=Politica Ticket Expiration
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
index 670175063f..58bcac414a 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ja.properties
@@ -64,3 +64,4 @@ informationUrl=Information URL
 privacyUrl=Privacy URL
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
index c238d9877b..aa30bbf83c 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_pt_BR.properties
@@ -64,3 +64,4 @@ informationUrl=Information URL
 privacyUrl=Privacy URL
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
index a88ccce746..9aac43a7e1 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel_ru.properties
@@ -65,3 +65,4 @@ informationUrl=Information URL
 privacyUrl=Privacy URL
 compose.title=username attribute provider
 usernameAttributeProviderConf.title=Username Attribute Provider for ${name}
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.html
new file mode 100644
index 0000000000..7416cfa810
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/TicketExpirationPolicyModalPanel.html
@@ -0,0 +1,30 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" >
+  <wicket:extend>
+    <span wicket:id="enable.tgtConf"/>
+    <span wicket:id="bean.tgtConf"/>
+    <span wicket:id="enable.stConf"/>
+    <span wicket:id="bean.stConf"/>
+    <span wicket:id="enable.proxyTgtConf"/>
+    <span wicket:id="bean.proxyTgtConf"/>
+    <span wicket:id="enable.proxyStConf"/>
+    <span wicket:id="bean.proxyStConf"/>
+  </wicket:extend>
+</html>
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java
index 0f2b92065f..15a10e62bc 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java
@@ -33,11 +33,6 @@ public class IdMPolicyTabProvider implements PolicyTabProvider {
 
     private static final long serialVersionUID = 2822554006571803418L;
 
-    @Override
-    public int getOrder() {
-        return 300;
-    }
-
     @Override
     public List<ITab> buildTabList(final PageReference pageRef) {
         List<ITab> tabs = new ArrayList<>();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoPolicyTabProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoPolicyTabProvider.java
index ebfdf655ec..e22d10104e 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoPolicyTabProvider.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoPolicyTabProvider.java
@@ -32,11 +32,6 @@ public class IdRepoPolicyTabProvider implements PolicyTabProvider {
 
     private static final long serialVersionUID = 5017230231088263303L;
 
-    @Override
-    public int getOrder() {
-        return 100;
-    }
-
     @Override
     public List<ITab> buildTabList(final PageReference pageRef) {
         List<ITab> tabs = new ArrayList<>();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
index 56a2dc7772..c36944da64 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
@@ -22,9 +22,8 @@ import java.io.Serializable;
 import java.util.List;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.springframework.core.Ordered;
 
-public interface PolicyTabProvider extends Serializable, Ordered {
+public interface PolicyTabProvider extends Serializable {
 
     List<ITab> buildTabList(PageReference pageRef);
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Policies.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Policies.java
index 083a41e384..ec680736c3 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Policies.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Policies.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.SyncopeWebApplication;
-import org.apache.syncope.client.console.commons.PolicyTabProvider;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
@@ -43,9 +42,9 @@ public class Policies extends BasePage {
         content.setMarkupId("policies");
 
         List<ITab> tabs = SyncopeWebApplication.get().getPolicyTabProviders().stream().
-                sorted(Comparator.comparing(PolicyTabProvider::getOrder)).
                 map(p -> p.buildTabList(getPageReference())).
                 flatMap(List::stream).
+                sorted(Comparator.comparing(tab -> tab.getTitle().getObject())).
                 collect(Collectors.toList());
         content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", tabs));
 
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties
index b44aedad21..0f8d43457c 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties
@@ -22,3 +22,4 @@ policy.access=Access
 policy.attrRelease=Attribute Release
 policy.auth=Authentication
 policy.propagation=Propagation
+policy.ticketExpiration=Ticket Expiration
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties
index 693aa359f3..db16fe95fe 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties
@@ -22,3 +22,4 @@ policy.access=Accesso
 policy.attrRelease=Rilascio Attributi
 policy.auth=Autenticazione
 policy.propagation=Propagazione
+policy.ticketExpiration=Scadenza Ticket
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties
index 5ed564cade..76212b4d7f 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties
@@ -22,3 +22,4 @@ policy.access=Access
 policy.attrRelease=Attribute Release
 policy.auth=Authentication
 policy.propagation=Propagation
+policy.ticketExpiration=Ticket Expiration
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties
index b44aedad21..0f8d43457c 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties
@@ -22,3 +22,4 @@ policy.access=Access
 policy.attrRelease=Attribute Release
 policy.auth=Authentication
 policy.propagation=Propagation
+policy.ticketExpiration=Ticket Expiration
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties
index 992307bc3d..0d5f9b2223 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties
@@ -23,3 +23,4 @@ policy.access=Access
 policy.attrRelease=Attribute Release
 policy.auth=Authentication
 policy.propagation=Propagation
+policy.ticketExpiration=Ticket Expiration
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails.properties
index 71cf88d0c1..96cd961bcf 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails.properties
@@ -22,3 +22,4 @@ resources=Assigned resources
 authPolicy=Authentication Policy
 accessPolicy=Access Policy
 attrReleasePolicy=Attribute Release Policy
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_fr_CA.properties
index 61d6e5862b..67f58c653a 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_fr_CA.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_fr_CA.properties
@@ -22,3 +22,4 @@ resources=Ressources assign\u00e9es
 authPolicy=Authentication Policy
 accessPolicy=Access Policy
 attrReleasePolicy=Attribute Release Policy
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_it.properties
index 5c053cfe93..6fe3561459 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_it.properties
@@ -22,3 +22,4 @@ resources=Risorse assegnate
 authPolicy=Politica Autenticazione
 accessPolicy=Politica Accesso
 attrReleasePolicy=Politica Rilascio Attributi
+ticketExpirationPolicy=Politica Ticket Expiration
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ja.properties
index 62242de5e6..5c96552b72 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ja.properties
@@ -22,3 +22,4 @@ resources=\u5272\u308a\u5f53\u3066\u3089\u308c\u305f\u30ea\u30bd\u30fc\u30b9
 authPolicy=Authentication Policy
 accessPolicy=Access Policy
 attrReleasePolicy=Attribute Release Policy
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_pt_BR.properties
index 7862d3e6bc..e9a09dbc90 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_pt_BR.properties
@@ -22,3 +22,4 @@ resources=Assigned resources
 authPolicy=Authentication Policy
 accessPolicy=Access Policy
 attrReleasePolicy=Attribute Release Policy
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ru.properties
index 83d69a3f37..dd2b0208f7 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmDetails_ru.properties
@@ -23,3 +23,4 @@ resources=\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u0
 authPolicy=Authentication Policy
 accessPolicy=Access Policy
 attrReleasePolicy=Attribute Release Policy
+ticketExpirationPolicy=Ticket Expiration Policy
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.properties
index 0c4e2d0c6c..5c77be2691 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.properties
@@ -47,5 +47,10 @@ backOffStrategy=BackOff Strategy
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configuration
-accessPolicyConf.title=Access Policy Configuration
+accessPolicyConf.title=Access Policy ${name} Configuration
 releaseAttrs=Attributes to Release
+ticketExpirationPolicyConf.title=Ticket Expiration ${name} Configuration
+enable.tgtConf=Enable TGT
+enable.stConf=Enable ST
+enable.proxyTgtConf=Enable Proxy TGT
+enable.proxyStConf=Enable Proxy ST
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_fr_CA.properties
index 3775e15689..75b107a188 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_fr_CA.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_fr_CA.properties
@@ -47,5 +47,10 @@ backOffStrategy=BackOff Strategy
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configuration
-accessPolicyConf.title=Access Policy Configuration
+accessPolicyConf.title=Access Policy ${name} Configuration
 releaseAttrs=Attributes to Release
+ticketExpirationPolicyConf.title=Ticket Expiration ${name} Configuration
+enable.tgtConf=Enable TGT
+enable.stConf=Enable ST
+enable.proxyTgtConf=Enable Proxy TGT
+enable.proxyStConf=Enable Proxy ST
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_it.properties
index 05615626b7..340c0e4a81 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_it.properties
@@ -47,5 +47,10 @@ backOffStrategy=Strategia di BackOff
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configurazione
-accessPolicyConf.title=Configurazione Accesso
+accessPolicyConf.title=Configurazione Politica di Accesso ${name}
 releaseAttrs=Attributi da Rilasciare
+ticketExpirationPolicyConf.title=Configurazione Ticket Expiration ${name}
+enable.tgtConf=Abilita TGT
+enable.stConf=Abilita ST
+enable.proxyTgtConf=Abilita Proxy TGT
+enable.proxyStConf=Abilita Proxy ST
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ja.properties
index 7e799cd8a9..07a0966201 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ja.properties
@@ -47,5 +47,10 @@ backOffStrategy=BackOff Strategy
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configuration
-accessPolicyConf.title=Access Policy Configuration
+accessPolicyConf.title=Access Policy ${name} Configuration
 releaseAttrs=Attributes to Release
+ticketExpirationPolicyConf.title=Ticket Expiration ${name} Configuration
+enable.tgtConf=Enable TGT
+enable.stConf=Enable ST
+enable.proxyTgtConf=Enable Proxy TGT
+enable.proxyStConf=Enable Proxy ST
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_pt_BR.properties
index 32740d406a..df24f1a1fa 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_pt_BR.properties
@@ -47,5 +47,10 @@ backOffStrategy=BackOff Strategy
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configuration
-accessPolicyConf.title=Access Policy Configuration
+accessPolicyConf.title=Access Policy ${name} Configuration
 releaseAttrs=Attributes to Release
+ticketExpirationPolicyConf.title=Ticket Expiration ${name} Configuration
+enable.tgtConf=Enable TGT
+enable.stConf=Enable ST
+enable.proxyTgtConf=Enable Proxy TGT
+enable.proxyStConf=Enable Proxy ST
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ru.properties
index 895e24d1ff..a0748b7f6d 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/policies/PolicyDirectoryPanel_ru.properties
@@ -48,5 +48,10 @@ backOffStrategy=BackOff Strategy
 fetchAroundProvisioning=Fetch Around Provisioning
 updateDelta=Update Delta
 conf=Configuration
-accessPolicyConf.title=Access Policy Configuration
+accessPolicyConf.title=Access Policy ${name} Configuration
 releaseAttrs=Attributes to Release
+ticketExpirationPolicyConf.title=Ticket Expiration ${name} Configuration
+enable.tgtConf=Enable TGT
+enable.stConf=Enable ST
+enable.proxyTgtConf=Enable Proxy TGT
+enable.proxyStConf=Enable Proxy ST
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultTicketExpirationPolicyConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultTicketExpirationPolicyConf.java
new file mode 100644
index 0000000000..e67740ecbf
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultTicketExpirationPolicyConf.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.policy;
+
+import java.io.Serializable;
+
+public class DefaultTicketExpirationPolicyConf implements TicketExpirationPolicyConf {
+
+    private static final long serialVersionUID = -6177632746685440169L;
+
+    public static class TGTConf implements Serializable {
+
+        private static final long serialVersionUID = 4020295776618112940L;
+
+        /**
+         * TTL of this ticket, in seconds.
+         */
+        private long maxTimeToLiveInSeconds;
+
+        public long getMaxTimeToLiveInSeconds() {
+            return maxTimeToLiveInSeconds;
+        }
+
+        public void setMaxTimeToLiveInSeconds(final long maxTimeToLiveInSeconds) {
+            this.maxTimeToLiveInSeconds = maxTimeToLiveInSeconds;
+        }
+    }
+
+    public static class STConf extends TGTConf {
+
+        private static final long serialVersionUID = -9141008704559934825L;
+
+        /**
+         * Number of times this ticket can be used.
+         */
+        private long numberOfUses;
+
+        public long getNumberOfUses() {
+            return numberOfUses;
+        }
+
+        public void setNumberOfUses(final long numberOfUses) {
+            this.numberOfUses = numberOfUses;
+        }
+    }
+
+    private TGTConf tgtConf;
+
+    private STConf stConf;
+
+    private TGTConf proxyTgtConf;
+
+    private STConf proxyStConf;
+
+    public TGTConf getTgtConf() {
+        return tgtConf;
+    }
+
+    public void setTgtConf(final TGTConf tgtConf) {
+        this.tgtConf = tgtConf;
+    }
+
+    public STConf getStConf() {
+        return stConf;
+    }
+
+    public void setStConf(final STConf stConf) {
+        this.stConf = stConf;
+    }
+
+    public TGTConf getProxyTgtConf() {
+        return proxyTgtConf;
+    }
+
+    public void setProxyTgtConf(final TGTConf proxyTgtConf) {
+        this.proxyTgtConf = proxyTgtConf;
+    }
+
+    public STConf getProxyStConf() {
+        return proxyStConf;
+    }
+
+    public void setProxyStConf(final STConf proxySTConf) {
+        this.proxyStConf = proxySTConf;
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyConf.java
similarity index 69%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyConf.java
index 56a2dc7772..656fb3dd3b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyConf.java
@@ -16,15 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.commons;
+package org.apache.syncope.common.lib.policy;
 
-import java.io.Serializable;
-import java.util.List;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.springframework.core.Ordered;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.syncope.common.lib.BaseBean;
 
-public interface PolicyTabProvider extends Serializable, Ordered {
-
-    List<ITab> buildTabList(PageReference pageRef);
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
+public interface TicketExpirationPolicyConf extends BaseBean {
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyTO.java
new file mode 100644
index 0000000000..04f48c1db5
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/TicketExpirationPolicyTO.java
@@ -0,0 +1,49 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.policy;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(allOf = { PolicyTO.class })
+public class TicketExpirationPolicyTO extends PolicyTO {
+
+    private static final long serialVersionUID = -6711411162433533300L;
+
+    private TicketExpirationPolicyConf conf;
+
+    @JacksonXmlProperty(localName = "_class", isAttribute = true)
+    @JsonProperty("_class")
+    @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED,
+            example = "org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO")
+    @Override
+    public String getDiscriminator() {
+        return getClass().getName();
+    }
+
+    public TicketExpirationPolicyConf getConf() {
+        return conf;
+    }
+
+    public void setConf(final TicketExpirationPolicyConf conf) {
+        this.conf = conf;
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
index 31387574cc..e211d7d84e 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ClientAppTO.java
@@ -70,6 +70,8 @@ public abstract class ClientAppTO implements NamedEntityTO {
 
     private String attrReleasePolicy;
 
+    private String ticketExpirationPolicy;
+
     private final List<Attr> properties = new ArrayList<>();
 
     @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -79,30 +81,6 @@ public abstract class ClientAppTO implements NamedEntityTO {
         // do nothing
     }
 
-    public String getAttrReleasePolicy() {
-        return attrReleasePolicy;
-    }
-
-    public void setAttrReleasePolicy(final String attrReleasePolicy) {
-        this.attrReleasePolicy = attrReleasePolicy;
-    }
-
-    public String getAccessPolicy() {
-        return accessPolicy;
-    }
-
-    public void setAccessPolicy(final String accessPolicy) {
-        this.accessPolicy = accessPolicy;
-    }
-
-    public String getAuthPolicy() {
-        return authPolicy;
-    }
-
-    public void setAuthPolicy(final String authPolicy) {
-        this.authPolicy = authPolicy;
-    }
-
     @Override
     public String getKey() {
         return key;
@@ -188,6 +166,38 @@ public abstract class ClientAppTO implements NamedEntityTO {
         this.usernameAttributeProviderConf = usernameAttributeProviderConf;
     }
 
+    public String getAttrReleasePolicy() {
+        return attrReleasePolicy;
+    }
+
+    public void setAttrReleasePolicy(final String attrReleasePolicy) {
+        this.attrReleasePolicy = attrReleasePolicy;
+    }
+
+    public String getAccessPolicy() {
+        return accessPolicy;
+    }
+
+    public void setAccessPolicy(final String accessPolicy) {
+        this.accessPolicy = accessPolicy;
+    }
+
+    public String getAuthPolicy() {
+        return authPolicy;
+    }
+
+    public void setAuthPolicy(final String authPolicy) {
+        this.authPolicy = authPolicy;
+    }
+
+    public String getTicketExpirationPolicy() {
+        return ticketExpirationPolicy;
+    }
+
+    public void setTicketExpirationPolicy(final String ticketExpirationPolicy) {
+        this.ticketExpirationPolicy = ticketExpirationPolicy;
+    }
+
     @JacksonXmlElementWrapper(localName = "properties")
     @JacksonXmlProperty(localName = "property")
     public List<Attr> getProperties() {
@@ -211,6 +221,7 @@ public abstract class ClientAppTO implements NamedEntityTO {
                 .append(authPolicy)
                 .append(accessPolicy)
                 .append(attrReleasePolicy)
+                .append(ticketExpirationPolicy)
                 .append(properties)
                 .toHashCode();
     }
@@ -242,6 +253,7 @@ public abstract class ClientAppTO implements NamedEntityTO {
                 .append(this.authPolicy, rhs.authPolicy)
                 .append(this.accessPolicy, rhs.accessPolicy)
                 .append(this.attrReleasePolicy, rhs.attrReleasePolicy)
+                .append(this.ticketExpirationPolicy, rhs.ticketExpirationPolicy)
                 .append(this.properties, rhs.properties)
                 .isEquals();
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
index a27e7dfa0c..5780d8c823 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
@@ -26,6 +26,7 @@ import org.apache.syncope.common.lib.BaseBean;
 import org.apache.syncope.common.lib.policy.AccessPolicyTO;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.ClientAppTO;
 
@@ -43,6 +44,8 @@ public class WAClientApp implements BaseBean {
 
     private AttrReleasePolicyTO attrReleasePolicy;
 
+    private TicketExpirationPolicyTO ticketExpirationPolicy;
+
     public ClientAppTO getClientAppTO() {
         return clientAppTO;
     }
@@ -80,4 +83,12 @@ public class WAClientApp implements BaseBean {
     public void setAttrReleasePolicy(final AttrReleasePolicyTO attrReleasePolicy) {
         this.attrReleasePolicy = attrReleasePolicy;
     }
+
+    public TicketExpirationPolicyTO getTicketExpirationPolicy() {
+        return ticketExpirationPolicy;
+    }
+
+    public void setTicketExpirationPolicy(final TicketExpirationPolicyTO ticketExpirationPolicy) {
+        this.ticketExpirationPolicy = ticketExpirationPolicy;
+    }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
index 7673f17d2a..de6ffd676c 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
@@ -50,6 +50,8 @@ public class RealmTO implements NamedEntityTO, TemplatableTO {
 
     private String attrReleasePolicy;
 
+    private String ticketExpirationPolicy;
+
     private final List<String> actions = new ArrayList<>();
 
     private final Map<String, AnyTO> templates = new HashMap<>();
@@ -133,6 +135,14 @@ public class RealmTO implements NamedEntityTO, TemplatableTO {
         this.attrReleasePolicy = attrReleasePolicy;
     }
 
+    public String getTicketExpirationPolicy() {
+        return ticketExpirationPolicy;
+    }
+
+    public void setTicketExpirationPolicy(final String ticketExpirationPolicy) {
+        this.ticketExpirationPolicy = ticketExpirationPolicy;
+    }
+
     @JacksonXmlElementWrapper(localName = "actions")
     @JacksonXmlProperty(localName = "action")
     public List<String> getActions() {
@@ -172,6 +182,7 @@ public class RealmTO implements NamedEntityTO, TemplatableTO {
                 append(authPolicy, other.authPolicy).
                 append(accessPolicy, other.accessPolicy).
                 append(attrReleasePolicy, other.attrReleasePolicy).
+                append(ticketExpirationPolicy, other.ticketExpirationPolicy).
                 append(actions, other.actions).
                 append(templates, other.templates).
                 append(resources, other.resources).
@@ -190,6 +201,7 @@ public class RealmTO implements NamedEntityTO, TemplatableTO {
                 append(authPolicy).
                 append(accessPolicy).
                 append(attrReleasePolicy).
+                append(ticketExpirationPolicy).
                 append(actions).
                 append(templates).
                 append(resources).
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
index e9a71bdd28..383a790c98 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
@@ -40,6 +40,10 @@ public enum PolicyType {
      * How access policies should be defined.
      */
     ACCESS,
+    /**
+     * Controls expiration of access tickets.
+     */
+    TICKET_EXPIRATION,
     /**
      * For handling propagation behavior.
      */
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
index cc92960cb9..b679cb6f6f 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.logic;
 
 import java.lang.reflect.Method;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -68,10 +69,8 @@ public class PolicyLogic extends AbstractTransactionalLogic<PolicyTO> {
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.POLICY_UPDATE + "')")
     public PolicyTO update(final PolicyType type, final PolicyTO policyTO) {
-        Policy policy = policyDAO.find(policyTO.getKey());
-        if (policy == null) {
-            throw new NotFoundException("Policy " + policyTO.getKey() + " not found");
-        }
+        Policy policy = Optional.ofNullable(policyDAO.<Policy>find(policyTO.getKey())).
+                orElseThrow(() -> new NotFoundException("Policy " + policyTO.getKey() + " not found"));
 
         PolicyUtils policyUtils = policyUtilsFactory.getInstance(policy);
         if (policyUtils.getType() != type) {
@@ -95,10 +94,8 @@ public class PolicyLogic extends AbstractTransactionalLogic<PolicyTO> {
     @PreAuthorize("hasRole('" + IdRepoEntitlement.POLICY_READ + "')")
     @Transactional(readOnly = true)
     public <T extends PolicyTO> T read(final PolicyType type, final String key) {
-        Policy policy = policyDAO.find(key);
-        if (policy == null) {
-            throw new NotFoundException("Policy " + key + " not found");
-        }
+        Policy policy = Optional.ofNullable(policyDAO.<Policy>find(key)).
+                orElseThrow(() -> new NotFoundException("Policy " + key + " not found"));
 
         PolicyUtils policyUtils = policyUtilsFactory.getInstance(policy);
         if (type != null && policyUtils.getType() != type) {
@@ -112,10 +109,8 @@ public class PolicyLogic extends AbstractTransactionalLogic<PolicyTO> {
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.POLICY_DELETE + "')")
     public <T extends PolicyTO> T delete(final PolicyType type, final String key) {
-        Policy policy = policyDAO.find(key);
-        if (policy == null) {
-            throw new NotFoundException("Policy " + key + " not found");
-        }
+        Policy policy = Optional.ofNullable(policyDAO.<Policy>find(key)).
+                orElseThrow(() -> new NotFoundException("Policy " + key + " not found"));
 
         PolicyUtils policyUtils = policyUtilsFactory.getInstance(policy);
         if (type != null && policyUtils.getType() != type) {
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
index 42249ce9a4..d513688fd1 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
@@ -25,6 +25,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 
 public interface Realm extends Entity {
 
@@ -46,17 +47,21 @@ public interface Realm extends Entity {
 
     void setPasswordPolicy(PasswordPolicy passwordPolicy);
 
+    AuthPolicy getAuthPolicy();
+
     void setAuthPolicy(AuthPolicy authPolicy);
 
-    AuthPolicy getAuthPolicy();
+    AccessPolicy getAccessPolicy();
 
     void setAccessPolicy(AccessPolicy accessPolicy);
 
-    AccessPolicy getAccessPolicy();
+    AttrReleasePolicy getAttrReleasePolicy();
 
     void setAttrReleasePolicy(AttrReleasePolicy policy);
 
-    AttrReleasePolicy getAttrReleasePolicy();
+    TicketExpirationPolicy getTicketExpirationPolicy();
+
+    void setTicketExpirationPolicy(TicketExpirationPolicy policy);
 
     boolean add(Implementation action);
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
index e82890ee0d..493b74c5fe 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
@@ -26,6 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 
 public interface ClientApp extends Entity {
 
@@ -73,6 +74,10 @@ public interface ClientApp extends Entity {
 
     void setAttrReleasePolicy(AttrReleasePolicy policy);
 
+    TicketExpirationPolicy getTicketExpirationPolicy();
+
+    void setTicketExpirationPolicy(TicketExpirationPolicy policy);
+
     Realm getRealm();
 
     void setRealm(Realm realm);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/TicketExpirationPolicy.java
similarity index 69%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/TicketExpirationPolicy.java
index 56a2dc7772..2b947381fb 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/TicketExpirationPolicy.java
@@ -16,15 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.commons;
+package org.apache.syncope.core.persistence.api.entity.policy;
 
-import java.io.Serializable;
-import java.util.List;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.springframework.core.Ordered;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf;
 
-public interface PolicyTabProvider extends Serializable, Ordered {
+public interface TicketExpirationPolicy extends Policy {
 
-    List<ITab> buildTabList(PageReference pageRef);
+    TicketExpirationPolicyConf getConf();
+
+    void setConf(TicketExpirationPolicyConf conf);
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractClientAppDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractClientAppDAO.java
index 1cfb6dc313..c4bcdb4b23 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractClientAppDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractClientAppDAO.java
@@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 
 public abstract class AbstractClientAppDAO<C extends ClientApp> extends AbstractDAO<C> implements ClientAppDAO<C> {
 
@@ -44,6 +45,8 @@ public abstract class AbstractClientAppDAO<C extends ClientApp> extends Abstract
             query.append("accessPolicy");
         } else if (AttrReleasePolicy.class.isAssignableFrom(policyClass)) {
             query.append("attrReleasePolicy");
+        } else if (TicketExpirationPolicy.class.isAssignableFrom(policyClass)) {
+            query.append("ticketExpirationPolicy");
         }
 
         return query;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
index 63c1bc475d..32b5b03b48 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.Policy;
 import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy;
@@ -50,6 +51,7 @@ import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationR
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy;
 
 public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
 
@@ -70,6 +72,8 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
                 ? JPAAccessPolicy.class
                 : AttrReleasePolicy.class.isAssignableFrom(reference)
                 ? JPAAttrReleasePolicy.class
+                : TicketExpirationPolicy.class.isAssignableFrom(reference)
+                ? JPATicketExpirationPolicy.class
                 : null;
     }
 
@@ -218,6 +222,11 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
             casSPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null));
             oidcRPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null));
             saml2SPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null));
+        } else if (policy instanceof TicketExpirationPolicy) {
+            realmDAO.findByPolicy(policy).forEach(realm -> realm.setTicketExpirationPolicy(null));
+            casSPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null));
+            oidcRPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null));
+            saml2SPClientAppDAO.findByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null));
         }
 
         entityManager().remove(policy);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
index 000824a700..da0ca624bb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
@@ -39,6 +39,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
 import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.ProvisioningPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -160,6 +161,8 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO {
             policyColumn = "accessPolicy";
         } else if (policy instanceof AttrReleasePolicy) {
             policyColumn = "attrReleasePolicy";
+        } else if (policy instanceof TicketExpirationPolicy) {
+            policyColumn = "ticketExpirationPolicy";
         }
 
         TypedQuery<Realm> query = entityManager().createQuery(
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 2e036f2d14..3ca3f275c2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -83,6 +83,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRule
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
 import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
 import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
@@ -136,6 +137,7 @@ import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationR
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAAnyTemplatePullTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask;
@@ -311,6 +313,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAAccessPolicy();
         } else if (reference.equals(AttrReleasePolicy.class)) {
             result = (E) new JPAAttrReleasePolicy();
+        } else if (reference.equals(TicketExpirationPolicy.class)) {
+            result = (E) new JPATicketExpirationPolicy();
         } else if (reference.equals(OIDCRPClientApp.class)) {
             result = (E) new JPAOIDCRPClientApp();
         } else if (reference.equals(CASSPClientApp.class)) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index 0bbe4f191d..95660b2983 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -47,11 +47,13 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RealmCheck;
 
 @Entity
@@ -86,6 +88,9 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @ManyToOne(fetch = FetchType.EAGER)
     private JPAAttrReleasePolicy attrReleasePolicy;
 
+    @ManyToOne(fetch = FetchType.EAGER)
+    private JPATicketExpirationPolicy ticketExpirationPolicy;
+
     @ManyToMany(fetch = FetchType.EAGER)
     @JoinTable(name = TABLE + "Action",
             joinColumns =
@@ -221,6 +226,17 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
         return this.attrReleasePolicy;
     }
 
+    @Override
+    public TicketExpirationPolicy getTicketExpirationPolicy() {
+        return this.ticketExpirationPolicy;
+    }
+
+    @Override
+    public void setTicketExpirationPolicy(final TicketExpirationPolicy policy) {
+        checkType(policy, JPATicketExpirationPolicy.class);
+        this.ticketExpirationPolicy = (JPATicketExpirationPolicy) policy;
+    }
+
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
index 0e4d0dbf90..860ce43690 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
@@ -34,11 +34,13 @@ import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @MappedSuperclass
@@ -80,6 +82,9 @@ public class AbstractClientApp extends AbstractGeneratedKeyEntity implements Cli
     @ManyToOne(fetch = FetchType.EAGER)
     private JPAAttrReleasePolicy attrReleasePolicy;
 
+    @ManyToOne(fetch = FetchType.EAGER)
+    private JPATicketExpirationPolicy ticketExpirationPolicy;
+
     @Lob
     private String properties;
 
@@ -197,6 +202,17 @@ public class AbstractClientApp extends AbstractGeneratedKeyEntity implements Cli
         this.attrReleasePolicy = (JPAAttrReleasePolicy) policy;
     }
 
+    @Override
+    public TicketExpirationPolicy getTicketExpirationPolicy() {
+        return this.ticketExpirationPolicy;
+    }
+
+    @Override
+    public void setTicketExpirationPolicy(final TicketExpirationPolicy policy) {
+        checkType(policy, JPATicketExpirationPolicy.class);
+        this.ticketExpirationPolicy = (JPATicketExpirationPolicy) policy;
+    }
+
     @Override
     public Realm getRealm() {
         return realm;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
index d6584d004d..e6bd285d28 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
@@ -29,6 +29,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtils;
 import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 
 public class JPAPolicyUtils implements PolicyUtils {
 
@@ -61,6 +62,9 @@ public class JPAPolicyUtils implements PolicyUtils {
             case ACCESS:
                 return AccessPolicy.class;
 
+            case TICKET_EXPIRATION:
+                return TicketExpirationPolicy.class;
+
             case PROPAGATION:
                 return PropagationPolicy.class;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
index f13302a680..0a0de4440f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
@@ -27,6 +27,7 @@ import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.policy.PropagationPolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
 import org.apache.syncope.common.lib.policy.PushPolicyTO;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
@@ -39,6 +40,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 
 public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
 
@@ -66,6 +68,8 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
             type = PolicyType.ACCESS;
         } else if (policy instanceof AttrReleasePolicy) {
             type = PolicyType.ATTR_RELEASE;
+        } else if (policy instanceof TicketExpirationPolicy) {
+            type = PolicyType.TICKET_EXPIRATION;
         } else {
             throw new IllegalArgumentException("Invalid policy: " + policy);
         }
@@ -92,6 +96,8 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
             type = PolicyType.ACCESS;
         } else if (policyClass == AttrReleasePolicyTO.class) {
             type = PolicyType.ATTR_RELEASE;
+        } else if (policyClass == TicketExpirationPolicyTO.class) {
+            type = PolicyType.TICKET_EXPIRATION;
         } else {
             throw new IllegalArgumentException("Invalid PolicyTO class: " + policyClass.getName());
         }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java
new file mode 100644
index 0000000000..8f44d5c5ad
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPATicketExpirationPolicy.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.policy;
+
+import java.util.Optional;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPATicketExpirationPolicy.TABLE)
+public class JPATicketExpirationPolicy extends AbstractPolicy implements TicketExpirationPolicy {
+
+    private static final long serialVersionUID = -4190607009908888884L;
+
+    public static final String TABLE = "TicketExpiration";
+
+    @Lob
+    private String jsonConf;
+
+    @Override
+    public TicketExpirationPolicyConf getConf() {
+        return Optional.ofNullable(jsonConf).
+                map(c -> POJOHelper.deserialize(c, TicketExpirationPolicyConf.class)).
+                orElse(null);
+    }
+
+    @Override
+    public void setConf(final TicketExpirationPolicyConf conf) {
+        jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null);
+    }
+}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
index dc601a64f6..53c6668c8e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
@@ -22,10 +22,12 @@ import java.util.List;
 import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -72,4 +74,21 @@ public class AbstractClientAppTest extends AbstractTest {
 
         return policyDAO.save(authPolicy);
     }
+
+    protected TicketExpirationPolicy buildAndSaveTicketExpirationPolicy() {
+        TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class);
+        ticketExpirationPolicy.setName("TicketExpirationPolicyTest");
+
+        DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf();
+        DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf();
+        tgtConf.setMaxTimeToLiveInSeconds(110);
+        conf.setTgtConf(tgtConf);
+        DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf();
+        stConf.setMaxTimeToLiveInSeconds(0);
+        stConf.setNumberOfUses(1);
+        conf.setStConf(stConf);
+        ticketExpirationPolicy.setConf(conf);
+
+        return policyDAO.save(ticketExpirationPolicy);
+    }
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index 3075b983ca..d79748f99b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.BackOffStrategy;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
@@ -52,6 +53,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRule
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule;
 import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule;
@@ -153,6 +155,10 @@ public class PolicyTest extends AbstractTest {
         List<AttrReleasePolicy> attrReleasePolicies = policyDAO.find(AttrReleasePolicy.class);
         assertNotNull(attrReleasePolicies);
         assertEquals(2, attrReleasePolicies.size());
+
+        List<TicketExpirationPolicy> ticketExpirationPolicies = policyDAO.find(TicketExpirationPolicy.class);
+        assertNotNull(ticketExpirationPolicies);
+        assertEquals(0, ticketExpirationPolicies.size());
     }
 
     @Test
@@ -328,6 +334,34 @@ public class PolicyTest extends AbstractTest {
         assertEquals(afterCount, beforeCount + 1);
     }
 
+    @Test
+    public void createTicketExpiration() {
+        int beforeCount = policyDAO.findAll().size();
+
+        TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class);
+        ticketExpirationPolicy.setName("TicketExpirationPolicyTest");
+
+        DefaultTicketExpirationPolicyConf ticketExpirationPolicyConf = new DefaultTicketExpirationPolicyConf();
+        DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf();
+        tgtConf.setMaxTimeToLiveInSeconds(110);
+        ticketExpirationPolicyConf.setTgtConf(tgtConf);
+        DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf();
+        stConf.setMaxTimeToLiveInSeconds(0);
+        stConf.setNumberOfUses(1);
+        ticketExpirationPolicyConf.setStConf(stConf);
+        ticketExpirationPolicy.setConf(ticketExpirationPolicyConf);
+
+        ticketExpirationPolicy = policyDAO.save(ticketExpirationPolicy);
+
+        assertNotNull(ticketExpirationPolicy);
+        assertNotNull(ticketExpirationPolicy.getKey());
+        assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getTgtConf());
+        assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getStConf());
+
+        int afterCount = policyDAO.findAll().size();
+        assertEquals(afterCount, beforeCount + 1);
+    }
+
     @Test
     public void update() {
         PasswordPolicy policy = policyDAO.find("ce93fcda-dc3a-4369-a7b0-a6108c261c85");
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
index dd091dc3d7..e603c1a7df 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
@@ -30,6 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.persistence.jpa.inner.AbstractClientAppTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -69,11 +70,13 @@ public class PolicyTest extends AbstractClientAppTest {
         AuthPolicy authPolicy = buildAndSaveAuthPolicy();
         AccessPolicy accessPolicy = buildAndSaveAccessPolicy();
         AttrReleasePolicy attrPolicy = buildAndSaveAttrRelPolicy();
+        TicketExpirationPolicy ticketExpirationPolicy = buildAndSaveTicketExpirationPolicy();
 
         Realm realm = realmDAO.getRoot();
         realm.setAuthPolicy(authPolicy);
         realm.setAccessPolicy(accessPolicy);
         realm.setAttrReleasePolicy(attrPolicy);
+        realm.setTicketExpirationPolicy(ticketExpirationPolicy);
 
         realm = realmDAO.save(realm);
         assertNotNull(realm.getAuthPolicy());
@@ -83,12 +86,14 @@ public class PolicyTest extends AbstractClientAppTest {
         policyDAO.delete(authPolicy);
         policyDAO.delete(accessPolicy);
         policyDAO.delete(attrPolicy);
+        policyDAO.delete(ticketExpirationPolicy);
         entityManager().flush();
 
         realm = realmDAO.getRoot();
         assertNull(realm.getAuthPolicy());
         assertNull(realm.getAccessPolicy());
         assertNull(realm.getAttrReleasePolicy());
+        assertNull(realm.getTicketExpirationPolicy());
     }
 
     @Test
@@ -96,6 +101,7 @@ public class PolicyTest extends AbstractClientAppTest {
         // Create new policy
         AccessPolicy accessPolicy = buildAndSaveAccessPolicy();
         AuthPolicy authPolicy = buildAndSaveAuthPolicy();
+        TicketExpirationPolicy ticketExpirationPolicy = buildAndSaveTicketExpirationPolicy();
 
         // Create new client app and assign policy
         OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class);
@@ -106,14 +112,22 @@ public class PolicyTest extends AbstractClientAppTest {
         rp.setClientSecret("secret");
         rp.setAccessPolicy(accessPolicy);
         rp.setAuthPolicy(authPolicy);
+        rp.setTicketExpirationPolicy(ticketExpirationPolicy);
 
         rp = oidcRelyingPartyDAO.save(rp);
-        assertNotNull(rp);
+        assertNotNull(rp.getAuthPolicy());
+        assertNotNull(rp.getAccessPolicy());
+        assertNotNull(rp.getTicketExpirationPolicy());
 
         policyDAO.delete(accessPolicy);
-        entityManager().flush();
-
         policyDAO.delete(authPolicy);
+        policyDAO.delete(ticketExpirationPolicy);
         entityManager().flush();
+
+        rp = oidcRelyingPartyDAO.find(rp.getKey());
+        assertNotNull(rp);
+        assertNull(rp.getAuthPolicy());
+        assertNull(rp.getAccessPolicy());
+        assertNull(rp.getTicketExpirationPolicy());
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
index d5c441c7c5..7d695694d9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
 
 public class ClientAppDataBinderImpl implements ClientAppDataBinder {
@@ -62,13 +63,15 @@ public class ClientAppDataBinderImpl implements ClientAppDataBinder {
     public <T extends ClientApp> T create(final ClientAppTO clientAppTO) {
         if (clientAppTO instanceof SAML2SPClientAppTO) {
             return (T) doCreate((SAML2SPClientAppTO) clientAppTO);
-        } else if (clientAppTO instanceof OIDCRPClientAppTO) {
+        }
+        if (clientAppTO instanceof OIDCRPClientAppTO) {
             return (T) doCreate((OIDCRPClientAppTO) clientAppTO);
-        } else if (clientAppTO instanceof CASSPClientAppTO) {
+        }
+        if (clientAppTO instanceof CASSPClientAppTO) {
             return (T) doCreate((CASSPClientAppTO) clientAppTO);
-        } else {
-            throw new IllegalArgumentException("Unsupported client app: " + clientAppTO.getClass().getName());
         }
+
+        throw new IllegalArgumentException("Unsupported client app: " + clientAppTO.getClass().getName());
     }
 
     @Override
@@ -162,12 +165,14 @@ public class ClientAppDataBinderImpl implements ClientAppDataBinder {
         clientAppTO.setPrivacyUrl(clientApp.getPrivacyUrl());
         clientAppTO.setUsernameAttributeProviderConf(clientApp.getUsernameAttributeProviderConf());
 
-        clientAppTO.setAuthPolicy(
-                Optional.ofNullable(clientApp.getAuthPolicy()).map(AuthPolicy::getKey).orElse(null));
-        clientAppTO.setAccessPolicy(
-                Optional.ofNullable(clientApp.getAccessPolicy()).map(AccessPolicy::getKey).orElse(null));
-        clientAppTO.setAttrReleasePolicy(
-                Optional.ofNullable(clientApp.getAttrReleasePolicy()).map(AttrReleasePolicy::getKey).orElse(null));
+        clientAppTO.setAuthPolicy(Optional.ofNullable(clientApp.getAuthPolicy()).
+                map(AuthPolicy::getKey).orElse(null));
+        clientAppTO.setAccessPolicy(Optional.ofNullable(clientApp.getAccessPolicy()).
+                map(AccessPolicy::getKey).orElse(null));
+        clientAppTO.setAttrReleasePolicy(Optional.ofNullable(clientApp.getAttrReleasePolicy()).
+                map(AttrReleasePolicy::getKey).orElse(null));
+        clientAppTO.setTicketExpirationPolicy(Optional.ofNullable(clientApp.getTicketExpirationPolicy()).
+                map(TicketExpirationPolicy::getKey).orElse(null));
 
         clientAppTO.getProperties().addAll(clientApp.getProperties());
     }
@@ -323,6 +328,20 @@ public class ClientAppDataBinderImpl implements ClientAppDataBinder {
             }
         }
 
+        if (clientAppTO.getTicketExpirationPolicy() == null) {
+            clientApp.setTicketExpirationPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getTicketExpirationPolicy());
+            if (policy instanceof TicketExpirationPolicy) {
+                clientApp.setTicketExpirationPolicy((TicketExpirationPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + TicketExpirationPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
         clientApp.setProperties(clientAppTO.getProperties());
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
index c137355c22..c3a5dcf157 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
@@ -28,6 +28,7 @@ import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.policy.PropagationPolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
 import org.apache.syncope.common.lib.policy.PushPolicyTO;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
@@ -49,6 +50,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRule
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -252,6 +254,15 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
             attrReleasePolicy.setOrder(attrReleasePolicyTO.getOrder());
             attrReleasePolicy.setStatus(attrReleasePolicyTO.getStatus());
             attrReleasePolicy.setConf(attrReleasePolicyTO.getConf());
+        } else if (policyTO instanceof TicketExpirationPolicyTO) {
+            if (result == null) {
+                result = (T) entityFactory.newEntity(TicketExpirationPolicy.class);
+            }
+
+            TicketExpirationPolicy ticketExpirationPolicy = TicketExpirationPolicy.class.cast(result);
+            TicketExpirationPolicyTO ticketExpirationPolicyTO = TicketExpirationPolicyTO.class.cast(policyTO);
+
+            ticketExpirationPolicy.setConf(ticketExpirationPolicyTO.getConf());
         }
 
         if (result != null) {
@@ -345,6 +356,12 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
             attrReleasePolicyTO.setOrder(attrReleasePolicy.getOrder());
             attrReleasePolicyTO.setStatus(attrReleasePolicy.getStatus());
             attrReleasePolicyTO.setConf(attrReleasePolicy.getConf());
+        } else if (policy instanceof TicketExpirationPolicy) {
+            TicketExpirationPolicy ticketExpirationPolicy = TicketExpirationPolicy.class.cast(policy);
+            TicketExpirationPolicyTO ticketExpirationPolicyTO = new TicketExpirationPolicyTO();
+            policyTO = (T) ticketExpirationPolicyTO;
+
+            ticketExpirationPolicyTO.setConf(ticketExpirationPolicy.getConf());
         }
 
         if (policyTO != null) {
@@ -353,7 +370,8 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
 
             if (!(policy instanceof AuthPolicy)
                     && !(policy instanceof AccessPolicy)
-                    && !(policy instanceof AttrReleasePolicy)) {
+                    && !(policy instanceof AttrReleasePolicy)
+                    && !(policy instanceof TicketExpirationPolicy)) {
 
                 policyTO.getUsedByResources().addAll(
                         resourceDAO.findByPolicy(policy).stream().
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
index 7c89e0c794..efd902a9e3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
+import java.util.Optional;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
@@ -39,6 +40,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
@@ -163,6 +165,17 @@ public class RealmDataBinderImpl implements RealmDataBinder {
                 throw sce;
             }
         }
+        if (realmTO.getTicketExpirationPolicy() != null) {
+            Policy policy = policyDAO.find(realmTO.getTicketExpirationPolicy());
+            if (policy instanceof TicketExpirationPolicy) {
+                realm.setTicketExpirationPolicy((TicketExpirationPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + TicketExpirationPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
 
         realmTO.getActions().forEach(logicActionsKey -> {
             Implementation logicAction = implementationDAO.find(logicActionsKey);
@@ -262,6 +275,20 @@ public class RealmDataBinderImpl implements RealmDataBinder {
             }
         }
 
+        if (realmTO.getTicketExpirationPolicy() == null) {
+            realm.setTicketExpirationPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(realmTO.getTicketExpirationPolicy());
+            if (policy instanceof TicketExpirationPolicy) {
+                realm.setTicketExpirationPolicy((TicketExpirationPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + TicketExpirationPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
         realmTO.getActions().forEach(logicActionsKey -> {
             Implementation logicActions = implementationDAO.find(logicActionsKey);
             if (logicActions == null) {
@@ -304,16 +331,22 @@ public class RealmDataBinderImpl implements RealmDataBinder {
 
         realmTO.setKey(realm.getKey());
         realmTO.setName(realm.getName());
-        realmTO.setParent(realm.getParent() == null ? null : realm.getParent().getKey());
+        Optional.ofNullable(realm.getParent()).ifPresent(parent -> realmTO.setParent(parent.getKey()));
         realmTO.setFullPath(realm.getFullPath());
 
         if (admin) {
-            realmTO.setAccountPolicy(realm.getAccountPolicy() == null ? null : realm.getAccountPolicy().getKey());
-            realmTO.setPasswordPolicy(realm.getPasswordPolicy() == null ? null : realm.getPasswordPolicy().getKey());
-            realmTO.setAuthPolicy(realm.getAuthPolicy() == null ? null : realm.getAuthPolicy().getKey());
-            realmTO.setAccessPolicy(realm.getAccessPolicy() == null ? null : realm.getAccessPolicy().getKey());
-            realmTO.setAttrReleasePolicy(
-                    realm.getAttrReleasePolicy() == null ? null : realm.getAttrReleasePolicy().getKey());
+            Optional.ofNullable(realm.getAccountPolicy()).
+                    ifPresent(policy -> realmTO.setAccountPolicy(policy.getKey()));
+            Optional.ofNullable(realm.getPasswordPolicy()).
+                    ifPresent(policy -> realmTO.setPasswordPolicy(policy.getKey()));
+            Optional.ofNullable(realm.getAuthPolicy()).
+                    ifPresent(policy -> realmTO.setAuthPolicy(policy.getKey()));
+            Optional.ofNullable(realm.getAccessPolicy()).
+                    ifPresent(policy -> realmTO.setAccessPolicy(policy.getKey()));
+            Optional.ofNullable(realm.getAttrReleasePolicy()).
+                    ifPresent(policy -> realmTO.setAttrReleasePolicy(policy.getKey()));
+            Optional.ofNullable(realm.getTicketExpirationPolicy()).
+                    ifPresent(policy -> realmTO.setTicketExpirationPolicy(policy.getKey()));
 
             realm.getActions().forEach(action -> realmTO.getActions().add(action.getKey()));
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
index cfc65f71fb..a053d29871 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
@@ -93,6 +93,14 @@ public class WAClientAppDataBinderImpl implements WAClientAppDataBinder {
                 waClientApp.setAttrReleasePolicy(
                         policyDataBinder.getPolicyTO(clientApp.getRealm().getAttrReleasePolicy()));
             }
+
+            if (clientApp.getTicketExpirationPolicy() != null) {
+                waClientApp.setTicketExpirationPolicy(
+                        policyDataBinder.getPolicyTO(clientApp.getTicketExpirationPolicy()));
+            } else if (clientApp.getRealm() != null && clientApp.getRealm().getTicketExpirationPolicy() != null) {
+                waClientApp.setTicketExpirationPolicy(
+                        policyDataBinder.getPolicyTO(clientApp.getRealm().getTicketExpirationPolicy()));
+            }
         } catch (Exception e) {
             LOG.error("While building the configuration from an application's policy ", e);
         }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 8b65c30d25..ace874418a 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -73,7 +73,9 @@ import org.apache.syncope.common.lib.policy.AuthPolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
 import org.apache.syncope.common.lib.policy.PolicyTO;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.request.AnyObjectCR;
 import org.apache.syncope.common.lib.request.AnyObjectUR;
 import org.apache.syncope.common.lib.request.AttrPatch;
@@ -961,6 +963,23 @@ public abstract class AbstractITCase {
         return policy;
     }
 
+    protected static TicketExpirationPolicyTO buildTicketExpirationPolicyTO() {
+        TicketExpirationPolicyTO policy = new TicketExpirationPolicyTO();
+        policy.setName("Test Ticket Expiration policy");
+
+        DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf();
+        DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf();
+        tgtConf.setMaxTimeToLiveInSeconds(110);
+        conf.setTgtConf(tgtConf);
+        DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf();
+        stConf.setMaxTimeToLiveInSeconds(0);
+        stConf.setNumberOfUses(1);
+        conf.setStConf(stConf);
+        policy.setConf(conf);
+
+        return policy;
+    }
+
     protected static List<AuditEntry> query(final AuditQuery query, final int maxWaitSeconds) {
         if (IS_ELASTICSEARCH_ENABLED) {
             try {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
index 13c18bc8d6..d146eabf42 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java
@@ -42,7 +42,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
     }
 
     private static void createAccountPolicy(final String name) {
-        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:0:link");
+        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
         TESTER.clickLink("body:content:tabbedPanel:panel:container:content:add");
         TESTER.assertComponent("body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer", Modal.class);
 
@@ -99,7 +99,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
     }
 
     private static void createPasswordPolicy(final String name) {
-        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
+        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link");
         TESTER.clickLink("body:content:tabbedPanel:panel:container:content:add");
         TESTER.assertComponent("body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer", Modal.class);
 
@@ -194,7 +194,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
     }
 
     private static void deleteAccountPolicy(final String name) {
-        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:0:link");
+        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
         Component component = findComponentByProp("name",
                 "body:content:tabbedPanel:panel:container:content:"
                 + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name);
@@ -219,7 +219,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
     }
 
     private static void deletePasswordPolicy(final String name) {
-        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
+        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link");
         Component component = findComponentByProp("name",
                 "body:content:tabbedPanel:panel:container:content:"
                 + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name);
@@ -270,6 +270,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
 
     @Test
     public void read() {
+        TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
         assertNotNull(findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:"
                 + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", "an account policy"));
     }
@@ -511,7 +512,7 @@ public class PoliciesITCase extends AbstractConsoleITCase {
                 "body:content:tabbedPanel:panel:outerObjectsRepeater:4:"
                 + "outer:form:content:container:content:wizard:form");
         formTester.submit("buttons:finish");
-        
+
         assertSuccessMessage();
         TESTER.cleanupFeedbackMessages();
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
index 14f783349f..418c933caf 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
@@ -42,10 +42,12 @@ import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
 import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
 import org.apache.syncope.common.lib.policy.PropagationPolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
 import org.apache.syncope.common.lib.policy.PushPolicyTO;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.to.ImplementationTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.BackOffStrategy;
@@ -213,6 +215,15 @@ public class PolicyITCase extends AbstractITCase {
         AccessPolicyTO accessPolicyTO = createPolicy(PolicyType.ACCESS, buildAccessPolicyTO());
         assertNotNull(accessPolicyTO);
         assertEquals("Test Access policy", accessPolicyTO.getName());
+
+        AttrReleasePolicyTO attrReleasePolicyTO = createPolicy(PolicyType.ATTR_RELEASE, buildAttrReleasePolicyTO());
+        assertNotNull(attrReleasePolicyTO);
+        assertEquals("Test Attribute Release policy", attrReleasePolicyTO.getName());
+
+        TicketExpirationPolicyTO ticketExpirationPolicyTO =
+                createPolicy(PolicyType.TICKET_EXPIRATION, buildTicketExpirationPolicyTO());
+        assertNotNull(ticketExpirationPolicyTO);
+        assertEquals("Test Ticket Expiration policy", ticketExpirationPolicyTO.getName());
     }
 
     @Test
@@ -308,6 +319,24 @@ public class PolicyITCase extends AbstractITCase {
         assertTrue(policyConf.getIncludeOnlyAttrs().contains("cn"));
     }
 
+    @Test
+    public void updateTicketExpirationPolicy() {
+        TicketExpirationPolicyTO newPolicyTO =
+                createPolicy(PolicyType.TICKET_EXPIRATION, buildTicketExpirationPolicyTO());
+        assertNotNull(newPolicyTO);
+
+        DefaultTicketExpirationPolicyConf policyConf = (DefaultTicketExpirationPolicyConf) newPolicyTO.getConf();
+        policyConf.getStConf().setNumberOfUses(2);
+
+        // update new policy
+        POLICY_SERVICE.update(PolicyType.TICKET_EXPIRATION, newPolicyTO);
+        newPolicyTO = POLICY_SERVICE.read(PolicyType.TICKET_EXPIRATION, newPolicyTO.getKey());
+        assertNotNull(newPolicyTO);
+
+        policyConf = (DefaultTicketExpirationPolicyConf) newPolicyTO.getConf();
+        assertEquals(2, policyConf.getStConf().getNumberOfUses());
+    }
+
     @Test
     public void delete() throws IOException {
         PullPolicyTO policy = buildPullPolicyTO();
@@ -349,6 +378,31 @@ public class PolicyITCase extends AbstractITCase {
         } catch (SyncopeClientException e) {
             assertNotNull(e);
         }
+
+        AttrReleasePolicyTO attrReleasePolicyTO = createPolicy(PolicyType.ATTR_RELEASE, buildAttrReleasePolicyTO());
+        assertNotNull(attrReleasePolicyTO);
+
+        POLICY_SERVICE.delete(PolicyType.ATTR_RELEASE, attrReleasePolicyTO.getKey());
+
+        try {
+            POLICY_SERVICE.read(PolicyType.ATTR_RELEASE, attrReleasePolicyTO.getKey());
+            fail("This should not happen");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+
+        TicketExpirationPolicyTO ticketExpirationPolicyTO =
+                createPolicy(PolicyType.TICKET_EXPIRATION, buildTicketExpirationPolicyTO());
+        assertNotNull(ticketExpirationPolicyTO);
+
+        POLICY_SERVICE.delete(PolicyType.TICKET_EXPIRATION, ticketExpirationPolicyTO.getKey());
+
+        try {
+            POLICY_SERVICE.read(PolicyType.TICKET_EXPIRATION, ticketExpirationPolicyTO.getKey());
+            fail("This should not happen");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
     }
 
     @Test
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
index 9ef5a4389e..b27545aa54 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
@@ -32,6 +32,8 @@ import org.apache.syncope.common.lib.policy.AccessPolicyTO;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
 import org.apache.syncope.common.lib.to.SAML2SPClientAppTO;
 import org.apache.syncope.common.lib.types.ClientAppType;
@@ -104,20 +106,29 @@ public class WAClientAppITCase extends AbstractITCase {
 
         AttrReleasePolicyTO attrReleasePolicyTO = createPolicy(PolicyType.ATTR_RELEASE, buildAttrReleasePolicyTO());
 
+        TicketExpirationPolicyTO ticketExpirationPolicyTO =
+                createPolicy(PolicyType.TICKET_EXPIRATION, buildTicketExpirationPolicyTO());
+
         oidcrpto.setAuthPolicy(authPolicyTO.getKey());
         oidcrpto.setAccessPolicy(accessPolicyTO.getKey());
         oidcrpto.setAttrReleasePolicy(attrReleasePolicyTO.getKey());
+        oidcrpto.setTicketExpirationPolicy(ticketExpirationPolicyTO.getKey());
 
         oidcrpto = createClientApp(ClientAppType.OIDCRP, oidcrpto);
 
         WAClientApp waClientApp = WA_CLIENT_APP_SERVICE.read(oidcrpto.getClientAppId(), null);
         assertNotNull(waClientApp);
         assertTrue(waClientApp.getAttrReleasePolicy().getConf() instanceof DefaultAttrReleasePolicyConf);
+        assertTrue(waClientApp.getTicketExpirationPolicy().getConf() instanceof DefaultTicketExpirationPolicyConf);
 
         DefaultAttrReleasePolicyConf attrReleasePolicyConf =
                 (DefaultAttrReleasePolicyConf) waClientApp.getAttrReleasePolicy().getConf();
         assertFalse(attrReleasePolicyConf.getReleaseAttrs().isEmpty());
         assertEquals("username", attrReleasePolicyConf.getReleaseAttrs().get("uid"));
         assertEquals("fullname", attrReleasePolicyConf.getReleaseAttrs().get("cn"));
+
+        DefaultTicketExpirationPolicyConf ticketExpirationPolicyConf =
+                (DefaultTicketExpirationPolicyConf) waClientApp.getTicketExpirationPolicy().getConf();
+        assertEquals(110, ticketExpirationPolicyConf.getTgtConf().getMaxTimeToLiveInSeconds());
     }
 }
diff --git a/pom.xml b/pom.xml
index 21eb3befd2..ec3ceb3b75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1461,7 +1461,7 @@ under the License.
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
-          <version>3.10.1</version>
+          <version>3.11.0</version>
           <configuration>
             <release>${targetJdk}</release>
             <useIncrementalCompilation>false</useIncrementalCompilation>
@@ -1737,7 +1737,7 @@ under the License.
             <dependency>
               <groupId>com.puppycrawl.tools</groupId>
               <artifactId>checkstyle</artifactId>
-              <version>10.8.1</version>
+              <version>10.9.1</version>
             </dependency>
           </dependencies>
           <configuration>
diff --git a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
index dd7b1a9494..d3fccd0f65 100644
--- a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
@@ -38,6 +38,7 @@ https://apereo.github.io/cas/6.6.x/integration/Attribute-Release-PrincipalId-Att
 . <<policies-authentication,authentication policy>>
 . <<policies-access,access policy>>
 . <<policies-attribute-release,attribute release policy>>
+. <<policies-ticket-expiration,ticket expiration policy>>
 . additional properties
 
 More parameters are required to be specified depending on the actual client application type.
diff --git a/src/main/asciidoc/reference-guide/concepts/policies.adoc b/src/main/asciidoc/reference-guide/concepts/policies.adoc
index 9ce0b1ae08..d28fc1044c 100644
--- a/src/main/asciidoc/reference-guide/concepts/policies.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/policies.adoc
@@ -470,3 +470,12 @@ https://github.com/apache/syncope/blob/3_0_X/core/provisioning-api/src/main/java
 endif::[]
 interface.
 ====
+
+[[policies-ticket-expiration]]
+==== Ticket Expiration
+
+Ticket Expiration policies control the duration of various types of WA sessions.
+
+[NOTE]
+Ticket Expiration Policy instances are dynamically translated into
+https://apereo.github.io/cas/6.6.x/ticketing/Configuring-Ticket-Expiration-Policy.html[their CAS equivalent^].
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
index d0cde2e4b0..c8c946b8d4 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
@@ -56,11 +56,14 @@ import org.apache.syncope.wa.starter.mapping.ClientAppMapper;
 import org.apache.syncope.wa.starter.mapping.DefaultAccessMapper;
 import org.apache.syncope.wa.starter.mapping.DefaultAttrReleaseMapper;
 import org.apache.syncope.wa.starter.mapping.DefaultAuthMapper;
+import org.apache.syncope.wa.starter.mapping.DefaultTicketExpirationMapper;
 import org.apache.syncope.wa.starter.mapping.HttpRequestAccessMapper;
 import org.apache.syncope.wa.starter.mapping.OIDCRPClientAppTOMapper;
 import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
 import org.apache.syncope.wa.starter.mapping.RemoteEndpointAccessMapper;
 import org.apache.syncope.wa.starter.mapping.SAML2SPClientAppTOMapper;
+import org.apache.syncope.wa.starter.mapping.TicketExpirationMapFor;
+import org.apache.syncope.wa.starter.mapping.TicketExpirationMapper;
 import org.apache.syncope.wa.starter.mapping.TimeBasedAccessMapper;
 import org.apache.syncope.wa.starter.oidc.WAOIDCJWKSGeneratorService;
 import org.apache.syncope.wa.starter.pac4j.saml.WASAML2ClientCustomizer;
@@ -172,6 +175,12 @@ public class WAContext {
         return new DefaultAuthMapper();
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public TicketExpirationMapper defaultTicketExpirationMapper() {
+        return new DefaultTicketExpirationMapper();
+    }
+
     @ConditionalOnMissingBean(name = "casSPClientAppTOMapper")
     @Bean
     public ClientAppMapper casSPClientAppTOMapper() {
@@ -200,26 +209,33 @@ public class WAContext {
 
         Map<String, AuthMapper> authPolicyConfMappers = new HashMap<>();
         ctx.getBeansOfType(AuthMapper.class).forEach((name, bean) -> {
-            AuthMapFor authMapFor = ctx.findAnnotationOnBean(name, AuthMapFor.class);
-            if (authMapFor != null) {
-                authPolicyConfMappers.put(authMapFor.authPolicyConfClass().getName(), bean);
+            AuthMapFor amf = ctx.findAnnotationOnBean(name, AuthMapFor.class);
+            if (amf != null) {
+                authPolicyConfMappers.put(amf.authPolicyConfClass().getName(), bean);
             }
         });
 
         Map<String, AccessMapper> accessPolicyConfMappers = new HashMap<>();
         ctx.getBeansOfType(AccessMapper.class).forEach((name, bean) -> {
-            AccessMapFor accessMapFor = ctx.findAnnotationOnBean(name, AccessMapFor.class);
-            if (accessMapFor != null) {
-                accessPolicyConfMappers.put(accessMapFor.accessPolicyConfClass().getName(), bean);
+            AccessMapFor amf = ctx.findAnnotationOnBean(name, AccessMapFor.class);
+            if (amf != null) {
+                accessPolicyConfMappers.put(amf.accessPolicyConfClass().getName(), bean);
             }
         });
 
         Map<String, AttrReleaseMapper> attrReleasePolicyConfMappers = new HashMap<>();
         ctx.getBeansOfType(AttrReleaseMapper.class).forEach((name, bean) -> {
-            AttrReleaseMapFor attrReleaseMapFor =
-                    ctx.findAnnotationOnBean(name, AttrReleaseMapFor.class);
-            if (attrReleaseMapFor != null) {
-                attrReleasePolicyConfMappers.put(attrReleaseMapFor.attrReleasePolicyConfClass().getName(), bean);
+            AttrReleaseMapFor armpf = ctx.findAnnotationOnBean(name, AttrReleaseMapFor.class);
+            if (armpf != null) {
+                attrReleasePolicyConfMappers.put(armpf.attrReleasePolicyConfClass().getName(), bean);
+            }
+        });
+
+        Map<String, TicketExpirationMapper> ticketExpirationPolicyConfMappers = new HashMap<>();
+        ctx.getBeansOfType(TicketExpirationMapper.class).forEach((name, bean) -> {
+            TicketExpirationMapFor temf = ctx.findAnnotationOnBean(name, TicketExpirationMapFor.class);
+            if (temf != null) {
+                ticketExpirationPolicyConfMappers.put(temf.ticketExpirationPolicyConfClass().getName(), bean);
             }
         });
 
@@ -239,6 +255,7 @@ public class WAContext {
                 authPolicyConfMappers,
                 accessPolicyConfMappers,
                 attrReleasePolicyConfMappers,
+                ticketExpirationPolicyConfMappers,
                 clientAppTOMappers);
     }
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
index 75aebb1030..f3ef455a58 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
@@ -24,12 +24,17 @@ import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.common.lib.to.ClientAppTO;
 import org.apereo.cas.services.BaseWebBasedRegisteredService;
+import org.apereo.cas.services.CasRegisteredService;
 import org.apereo.cas.services.DefaultRegisteredServiceProperty;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.services.RegisteredServiceProperty;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 
 abstract class AbstractClientAppMapper implements ClientAppMapper {
 
@@ -59,19 +64,33 @@ abstract class AbstractClientAppMapper implements ClientAppMapper {
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
-            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
+            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy,
+            final RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy,
+            final RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy,
+            final RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy,
+            final RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy) {
 
-        if (authPolicy != null) {
-            service.setAuthenticationPolicy(authPolicy);
-        }
-        if (mfaPolicy != null) {
-            service.setMultifactorAuthenticationPolicy(mfaPolicy);
-        }
-        if (accessStrategy != null) {
-            service.setAccessStrategy(accessStrategy);
-        }
-        if (attributeReleasePolicy != null) {
-            service.setAttributeReleasePolicy(attributeReleasePolicy);
+        Optional.ofNullable(authPolicy).ifPresent(service::setAuthenticationPolicy);
+
+        Optional.ofNullable(mfaPolicy).ifPresent(service::setMultifactorAuthenticationPolicy);
+
+        Optional.ofNullable(accessStrategy).ifPresent(service::setAccessStrategy);
+
+        Optional.ofNullable(attributeReleasePolicy).ifPresent(service::setAttributeReleasePolicy);
+
+        Optional.ofNullable(tgtExpirationPolicy).ifPresent(service::setTicketGrantingTicketExpirationPolicy);
+
+        if (service instanceof CasRegisteredService) {
+            CasRegisteredService casRegisteredService = (CasRegisteredService) service;
+
+            Optional.ofNullable(stExpirationPolicy).
+                    ifPresent(casRegisteredService::setServiceTicketExpirationPolicy);
+
+            Optional.ofNullable(tgtProxyExpirationPolicy).
+                    ifPresent(casRegisteredService::setProxyGrantingTicketExpirationPolicy);
+
+            Optional.ofNullable(stProxyExpirationPolicy).
+                    ifPresent(casRegisteredService::setProxyTicketExpirationPolicy);
         }
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
index 5d007d2a05..a3a4bd70e7 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
@@ -26,6 +26,10 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 import org.springframework.context.ConfigurableApplicationContext;
 
 @ClientAppMapFor(clientAppClass = CASSPClientAppTO.class)
@@ -38,7 +42,11 @@ public class CASSPClientAppTOMapper extends AbstractClientAppMapper {
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
-            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
+            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy,
+            final RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy,
+            final RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy,
+            final RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy,
+            final RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy) {
 
         CASSPClientAppTO cas = CASSPClientAppTO.class.cast(clientApp.getClientAppTO());
 
@@ -46,7 +54,8 @@ public class CASSPClientAppTOMapper extends AbstractClientAppMapper {
         service.setServiceId(cas.getServiceId());
         setCommon(service, cas);
 
-        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy,
+                tgtExpirationPolicy, stExpirationPolicy, tgtProxyExpirationPolicy, stProxyExpirationPolicy);
 
         return service;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
index dfd9c798d4..d3d0010b0b 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
@@ -24,6 +24,10 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 import org.springframework.context.ConfigurableApplicationContext;
 
 @FunctionalInterface
@@ -35,5 +39,9 @@ public interface ClientAppMapper {
             RegisteredServiceAuthenticationPolicy authPolicy,
             RegisteredServiceMultifactorPolicy mfaPolicy,
             RegisteredServiceAccessStrategy accessStrategy,
-            RegisteredServiceAttributeReleasePolicy attributeReleasePolicy);
+            RegisteredServiceAttributeReleasePolicy attributeReleasePolicy,
+            RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy,
+            RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy,
+            RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy,
+            RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy);
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultTicketExpirationMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultTicketExpirationMapper.java
new file mode 100644
index 0000000000..5a0a3ec4b1
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultTicketExpirationMapper.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.wa.starter.mapping;
+
+import java.util.Optional;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
+import org.apereo.cas.services.DefaultRegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.DefaultRegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.DefaultRegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.DefaultRegisteredServiceTicketGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
+
+@TicketExpirationMapFor(ticketExpirationPolicyConfClass = DefaultTicketExpirationPolicyConf.class)
+public class DefaultTicketExpirationMapper implements TicketExpirationMapper {
+
+    protected Optional<DefaultTicketExpirationPolicyConf> conf(final TicketExpirationPolicyTO policy) {
+        if (policy.getConf() instanceof DefaultTicketExpirationPolicyConf) {
+            return Optional.of((DefaultTicketExpirationPolicyConf) policy.getConf());
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public RegisteredServiceTicketGrantingTicketExpirationPolicy buildTGT(final TicketExpirationPolicyTO policy) {
+        return conf(policy).flatMap(conf -> Optional.ofNullable(conf.getTgtConf())).
+                map(conf -> {
+                    DefaultRegisteredServiceTicketGrantingTicketExpirationPolicy result =
+                            new DefaultRegisteredServiceTicketGrantingTicketExpirationPolicy();
+                    result.setMaxTimeToLiveInSeconds(conf.getMaxTimeToLiveInSeconds());
+                    return result;
+                }).
+                orElse(null);
+    }
+
+    @Override
+    public RegisteredServiceServiceTicketExpirationPolicy buildST(final TicketExpirationPolicyTO policy) {
+        return conf(policy).flatMap(conf -> Optional.ofNullable(conf.getStConf())).
+                map(conf -> {
+                    DefaultRegisteredServiceServiceTicketExpirationPolicy result =
+                            new DefaultRegisteredServiceServiceTicketExpirationPolicy();
+                    result.setNumberOfUses(conf.getNumberOfUses());
+                    result.setTimeToLive(Long.toString(conf.getMaxTimeToLiveInSeconds()));
+                    return result;
+                }).
+                orElse(null);
+    }
+
+    @Override
+    public RegisteredServiceProxyGrantingTicketExpirationPolicy buildProxyTGT(final TicketExpirationPolicyTO policy) {
+        return conf(policy).flatMap(conf -> Optional.ofNullable(conf.getProxyTgtConf())).
+                map(conf -> {
+                    DefaultRegisteredServiceProxyGrantingTicketExpirationPolicy result =
+                            new DefaultRegisteredServiceProxyGrantingTicketExpirationPolicy();
+                    result.setMaxTimeToLiveInSeconds(conf.getMaxTimeToLiveInSeconds());
+                    return result;
+                }).
+                orElse(null);
+    }
+
+    @Override
+    public RegisteredServiceProxyTicketExpirationPolicy buildProxyST(final TicketExpirationPolicyTO policy) {
+        return conf(policy).flatMap(conf -> Optional.ofNullable(conf.getProxyStConf())).
+                map(conf -> {
+                    DefaultRegisteredServiceProxyTicketExpirationPolicy result =
+                            new DefaultRegisteredServiceProxyTicketExpirationPolicy();
+                    result.setNumberOfUses(conf.getNumberOfUses());
+                    result.setTimeToLive(Long.toString(conf.getMaxTimeToLiveInSeconds()));
+                    return result;
+                }).
+                orElse(null);
+    }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
index a385b1cdac..654c33f5a4 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
@@ -44,6 +44,10 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
 import org.springframework.context.ConfigurableApplicationContext;
 
@@ -59,7 +63,11 @@ public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
-            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
+            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy,
+            final RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy,
+            final RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy,
+            final RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy,
+            final RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy) {
 
         OIDCRPClientAppTO rp = OIDCRPClientAppTO.class.cast(clientApp.getClientAppTO());
         OidcRegisteredService service = new OidcRegisteredService();
@@ -151,7 +159,8 @@ public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
                     CUSTOM_SCOPE, customClaims.stream().collect(Collectors.toList())));
         }
 
-        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, chain);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, chain,
+                tgtExpirationPolicy, stExpirationPolicy, tgtProxyExpirationPolicy, stProxyExpirationPolicy);
 
         return service;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
index b8000e536f..b0a9fd8cf6 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
@@ -31,6 +31,10 @@ import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceDelegatedAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.ObjectProvider;
@@ -52,6 +56,8 @@ public class RegisteredServiceMapper {
 
     protected final Map<String, AttrReleaseMapper> attrReleasePolicyConfMappers;
 
+    protected final Map<String, TicketExpirationMapper> ticketExpirationPolicyConfMappers;
+
     protected final Map<String, ClientAppMapper> clientAppTOMappers;
 
     public RegisteredServiceMapper(
@@ -61,6 +67,7 @@ public class RegisteredServiceMapper {
             final Map<String, AuthMapper> authPolicyConfMappers,
             final Map<String, AccessMapper> accessPolicyConfMappers,
             final Map<String, AttrReleaseMapper> attrReleasePolicyConfMappers,
+            final Map<String, TicketExpirationMapper> ticketExpirationPolicyConfMappers,
             final Map<String, ClientAppMapper> clientAppTOMappers) {
 
         this.ctx = ctx;
@@ -69,6 +76,7 @@ public class RegisteredServiceMapper {
         this.authPolicyConfMappers = authPolicyConfMappers;
         this.accessPolicyConfMappers = accessPolicyConfMappers;
         this.attrReleasePolicyConfMappers = attrReleasePolicyConfMappers;
+        this.ticketExpirationPolicyConfMappers = ticketExpirationPolicyConfMappers;
         this.clientAppTOMappers = clientAppTOMappers;
     }
 
@@ -128,6 +136,31 @@ public class RegisteredServiceMapper {
                         map(mapper -> mapper.build(attrReleasePolicyTO)).
                         orElse(null);
 
-        return clientAppMapper.map(ctx, clientApp, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
+        RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy = null;
+        RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy = null;
+        RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy = null;
+        RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy = null;
+        if (clientApp.getTicketExpirationPolicy() != null) {
+            TicketExpirationMapper ticketExpirationMapper = ticketExpirationPolicyConfMappers.get(
+                    clientApp.getTicketExpirationPolicy().getConf().getClass().getName());
+            if (ticketExpirationMapper != null) {
+                tgtExpirationPolicy = ticketExpirationMapper.buildTGT(clientApp.getTicketExpirationPolicy());
+                stExpirationPolicy = ticketExpirationMapper.buildST(clientApp.getTicketExpirationPolicy());
+                tgtProxyExpirationPolicy = ticketExpirationMapper.buildProxyTGT(clientApp.getTicketExpirationPolicy());
+                stProxyExpirationPolicy = ticketExpirationMapper.buildProxyST(clientApp.getTicketExpirationPolicy());
+            }
+        }
+
+        return clientAppMapper.map(
+                ctx,
+                clientApp,
+                authPolicy,
+                mfaPolicy,
+                accessStrategy,
+                attributeReleasePolicy,
+                tgtExpirationPolicy,
+                stExpirationPolicy,
+                tgtProxyExpirationPolicy,
+                stProxyExpirationPolicy);
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
index ac7e576c69..51eaebb333 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
@@ -27,6 +27,10 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
 import org.apereo.cas.support.saml.services.SamlRegisteredService;
 import org.apereo.cas.util.model.TriStateBoolean;
 import org.springframework.context.ConfigurableApplicationContext;
@@ -41,7 +45,11 @@ public class SAML2SPClientAppTOMapper extends AbstractClientAppMapper {
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
-            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
+            final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy,
+            final RegisteredServiceTicketGrantingTicketExpirationPolicy tgtExpirationPolicy,
+            final RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy,
+            final RegisteredServiceProxyGrantingTicketExpirationPolicy tgtProxyExpirationPolicy,
+            final RegisteredServiceProxyTicketExpirationPolicy stProxyExpirationPolicy) {
 
         SAML2SPClientAppTO sp = SAML2SPClientAppTO.class.cast(clientApp.getClientAppTO());
         SamlRegisteredService service = new SamlRegisteredService();
@@ -64,7 +72,8 @@ public class SAML2SPClientAppTOMapper extends AbstractClientAppMapper {
         }
         service.setServiceProviderNameIdQualifier(sp.getServiceProviderNameIdQualifier());
 
-        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy,
+                tgtExpirationPolicy, stExpirationPolicy, tgtProxyExpirationPolicy, stProxyExpirationPolicy);
 
         return service;
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapFor.java
similarity index 63%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
copy to wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapFor.java
index 56a2dc7772..b13b2fe106 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PolicyTabProvider.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapFor.java
@@ -16,15 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.commons;
+package org.apache.syncope.wa.starter.mapping;
 
-import java.io.Serializable;
-import java.util.List;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.springframework.core.Ordered;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf;
 
-public interface PolicyTabProvider extends Serializable, Ordered {
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TicketExpirationMapFor {
 
-    List<ITab> buildTabList(PageReference pageRef);
+    Class<? extends TicketExpirationPolicyConf> ticketExpirationPolicyConfClass();
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapper.java
new file mode 100644
index 0000000000..fc040b0a86
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/TicketExpirationMapper.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.wa.starter.mapping;
+
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
+import org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
+import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
+
+public interface TicketExpirationMapper {
+
+    RegisteredServiceTicketGrantingTicketExpirationPolicy buildTGT(TicketExpirationPolicyTO policy);
+
+    RegisteredServiceServiceTicketExpirationPolicy buildST(TicketExpirationPolicyTO policy);
+
+    RegisteredServiceProxyGrantingTicketExpirationPolicy buildProxyTGT(TicketExpirationPolicyTO policy);
+
+    RegisteredServiceProxyTicketExpirationPolicy buildProxyST(TicketExpirationPolicyTO policy);
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
index 87e6303d15..635338055f 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
@@ -34,6 +34,8 @@ import org.apache.syncope.common.lib.policy.AuthPolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf;
+import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
 import org.apache.syncope.common.lib.to.SAML2SPClientAppTO;
@@ -125,6 +127,14 @@ public class WAServiceRegistryTest extends AbstractTest {
             attrReleasePolicy.setConf(attrReleasePolicyConf);
             waClientApp.setAttrReleasePolicy(attrReleasePolicy);
         }
+
+        TicketExpirationPolicyTO ticketExpirationPolicy = new TicketExpirationPolicyTO();
+        DefaultTicketExpirationPolicyConf ticketExpirationPolicyConf = new DefaultTicketExpirationPolicyConf();
+        DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf();
+        tgtConf.setMaxTimeToLiveInSeconds(110);
+        ticketExpirationPolicyConf.setTgtConf(tgtConf);
+        ticketExpirationPolicy.setConf(ticketExpirationPolicyConf);
+        waClientApp.setTicketExpirationPolicy(ticketExpirationPolicy);
     }
 
     @Autowired
@@ -274,5 +284,7 @@ public class WAServiceRegistryTest extends AbstractTest {
         assertNotNull(delegatedAuthPolicy);
         assertEquals(1, delegatedAuthPolicy.getAllowedProviders().size());
         assertTrue(delegatedAuthPolicy.getAllowedProviders().contains(authModuleTO.getKey()));
+
+        assertNotNull(service.getTicketGrantingTicketExpirationPolicy());
     }
 }