Fork me on GitHub

Password Expiry and Force Initial Password Change

General

Since version 1.1.0 Oak provides functionality to expire passwords of users as well as force users to change their password upon initial (first-time) login.

Password Expiry

Administrators may configure passwords to expire within a configurable amount of time (days). A user whose password has expired will no longer be able to obtain a session/login.

Force Initial Password Change

An administrator may configure the system such that a user is forced to set a new password upon first login. This is a special form of Password Expiry above, in that upon creation a user account's password is expired by default. Upon initial login, the user will not be able to obtain a session/login and the password needs to be changed prior to a next attempt. For specifying the new password, the initial password has to be provided.

Configuration

An administrator may enable password expiry and initial password change via the org.apache.jackrabbit.oak.security.user.UserConfigurationImpl OSGi configuration. By default both features are disabled.

The following configuration options are supported:

Parameter Type Default Description
PARAM_PASSWORD_MAX_AGE int 0 Number of days until the password expires.
PARAM_PASSWORD_INITIAL_CHANGE boolean false boolean flag to enable initial pw change.
PARAM_PASSWORD_EXPIRY_FOR_ADMIN boolean false flag to enable pw expiry for admin user.

Note:

  • Maximum Password Age (maxPasswordAge) will only be enabled when a value greater 0 is set (expiration time in days).
  • Change Password On First Login (initialPasswordChange): When enabled, forces users to change their password upon first login.

How it works

Definition of Expired Password

An expired password is defined as follows:

  • The current date-time is after or on the date-time + maxPasswordAge specified in a rep:passwordLastModified property
  • OR: Expiry and/or Enforce Password Change is enabled, but no rep:passwordLastModified property exists

For the above, a password node rep:pw and a property rep:passwordLastModified, governed by a new rep:Password node type and located in the user's home, have been introduced, leaving open future enhancements to password management (such as password policies, history, et al):

Representation in the Repository

Node Type rep:Password
[rep:Password]
    - * (UNDEFINED) protected
    - * (UNDEFINED) protected multiple
Node rep:pwd and Property rep:passwordLastModified
[rep:User]  > rep:Authorizable, rep:Impersonatable
    + rep:pwd (rep:Password) = rep:Password protected
    ...

The rep:pw node and the rep:passwordLastModified property are defined protected in order to guard against the user modifying (overcoming) her password expiry. The new sub-node also has the advantage of allowing repository consumers to e.g. register specific commit hooks / actions on such a node.

In the future the rep:password property on the user node may be migrated to the rep:pw sub-node.

User Creation

Upon initial creation of a user, the rep:passwordLastModified property is omitted. If expiry or initialPasswordChange are enabled, the absence of the property will be interpreted as immediate expiry of the password. When subsequently the user changes her password via User#changePassword, the rep:passwordLastModified property is set and henceforth interpreted.

Authentication

A login module must throw a javax.security.auth.login.CredentialExpiredException upon encountering an expired password. A consumer implementation can then differentiate between a failed login (due to a wrong password specified) and an expired password, allowing the consumer to take action, e.g. to redirect to a change password form.

In Oak, the Authentication implementation provided by default with the user management compares within its authenticate() method the system time with the value stored in the rep:passwordLastModified and throws a CredentialExpiredException if now is after or on the date-time specified by the value.

In the case of initialPasswordChange a password is considered expired if no rep:passwordLastModified property can be found on login.

Both expiry and force initial password change are checked after regular credentials verification, so as to prevent an attacker identifying valid users by being redirected to a change password form upon expiry.

UserAuthenticationFactory

As described with section User Management: The Default Implementation it is possible to change the default implementation of the UserAuthenticationFactory by pluggin a custom implementation at runtime.

It's important to note that the authentication related part of password expiry is handled by the Authentication implementation exposed by the default UserAuthenticationFactory. Replacing the factory will ultimately disable the password expiry feature unless a custom implementation respects and enforces the constraints explained before.

Changing an Expired Password

Oak supports changing a user's expired password as part of the normal login process.

Consumers of the repository already specify javax.jcr.SimpleCredentials during login, as part of the normal authentication process. In order to change the password for an expired user, the login may be called with the affected user's SimpleCredentials, while additionally providing the new password via a credentials attribute newPassword.

After verifying the user's credentials, before checking expiry, said attribute is then used by the Authentication implementation to change the user's password.

This way the user can change the password while the expiry check succeeds (password expired = false) and a session/login is provided at the same time.

This method of changing password via the normal login call only works if a user's password is in fact expired and cannot be used for regular password changes (attribute is ignored, use User#changePassword directly instead).

Should the Password History feature be enabled, and - for the above password change - a password already in the history be used, the change will fail and the login still throw a CredentialExpiredException. In order for consumers of the exception to become aware that the credentials are still considered expired, and that the password was not changed due to the new password having been found in the password history, the credentials object is fitted with an additional attribute with name PasswordHistoryException.

This attribute may contain the following two values:

  • “New password was found in password history.” or
  • “New password is identical to the current password.”

XML Import

When users are imported via the Oak JCR XML importer, the expiry relevant nodes and property are supported. If the XML specifies a rep:pw node and optionally a rep:passwordLastModified property, these are imported, irrespective of the password expiry or force initial password change being enabled in the configuration. If they're enabled, the imported property will be used in the normal login process as described above. If not enabled, the imported property will have no effect.

On the other hand, if the imported user already exists, potentially existing rep:passwordLastModified properties will be overwritten with the value from the import. If password expiry is enabled, this may cause passwords to expire earlier or later than anticipated, governed by the new value. Also, an import may create such a property where none previously existed, thus effectively cancelling the need to change the password on first login - if the feature is enabled.

Therefore customers using the importer in such fashion should be aware of the potential need to enable password expiry/force initial password change for the imported data to make sense, and/or the effect on already existing/overwritten data.

With the changes made in the light of OAK-8408 the following rules apply when importing a user without an extra rep:pw node:

  • if initialPasswordChange is enabled, rep:passwordLastModified will never be set irrespective if the user node is new or modified. i.e. the user will be force to change the pw upon login.
  • if pw-expiry is enabled, rep:passwordLastModified will only be set for a new user node (but not if node gets modified). this ensures that the password will expire but doesn't reset the expiry when changing an existing user with XML import.
  • if both initialPasswordChange and pw-expiry are enabled, the rules for initialPasswordChange apply.