apereo/cas · error · InvalidLoginTimeException
Unable to login at this time
Error message
Unable to login at this time
What it means
JsonResourceAuthenticationHandler supports per-account availability windows formatted as 'start~end' datetimes. If the current UTC time is before the start or after the end of that window, it throws InvalidLoginTimeException 'Unable to login at this time'.
Solutions
- Widen or remove the availability window on the account in the JSON resource
- Confirm window datetimes are expressed in a timezone consistent with UTC comparison (prefer offsets like 2026-09-08T09:00:00+00:00)
- Check the server clock/timezone if the window looks correct
- If the account should be permanently available, clear the availability field entirely
Example fix
// before "availability": "2024-01-01T00:00:00Z~2024-02-01T00:00:00Z" // after "availability": "2024-01-01T00:00:00Z~2030-01-01T00:00:00Z"
Defensive patterns
Strategy: validation
Validate before calling
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); // parse account.availability 'start~end' and confirm now is inside before authenticating
Try / catch
try {
return handler.authenticate(credential);
} catch (InvalidLoginTimeException e) {
// inform user of allowed window
} Prevention
- Store availability windows with explicit UTC offsets
- Remove stale temporary windows after grants expire
- Verify server clock/NTP if windows look correct but fail
When it happens
Trigger: authenticateUsernamePasswordInternal for an account whose availability field is non-blank and ZonedDateTime.now(UTC) is outside the parsed start~end range.
Common situations: Account temporarily enabled only during certain hours/dates and login attempted outside them; timezone confusion — window stored in local time but compared against UTC; window left set from a past temporary grant; malformed date failing conversion is a related failure.
Related errors
- Unable to login from this location
- Account password on file does not match the provided…
- Authentication handler is disabled
- No user can be accepted because none is defined
- not found in backing map.
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/a054f7e4e759ce13.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-generic/src/main/java/org/apereo/cas/adaptors/generic/JsonResourceAuthenticationHandler.java:93
case EXPIRED -> throw new AccountExpiredException();
case LOCKED -> throw new AccountLockedException();
case MUST_CHANGE_PASSWORD -> throw new AccountPasswordMustChangeException();
case OK -> LOGGER.debug("Account status is OK");
}
val clientInfo = ClientInfoHolder.getClientInfo();
if (clientInfo != null && StringUtils.isNotBlank(account.getLocation())
&& !RegexUtils.find(account.getLocation(), clientInfo.getClientIpAddress())) {
throw new InvalidLoginLocationException("Unable to login from this location");
}
if (StringUtils.isNotBlank(account.getAvailability())) {
val range = Splitter.on("~").splitToList(account.getAvailability());
val startDate = DateTimeUtils.convertToZonedDateTime(range.getFirst());
val endDate = DateTimeUtils.convertToZonedDateTime(range.get(1));
val now = ZonedDateTime.now(Clock.systemUTC());
if (now.isBefore(startDate) || now.isAfter(endDate)) {
throw new InvalidLoginTimeException("Unable to login at this time");
}
}
val warnings = new ArrayList<MessageDescriptor>();
if (account.getExpirationDate() != null) {
val now = LocalDate.now(ZoneOffset.UTC);
if (now.isEqual(account.getExpirationDate()) || now.isAfter(account.getExpirationDate())) {
throw new AccountExpiredException();
}
if (getPasswordPolicyConfiguration() != null) {
val warningPeriod = account.getExpirationDate()
.minusDays(getPasswordPolicyConfiguration().getPasswordWarningNumberOfDays());
if (now.isAfter(warningPeriod) || now.isEqual(warningPeriod)) {
val daysRemaining = ChronoUnit.DAYS.between(now, account.getExpirationDate());
warnings.add(new DefaultMessageDescriptor(
"password.expiration.loginsRemaining",
"You have {0} logins remaining before you MUST change your password.",
new Serializable[]{daysRemaining}));View on GitHub (pinned to e7288fc434)