{"record":{"id":"7f0cc8950d48f4d0","repo":"apereo/cas","slug":"primary-ticket-granting-ticket-expiration-policy-i","errorCode":null,"errorMessage":"Primary ticket-granting ticket expiration policy is set to NEVER expire tickets.","messagePattern":"Primary ticket-granting ticket expiration policy is set to NEVER expire tickets\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/builder/TicketGrantingTicketExpirationPolicyBuilder.java","lineNumber":72,"sourceCode":"     * @return the expiration policy\n     */\n    public ExpirationPolicy toRememberMeTicketExpirationPolicy() {\n        val tgt = casProperties.getTicket().getTgt();\n        val timeToKillInSeconds = Beans.newDuration(tgt.getRememberMe().getTimeToKillInSeconds()).toSeconds();\n        LOGGER.debug(\"Remember me expiration policy is being configured based on hard timeout of [{}] seconds\", timeToKillInSeconds);\n        val rememberMePolicy = new HardTimeoutExpirationPolicy(timeToKillInSeconds);\n        val policy = new RememberMeDelegatingExpirationPolicy();\n        policy.addPolicy(RememberMeDelegatingExpirationPolicy.POLICY_NAME_REMEMBER_ME, rememberMePolicy);\n        policy.addPolicy(BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT, toTicketGrantingTicketExpirationPolicy());\n        return policy;\n    }\n\n    private ExpirationPolicy toTicketGrantingTicketExpirationPolicy() {\n        val tgt = casProperties.getTicket().getTgt();\n\n        if (Beans.isInfinitelyDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())\n            && Beans.isInfinitelyDurable(tgt.getPrimary().getTimeToKillInSeconds())) {\n            LOGGER.warn(\"Primary ticket-granting ticket expiration policy is set to NEVER expire tickets.\");\n            return NeverExpiresExpirationPolicy.INSTANCE;\n        }\n        if (Beans.isNeverDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())\n            && Beans.isNeverDurable(tgt.getPrimary().getTimeToKillInSeconds())) {\n            LOGGER.warn(\"Ticket-granting ticket expiration policy is set to ALWAYS expire tickets.\");\n            return AlwaysExpiresExpirationPolicy.INSTANCE;\n        }\n\n        if (StringUtils.isNotBlank(tgt.getTimeout().getMaxTimeToLiveInSeconds())) {\n            val seconds = Beans.newDuration(tgt.getTimeout().getMaxTimeToLiveInSeconds()).toSeconds();\n            LOGGER.debug(\"Ticket-granting ticket expiration policy is based on a timeout of [{}] seconds\", seconds);\n            return new TimeoutExpirationPolicy(seconds);\n        }\n\n        if (StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeInBetweenUsesInSeconds())\n            && StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeToKillInSeconds())) {\n            val policy = new ThrottledUseAndTimeoutExpirationPolicy();\n            val seconds = Beans.newDuration(tgt.getThrottledTimeout().getTimeToKillInSeconds()).toSeconds();","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/builder/TicketGrantingTicketExpirationPolicyBuilder.java#L54-L90","documentation":"When building the TGT expiration policy from cas.ticket.tgt.* properties, TicketGrantingTicketExpirationPolicyBuilder checks Beans.isInfinitelyDurable on both primary maxTimeToLiveInSeconds and timeToKillInSeconds. If both are effectively infinite (e.g. PT-forever sentinel values), it warns that tickets will NEVER expire and returns NeverExpiresExpirationPolicy.INSTANCE. This is a deliberate configuration warning about a security-sensitive setup.","triggerScenarios":"Setting both cas.ticket.tgt.primary.max-time-to-live-in-seconds and cas.ticket.tgt.primary.time-to-kill-in-seconds to an 'infinite' value (Beans.isInfinitelyDurable, e.g. -1 or the forever sentinel) so toTicketGrantingTicketExpirationPolicy() builds NeverExpiresExpirationPolicy.","commonSituations":"Copy-pasted 'make sessions last forever' configuration; sample configs with -1 values left in production; operators surprised that logout alone no longer cleans stale TGTs, leading to unbounded ticket-registry growth.","solutions":["Set finite values for cas.ticket.tgt.primary.max-time-to-live-in-seconds and time-to-kill-in-seconds (e.g. 28800 / 3600).","If never-expiring TGTs are truly intended, accept the warning and ensure explicit cleanup (eviction/TTL at the ticket registry level, e.g. Redis/TTL sweeps) exists.","Verify you did not accidentally use the 'forever' sentinel meant for other settings.","Review remember-me vs primary settings; use toRememberMeTicketExpirationPolicy semantics rather than infinite primary TGTs."],"exampleFix":"// before\ncas.ticket.tgt.primary.max-time-to-live-in-seconds=-1\ncas.ticket.tgt.primary.time-to-kill-in-seconds=-1\n// after\ncas.ticket.tgt.primary.max-time-to-live-in-seconds=28800\ncas.ticket.tgt.primary.time-to-kill-in-seconds=14400","handlingStrategy":"validation","validationCode":"if (cas.ticket.tgt.primary.maxTimeToLiveInSeconds <= 0 || cas.ticket.tgt.primary.timeToKillInSeconds <= 0) { fail(\"TGT expiration values must be positive finite seconds\"); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never ship -1/forever sentinel values for TGT TTLs to production","Add config review checks that assert finite positive TGT durations","If infinite TGTs are intentional, compensate with registry-level eviction/TTL"],"tags":["cas","expiration-policy","configuration","security"],"backgroundTag":"invalid-config-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}