Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java	(revision 1873068)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java	(date 1579801651000)
@@ -137,6 +137,8 @@
             }
         } catch (RepositoryException e) {
             throw new LoginException(e.getMessage());
+        } finally {
+            removeNewPwAttribute(credentials);
         }
         return success;
     }
@@ -177,6 +179,17 @@
         return false;
     }
 
+    /**
+     * Make sure the new-password attribute is no longer present on the credentials after the login phase
+     *
+     * @param credentials
+     */
+    private static void removeNewPwAttribute(Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            ((SimpleCredentials) credentials).removeAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD);
+        }
+    }
+
     private boolean impersonate(AuthInfo info, User user) {
         try {
             if (user.getID().equals(info.getUserID())) {
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java	(revision 1873068)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java	(date 1579801380000)
@@ -32,7 +32,9 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -101,4 +103,19 @@
         // during user creation pw last modified is set, thus it shouldn't expire
         a.authenticate(new SimpleCredentials(userId, userId.toCharArray()));
     }
+
+    @Test
+    public void testAuthenticateWithNewPasswordAttribute() throws Exception {
+        Authentication a = new UserAuthentication(getUserConfiguration(), root, userId);
+        SimpleCredentials sc = new SimpleCredentials(userId, userId.toCharArray());
+        sc.setAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD, "SureChangedMyPassword!");
+        try {
+            // the user should need to change the password on first login
+            // if new-pw attribute is present it will be used to reset password
+            assertTrue(a.authenticate(sc));
+        } finally {
+            // upon authentication the CREDENTIALS_ATTRIBUTE_NEWPASSWORD must be removed
+            assertNull(sc.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
+        }
+    }
 }
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java	(revision 1873068)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java	(date 1579801651000)
@@ -34,6 +34,8 @@
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -43,6 +45,7 @@
 public class ResetExpiredPasswordTest extends AbstractSecurityTest implements UserConstants {
 
     private String userId;
+    private SimpleCredentials creds;
 
     @Before
     public void before() throws Exception {
@@ -63,13 +66,18 @@
     }
 
     private void authenticate(String expiredPw, Object newPw) throws LoginException {
-        SimpleCredentials creds = new SimpleCredentials(userId, expiredPw.toCharArray());
+        creds = new SimpleCredentials(userId, expiredPw.toCharArray());
         creds.setAttribute(UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD, newPw);
 
         Authentication a = new UserAuthentication(getUserConfiguration(), root, userId);
         a.authenticate(creds);
     }
 
+    private static void assertCredentials(SimpleCredentials sc) {
+        assertNotNull(sc);
+        assertNull(sc.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
+    }
+
     @Test
     public void testPasswordChangePersisted() throws Exception {
         authenticate(userId, "newPw");
@@ -78,11 +86,13 @@
         Root rootBasedOnSeparateSession = login(getAdminCredentials()).getLatestRoot();
         Tree userTree = rootBasedOnSeparateSession.getTree(getTestUser().getPath());
         assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), "newPw"));
+        assertCredentials(creds);
     }
 
     @Test
     public void testAuthenticatePasswordExpiredThenChanged() throws Exception {
         authenticate(userId, userId);
+        assertCredentials(creds);
     }
 
     @Test
@@ -96,6 +106,7 @@
             Tree userTree = root.getTree(getTestUser().getPath());
             assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), userId));
             assertEquals(0, userTree.getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getValue(Type.LONG).longValue());
+            assertCredentials(creds);
         }
     }
 
@@ -110,6 +121,7 @@
             Tree userTree = root.getTree(getTestUser().getPath());
             assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), userId));
             assertEquals(0, userTree.getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getValue(Type.LONG).longValue());
+            assertCredentials(creds);
         }
     }
 }
