diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml
index d2b88ec63a26a1f9722aebfdae133123d8e4e9e3..433be7131e6deadcfc41791870f495a4af215b3b 100644
--- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml
+++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml
@@ -57,6 +57,7 @@
 """\n
 from DateTime import DateTime\n
 \n
+request = context.REQUEST\n
 portal = context.getPortalObject()\n
 portal_preferences = portal.portal_preferences\n
 \n
@@ -86,6 +87,7 @@ if len(failures_for_period)>= max_authentication_failures:\n
   context.log(\'check=%s  block=%s  release=%s-> %s\' %(check_duration, block_duration, block_timeout, failures_for_period))\n
   if block_timeout > now:\n
     context.log(\'block %s\' %context.getReference())\n
+    request.set(\'is_user_account_blocked\', True)\n
     return 1\n
 \n
 return 0\n
diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml
index bb2e33ca369e302794fcee07b8f156987cbe0ac3..30d50d602eb68e50d604e0778bc119eb480c1326 100644
--- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml
+++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml
@@ -57,6 +57,7 @@
 """\n
 from Products.ERP5Type.Cache import CachingMethod\n
 \n
+request = context.REQUEST\n
 portal = context.getPortalObject()\n
 \n
 def _isPasswordExpired():\n
@@ -66,15 +67,17 @@ def _isPasswordExpired():\n
   max_password_lifetime_duration = portal.portal_preferences.getPreferredMaxPasswordLifetimeDuration()\n
   last_password_modification_date = context.getLastPasswordModificationDate()\n
   if last_password_modification_date is not None and \\\n
-     (last_password_modification_date + max_password_lifetime_duration*one_hour-5) < now:\n
+     (last_password_modification_date + max_password_lifetime_duration*one_hour) < now:\n
     # password is expired\n
     context.log(\'expired %s\' %context.getReference())\n
     return 1\n
   return 0\n
 \n
-_isPasswordExpired1 = CachingMethod(_isPasswordExpired,\n
+_isPasswordExpired = CachingMethod(_isPasswordExpired,\n
                                    id=\'Person_isPasswordExpired\',\n
                                    cache_factory=\'erp5_content_short\')\n
+is_password_expired = _isPasswordExpired()\n
+request.set(\'is_user_account_password_expired\', is_password_expired)\n
 return _isPasswordExpired()\n
 
 
diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml
index 19bb2b8b8a04401489f699dd9863e1b219aca40b..171d64a72aecf7f79de1a024d4f0c520413e4504 100644
--- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml
+++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml
@@ -46,9 +46,32 @@
              isAnon mtool/isAnonymousUser|nothing;">\n
     <tal:block tal:condition="isAnon">\n
       <tal:block tal:define="dummy python: response.expireCookie(\'__ac\', path=\'/\');\n
-                 url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Login and/or password is incorrect.\'));\n
-                 url python: request.get(\'came_from\') and  \'%s&amp;came_from=%s\' % (url, request[\'came_from\']) or url;\n
-                 dummy python: response.redirect(url);" />\n
+                 is_user_account_blocked python: request.get(\'is_user_account_blocked\', False);\n
+                 is_user_account_password_expired python: request.get(\'is_user_account_password_expired\', False);\n
+                 d python: context.log(\'? --%s %s\' %(is_user_account_blocked, is_user_account_password_expired));">\n
+\n
+        <!-- Login and/or password is incorrect. -->\n
+        <tal:block  tal:omit-tag="python: not is_user_account_blocked and not is_user_account_password_expired"\n
+                    tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Login and/or password is incorrect.\'));\n
+                                url python: request.get(\'came_from\') and  \'%s&amp;came_from=%s\' % (url, request[\'came_from\']) or url;\n
+                                dummy python: response.redirect(url);">\n
+        </tal:block>\n
+\n
+        <!-- Login is blocked. -->\n
+        <tal:block tal:condition="is_user_account_blocked">\n
+          <tal:block tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Account is blocked.\'));\n
+                               url python: request.get(\'came_from\') and  \'%s&amp;came_from=%s\' % (url, request[\'came_from\']) or url;\n
+                               dummy python: response.redirect(url);"/>\n
+        </tal:block>\n
+\n
+        <!-- Password is expired. -->\n
+        <tal:block tal:condition="is_user_account_password_expired">\n
+          <tal:block tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Password is expired.\'));\n
+                                 url python: request.get(\'came_from\') and  \'%s&amp;came_from=%s\' % (url, request[\'came_from\']) or url;\n
+                                 dummy python: response.redirect(url);"/>\n
+        </tal:block>\n
+\n
+      </tal:block>\n
     </tal:block>\n
     <tal:block tal:condition="not: isAnon">\n
       <tal:block tal:define="came_from python: request.get(\'came_from\') or here.absolute_url();\n
@@ -73,7 +96,7 @@
         </item>
         <item>
             <key> <string>output_encoding</string> </key>
-            <value> <string>utf-8</string> </value>
+            <value> <string>iso-8859-15</string> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/bt5/erp5_authentication_policy/bt/revision b/bt5/erp5_authentication_policy/bt/revision
index 56a6051ca2b02b04ef92d5150c9ef600403cb1de..d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 100644
--- a/bt5/erp5_authentication_policy/bt/revision
+++ b/bt5/erp5_authentication_policy/bt/revision
@@ -1 +1 @@
-1
\ No newline at end of file
+2
\ No newline at end of file
diff --git a/product/ERP5Security/ERP5UserManager.py b/product/ERP5Security/ERP5UserManager.py
index 6a0e58ef8706b41822180ea22fca2535d6e8b9f1..dd6377c5d2da6f6427bd44f642663d59471dc09e 100644
--- a/product/ERP5Security/ERP5UserManager.py
+++ b/product/ERP5Security/ERP5UserManager.py
@@ -163,12 +163,38 @@ class ERP5UserManager(BasePlugin):
                                                  id='ERP5UserManager_authenticateCredentials',
                                                  cache_factory='erp5_content_short')
         try:
-          return _authenticateCredentials(
-                      login=credentials.get('login'),
-                      password=credentials.get('password'),
-                      path=self.getPhysicalPath())
+          authentication_result = _authenticateCredentials(
+                                    login=credentials.get('login'),
+                                    password=credentials.get('password'),
+                                    path=self.getPhysicalPath())
+                           
         except _AuthenticationFailure:
+          authentication_result = None
+       
+        method = getattr(self, 'ERP5Site_isAuthenticationPolicyEnabled', None)
+        if method is None or (method is not None and not method()):
+          # stop here, no authentication policy enabled 
+          # so just return authentication check result
+          # XXX: move to ERP5 Site API
+          return authentication_result
+        
+        # authentication policy enabled, we need person object anyway
+        user_list = self.getUserByLogin(credentials.get('login'))
+        if not user_list:
+          # not an ERP5 Person object
           return None
+        user = user_list[0]
+        
+        if authentication_result is None:
+          # file a failed authentication attempt
+          user.notifyLoginFailure()
+          return None
+          
+        # check if user account is blocked and if password is expired or not
+        if user.isLoginBlocked() or user.isPasswordExpired(): 
+          return None
+        
+        return authentication_result
 
     #
     #   IUserEnumerationPlugin implementation