Change expired password during login
This solution document will give you an idea on how to configure the login flow to detect expired password and prompt the user for a password change.
This document will use the username, password and OTP scenario as an example, but can be replaced by any other authentication flow.
Prereqs
This will work with federated setups, but part of the authentication has to be made with "internal authenticators". You will need to find the proper internal authenticator that matches the SAML authenticator to be replaced.
Please make sure to replace all references to match your environment.
The templates files used in this document can be found on https://files.phenixid.se in the folder PhenixID MFA PAS/Integrations/Change expired password during login.
According to example below, templates should be placed in the authentication module (mods/com.phenixidentity~auth-http~<version>/templates).
Authenticators
SAMLDataSave - start
This will be the point of start for this flow.
<p>{
"name": "SAMLDataSave",
"id": "start",
"alias": "start",
"configuration": {
"nextAuthenticator": "internal-UidPWDOTP",
"idpID": "idp"
}
}</p>
PostUidPasswordAndOTP - internal-UidPWDOTP
This is the authenticator that performs the username, password and OTP validation.
This is also the step where the password expiration is detected and stored in the session.
<p>{
"id": "internal-UidPWDOTP",
"alias": "internal-UidPWDOTP",
"name": "PostUidPasswordAndOTP",
"configuration": {
"userValidationPipeID": "UserLookupAndAuthWithLDAP",
"otpValidationPipeID": "ValidateSentOtp",
"successURL": "/saml/authenticate/dispatch/"
}
}</p>
Dispatch - dispatch
The user will be dispatched to the password change if the password is expired.
The user will be dispatched to the "sso authenticator" if the password not is expired.
<p>{
"name": "Dispatch",
"id": "dispatch",
"alias": "dispatch",
"configuration": {
"idpID": "idp",
"mapping": [
{
"authenticator": "changepwd",
"expression": "session.properties().getValueOrDefault('changepwd','').equals('true')"
},
{
"authenticator": "sso",
"expression": "!session.properties().getValueOrDefault('changepwd','').equals('true')"
}
]
}
}</p>
Registration - changepwd
This authenticator will perform the password change.
<p>{
"alias": "changepwd",
"id": "changepwd",
"name": "Registration",
"configuration": {
"stages": [
{
"pipeid": "ChangePWDPipe",
"template": "pwchange",
"sessionValues": [
"username",
"customError",
"dummy"
],
"translation": []
},
{
"pipeid": "",
"template": "pwchange-redir",
"sessionValues": [
"redirect_to_saml_uri"
],
"translation": []
}
]
}
}</p>
PostUidAndPasswordSAML - sso
This authenticator will create the SAML assertion and login the user to the SP.
<p>{
"name": "PostUidAndPasswordSAML",
"id": "sso",
"alias": "sso",
"configuration": {
"pipeID": "PostUid_and_Password_SAML_SSO",
"idpID": "idp"
}
}</p>
Pipes
UserLookupAndAuthWithLDAP
This pipe will perform username and password validation, generate and send OTP.
The session will be updated if the password is expired.
<p>{
"id": "UserLookupAndAuthWithLDAP",
"valves": [
{
"name": "LDAPSearchValve",
"config": {
"connection_ref": "8d7362da-15c4-4178-888d-086b337e6f33",
"base_dn": "dc=company,dc=local",
"scope": "SUB",
"size_limit": "0",
"filter_template": "(&(objectclass=*)(samaccountname={{request.username}}))",
"attributes": "commonName,uid,mail,mobile"
}
},
{
"name": "LDAPBindValve",
"config": {
"connection_ref": "8d7362da-15c4-4178-888d-086b337e6f33",
"password_param_name": "password",
"allowed_error_codes": "532,773"
}
},
{
"name": "SessionLoadValve",
"config": {
"id": "{{request.session_id}}",
"exec_if_expr": "flow.firstItem().containsProperty('LDAP_BIND_ERROR_CODE')"
}
},
{
"name": "SessionPropertyReplaceValve",
"config": {
"name": "changepwd",
"value": "true",
"exec_if_expr": "flow.firstItem().containsProperty('LDAP_BIND_ERROR_CODE')"
}
},
{
"name": "SessionPropertyReplaceValve",
"config": {
"name": "oldpassword",
"value": "{{request.password}}",
"exec_if_expr": "flow.firstItem().containsProperty('LDAP_BIND_ERROR_CODE')"
}
},
{
"name": "SessionPropertyReplaceValve",
"config": {
"name": "username",
"value": "{{request.username}}",
"exec_if_expr": "flow.firstItem().containsProperty('LDAP_BIND_ERROR_CODE')"
}
},
{
"name": "SessionPersistValve",
"config": {
"exec_if_expr": "flow.firstItem().containsProperty('LDAP_BIND_ERROR_CODE')"
}
},
{
"name": "OTPGeneratorValve",
"config": {
"length": "6",
"name": "generated_otp"
}
},
{
"name": "OTPBySMSValve",
"enabled": "true",
"config": {
"userid_param_name": "username",
"gw_username": "GatewayAccount",
"gw_password": "GatewayPassword"
}
}
]
}</p>
ValidateSentOtp
This pipe will validate the OTP.
<p>{
"id": "ValidateSentOtp",
"valves": [
{
"name": "SessionLoadValve",
"config": {
"id": "{{request.session_id}}"
}
},
{
"name": "OTPValidationValve",
"enabled": "true",
"config": {
"provided_otp_param_name": "{{request.otp}}",
"generated_otp_param_name": "generated_otp"
}
}
]
}</p>
ChangePWDPipe
This pipe will do the password change.
<p>{
"id": "ChangePWDPipe",
"valves": [
{
"name": "SessionLoadValve",
"config": {
"id": "{{request.session_id}}"
}
},
{
"name": "LDAPSearchValve",
"config": {
"connection_ref": "8d7362da-15c4-4178-888d-086b337e6f33",
"base_dn": "dc=company,dc=local",
"scope": "SUB",
"size_limit": "0",
"filter_template": "(&(objectclass=*)(samaccountname={{session.username}}))",
"attributes": "commonName,uid,mail,mobile,pwdLastSet"
}
},
{
"name": "ADPasswordChangeValve",
"config": {
"connection_ref": "8d7362da-15c4-4178-888d-086b337e6f33",
"value": "{{request.pwdnew}}",
"unlock": "false",
"current_password_param_name": "{{session.oldpassword}}"
}
},
{
"name": "SessionPropertyRemoveValve",
"config": {
"name": "changepwd"
}
},
{
"name": "SessionPropertyReplaceValve",
"config": {
"name": "redirect_to_saml_uri",
"value": "/saml/authenticate/sso"
}
},
{
"name": "SessionPersistValve",
"config": {}
}
]
}</p>
PostUid_and_Password_SAML_SSO
This pipe will issue the SAML assertion and redirect the end user to the SP.
<p>{
"id": "PostUid_and_Password_SAML_SSO",
"valves": [
{
"name": "FlowFailValve",
"config": {
"message": "No current session",
"skip_if_expr": "request.authenticatedrequest=='true'"
}
},
{
"name": "SessionLoadValve",
"config": {
"id": "{{request.session_id}}"
}
},
{
"name": "LDAPSearchValve",
"config": {
"connection_ref": "8d7362da-15c4-4178-888d-086b337e6f33",
"base_dn": "dc=company,dc=local",
"scope": "SUB",
"size_limit": "0",
"filter_template": "(sAMAccountName={{request.username}})",
"attributes": "sAMAccountName"
}
},
{
"name": "AssertionProvider",
"config": {
"targetEntityID": "idp",
"sourceID": "https://apache.phenixid.se/saml",
"nameIDAttribute": "sAMAccountName"
}
}
]
}
</p>