diff --git a/src/api/common.js b/src/api/common.js
index ae6d4517..dffe3e30 100644
--- a/src/api/common.js
+++ b/src/api/common.js
@@ -12,6 +12,7 @@ import { getCurrentLangAsV1Format } from 'src/i18n'
 import { i18n } from 'src/boot/i18n'
 
 import saveAs from 'file-saver'
+import { PATH_CHANGE_PASSWORD } from 'src/router/routes'
 export const LIST_DEFAULT_PAGE = 1
 export const LIST_DEFAULT_ROWS = 24
 export const LIST_ALL_ROWS = 1000
@@ -177,6 +178,11 @@ function handleResponseError (err) {
     if (code === 403 && message === 'Invalid license') {
         message = i18n.global.tc('Contact your administrator to activate this functionality')
     }
+    if (code === 403 && message === 'Password expired') {
+        message = i18n.global.tc('Password Expired')
+        return this.$router.push({ path: PATH_CHANGE_PASSWORD })
+    }
+
     if (code !== null && message !== null) {
         throw new ApiResponseError(code, message)
     }
diff --git a/src/api/user.js b/src/api/user.js
index e32484e5..bf3f970e 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -1,5 +1,6 @@
 
 import _ from 'lodash'
+import { i18n } from 'boot/i18n'
 import {
     get,
     post,
@@ -51,6 +52,15 @@ export async function loginByExchangeToken (token) {
     }
 }
 
+export async function getPreLoginPasswordInfo () {
+    try {
+        const res = await httpApi.get('api/platforminfo')
+        return res.data.security.password
+    } catch (err) {
+        throw new Error(err.response.data.message)
+    }
+}
+
 export async function getUserData (id) {
     const allPromise = Promise.all([
         getSubscriberById(id),
@@ -167,6 +177,29 @@ export async function getPlatformInfo () {
     })
 }
 
+export function changeExpiredPassword (payload) {
+    return new Promise((resolve, reject) => {
+        httpApi.post('/api/passwordchange/', {
+            new_password: payload.new_password
+        }, {
+            auth: {
+                username: payload.username,
+                password: payload.old_password
+            }
+        }).then((result) => {
+            resolve(result)
+        }).catch((err) => {
+            if (err.response.status === 401) {
+                reject(`Unauthorized. ${i18n.global.tc('Wrong username or password')}`)
+            } else if (err.response.status === 422) {
+                reject(_formatPasswordError(err.response.data.message))
+            } else {
+                reject('Unexpected error')
+            }
+        })
+    })
+}
+
 export async function createAuthToken (tokenExpiringTime) {
     const response = await post({
         resource: 'authtokens',
@@ -177,3 +210,7 @@ export async function createAuthToken (tokenExpiringTime) {
     })
     return response.token
 }
+
+function _formatPasswordError (error) {
+    return error.split("'").slice(-2, -1)[0].replaceAll(',', ', ')
+}
diff --git a/src/boot/routes.js b/src/boot/routes.js
index dcccf37d..a25e9137 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -9,7 +9,7 @@ import {
 
 export default ({ app, router, store }) => {
     router.beforeEach((to, from, next) => {
-        const publicUrls = ['/login', '/recoverpassword']
+        const publicUrls = ['/login', '/recoverpassword', '/changepassword']
         // not authorized user
         if (!hasJwt()) {
             if (!publicUrls.includes(to.path)) {
diff --git a/src/i18n/de.json b/src/i18n/de.json
index e46d6895..585cd1b3 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -58,6 +58,7 @@
     "Block Mode for outbounds calls": "Block Mode for outbounds calls",
     "Block Outgoing": "Ausgehende Anrufe blockieren",
     "Block anonymous inbound calls": "Block anonymous inbound calls",
+    "Block outgoing": "Block outgoing",
     "Busy Greeting": "Begrüßung, wenn besetzt",
     "Busy Lamp Field": "Besetztlampenfeld",
     "CDR": "CDR",
@@ -107,7 +108,7 @@
     "Conference name": "Konferenzname",
     "Confirm": "Bestätigen",
     "Contact": "Kontakt",
-    "Contact your administrator to activate this functionality":"Zur Aktivierung dieser Funktion kontaktieren Sie Ihren Administrator.",
+    "Contact your administrator to activate this functionality": "Zur Aktivierung dieser Funktion kontaktieren Sie Ihren Administrator.",
     "Content": "Inhalt",
     "Conversations": "Konversationen",
     "Copy link": "Link kopieren",
@@ -119,6 +120,7 @@
     "Create Call Queue": "Anrufwarteschlange hinzufügen",
     "Create Config": "Konfiguration hinzufügen",
     "Create List": "Liste erstellen",
+    "Create New Password": "Create New Password",
     "Create destination": "Ziel erstellen",
     "Create device": "Gerät hinzufügen",
     "Create group": "Gruppe hinzufügen",
@@ -128,6 +130,7 @@
     "Created device {device} successfully": "Gerät {device} hinzugefügt",
     "Created manager secretary config for {msConfig} successfully": "Konfiguration für {msConfig} hinzugefügt",
     "Created sound set {soundSet} successfully": "Sound-Set {soundSet} hinzugefügt",
+    "Current Password": "Current Password",
     "Custom Announcement": "Benutzerdefinierte Ansage",
     "Custom Announcements": "Individuelle Ansagen",
     "Custom sound": "Benutzerdefinierter Sound",
@@ -300,6 +303,7 @@
     "Ncos Set": "Ncos Set",
     "Never": "Nie",
     "New Messages": "Neue Nachrichten",
+    "New Password": "New Password",
     "New SIP Password": "Neues SIP-Passwort",
     "New SIP Password confirm": "Neues SIP-Passwort bestätigen",
     "New Web Password": "Neues Web-Passwort",
@@ -356,10 +360,18 @@
     "Parallel Ringing": "Paralleles Klingeln",
     "Parent": "Eltern",
     "Password": "Passwort",
+    "Password Expired": "Password Expired",
     "Password Retype": "Passwort-Neueingabe",
     "Password changed successfully": "Passwort erfolgreich geändert",
     "Password confirm": "Passwort bestätigen",
+    "Password is considered weak": "Password is considered weak",
     "Password is not strong enough": "Passwort ist nicht sicher genug",
+    "Password is not strong enough, add more digits": "Password is not strong enough, add more digits",
+    "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters",
+    "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters",
+    "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters",
+    "Password must be at least {max} characters long": "Password must be at least {max} characters long",
+    "Password must be at least {min} characters long": "Password must be at least {min} characters long",
     "Passwords must be equal": "Passwörter müssen übereinstimmen",
     "Phone model": "Telefonmodell",
     "Phone number": "Rufnummer",
@@ -602,6 +614,7 @@
     "Your number is visible to the callee": "Rufnummer wird dem Angerufenen angezeigt",
     "Your number is visible to the callee within own PBX": "Ihre Rufnummer wird dem Angerufenen bei PBX-internen Verbindungen angezeigt",
     "Your password has been changed successfully": "Ihr Passwort wurde erfolgreich geändert",
+    "Your password has expired": "Your password has expired",
     "Your web password has been changed successfully": "Ihr Web-Passwort wurde erfolgreich geändert",
     "ago": "vor",
     "and": "und",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 7226aaf3..a63caf11 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -57,6 +57,7 @@
     "Block Mode for outbounds calls": "Block Mode for outbounds calls",
     "Block Outgoing": "Block Outgoing",
     "Block anonymous inbound calls": "Block anonymous inbound calls",
+    "Block outgoing": "Block outgoing",
     "Busy Greeting": "Busy Greeting",
     "Busy Lamp Field": "Busy Lamp Field",
     "CDR": "CDR",
@@ -105,7 +106,7 @@
     "Conference": "Conference",
     "Confirm": "Confirm",
     "Contact": "Contact",
-    "Contact your administrator to activate this functionality":"Contact your administrator to activate this functionality",
+    "Contact your administrator to activate this functionality": "Contact your administrator to activate this functionality",
     "Content": "Content",
     "Conversations": "Conversations",
     "Cost": "Cost",
@@ -116,6 +117,7 @@
     "Create Call Queue": "Create Call Queue",
     "Create Config": "Create Config",
     "Create List": "Create List",
+    "Create New Password": "Create New Password",
     "Create destination": "Create destination",
     "Create device": "Create device",
     "Create group": "Create group",
@@ -125,6 +127,7 @@
     "Created device {device} successfully": "Created device {device} successfully",
     "Created manager secretary config for {msConfig} successfully": "Created manager secretary config for {msConfig} successfully",
     "Created sound set {soundSet} successfully": "Created sound set {soundSet} successfully",
+    "Current Password": "Current Password",
     "Custom Announcement": "Custom Announcement",
     "Custom Announcements": "Custom Announcements",
     "Custom sound": "Custom sound",
@@ -288,6 +291,7 @@
     "Ncos": "Ncos",
     "Ncos Set": "Ncos Set",
     "Never": "Never",
+    "New Password": "New Password",
     "New SIP Password": "New SIP Password",
     "New SIP Password confirm": "New SIP Password confirm",
     "New Web Password": "New Web Password",
@@ -342,10 +346,18 @@
     "Parallel Ringing": "Parallel Ringing",
     "Parent": "Parent",
     "Password": "Password",
+    "Password Expired": "Password Expired",
     "Password Retype": "Password Retype",
     "Password changed successfully": "Password changed successfully",
     "Password confirm": "Password confirm",
+    "Password is considered weak": "Password is considered weak",
     "Password is not strong enough": "Password is not strong enough",
+    "Password is not strong enough, add more digits": "Password is not strong enough, add more digits",
+    "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters",
+    "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters",
+    "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters",
+    "Password must be at least {max} characters long": "Password must be at least {max} characters long",
+    "Password must be at least {min} characters long": "Password must be at least {min} characters long",
     "Passwords must be equal": "Passwords must be equal",
     "Phone model": "Phone model",
     "Phone number": "Phone number",
@@ -581,6 +593,7 @@
     "Your number is hidden to the callee within own PBX": "Your number is hidden to the callee within own PBX",
     "Your number is visible to the callee": "Your number is visible to the callee",
     "Your number is visible to the callee within own PBX": "Your number is visible to the callee within own PBX",
+    "Your password has expired": "Your password has expired",
     "ago": "ago",
     "and": "and",
     "and call from": "and call from",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index dfe5e448..7508893a 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -58,6 +58,7 @@
     "Block Mode for outbounds calls": "Block Mode for outbounds calls",
     "Block Outgoing": "Bloquear Salientes",
     "Block anonymous inbound calls": "Block anonymous inbound calls",
+    "Block outgoing": "Block outgoing",
     "Busy Greeting": "Saludo de Ocupado",
     "Busy Lamp Field": "Campo de lámpara ocupado",
     "CDR": "CDR",
@@ -107,7 +108,7 @@
     "Conference name": "Nombre de la conferencia",
     "Confirm": "Confirmar",
     "Contact": "Contacto",
-    "Contact your administrator to activate this functionality":"Póngase en contacto con su administrador para activar esta funcionalidad",
+    "Contact your administrator to activate this functionality": "Póngase en contacto con su administrador para activar esta funcionalidad",
     "Content": "Contenido",
     "Conversations": "Conversaciones",
     "Copy link": "Copiar enlace",
@@ -119,6 +120,7 @@
     "Create Call Queue": "Crear Cola de Llamadas",
     "Create Config": "Crear Configuración",
     "Create List": "Crear Lista",
+    "Create New Password": "Create New Password",
     "Create destination": "Crear destino",
     "Create device": "Crear dispositivo",
     "Create group": "Crear grupo",
@@ -128,6 +130,7 @@
     "Created device {device} successfully": "Dispositivo {device} creado exitosamente",
     "Created manager secretary config for {msConfig} successfully": "Se creó exitosamente la configuración de Jefe-Asistente para {msConfig}",
     "Created sound set {soundSet} successfully": "Conjunto de sonido {soundSet} creado exitosamente",
+    "Current Password": "Current Password",
     "Custom Announcement": "Anuncio Personalizado",
     "Custom Announcements": "Anuncios Personalizados",
     "Custom sound": "Sonido personalizado",
@@ -301,6 +304,7 @@
     "Ncos Set": "Ncos Set",
     "Never": "Nunca",
     "New Messages": "Nuevos Mensajes",
+    "New Password": "New Password",
     "New SIP Password": "New SIP Password",
     "New SIP Password confirm": "New SIP Password confirm",
     "New Web Password": "New Web Password",
@@ -355,10 +359,18 @@
     "Parallel Ringing": "Llamada en paralelo",
     "Parent": "Parent",
     "Password": "Contraseña",
+    "Password Expired": "Password Expired",
     "Password Retype": "Repetir contraseña",
     "Password changed successfully": "La contraseña ha sido cambiada correctamente.",
     "Password confirm": "Password confirm",
+    "Password is considered weak": "Password is considered weak",
     "Password is not strong enough": "La contraseña no es lo suficientemente fuerte",
+    "Password is not strong enough, add more digits": "Password is not strong enough, add more digits",
+    "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters",
+    "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters",
+    "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters",
+    "Password must be at least {max} characters long": "Password must be at least {max} characters long",
+    "Password must be at least {min} characters long": "Password must be at least {min} characters long",
     "Passwords must be equal": "Las contraseñas deben ser iguales",
     "Phone model": "Modelo de teléfono",
     "Phone number": "Número de teléfono",
@@ -606,6 +618,7 @@
     "Your number is visible to the callee": "Su número es visible para la persona que llama",
     "Your number is visible to the callee within own PBX": "Su número es visible para el receptor de la llamada en su propia centralita.",
     "Your password has been changed successfully": "Su contraseña ha sido cambiada exitosamente",
+    "Your password has expired": "Your password has expired",
     "ago": "atrás",
     "and": "y",
     "and call from": "y llamar desde",
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 48f300e1..8342e06d 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -58,6 +58,7 @@
     "Block Mode for outbounds calls": "Mode blocage pour les appels sortants",
     "Block Outgoing": "Bloquer les sortants",
     "Block anonymous inbound calls": "Bloquer les appels entrants anonymes",
+    "Block outgoing": "Block outgoing",
     "Busy Greeting": "Message d'accueil en cas d'occupation",
     "Busy Lamp Field": "Champ de la lampe occupée",
     "CDR": "CDR",
@@ -107,7 +108,7 @@
     "Conference name": "Nom de la conférence",
     "Confirm": "Confirmer",
     "Contact": "Contact",
-    "Contact your administrator to activate this functionality":"Contactez votre administrateur pour activer cette fonctionnalité",
+    "Contact your administrator to activate this functionality": "Contactez votre administrateur pour activer cette fonctionnalité",
     "Content": "Contenu",
     "Conversations": "Conversations",
     "Copy link": "Copier le lien",
@@ -119,6 +120,7 @@
     "Create Call Queue": "Créer une file d’attente",
     "Create Config": "Créer Configuration",
     "Create List": "Créer une liste",
+    "Create New Password": "Create New Password",
     "Create destination": "Créer une destination",
     "Create device": "Créer un appareil",
     "Create group": "Créer un groupe",
@@ -128,6 +130,7 @@
     "Created device {device} successfully": "Le poste {device} a été créé avec succès",
     "Created manager secretary config for {msConfig} successfully": "Création réussie de la configuration du secrétaire du gestionnaire pour {msConfig}",
     "Created sound set {soundSet} successfully": "Création du jeu de sons {soundSet} avec succès",
+    "Current Password": "Current Password",
     "Custom Announcement": "Annonce personnalisée",
     "Custom Announcements": "Annonces personnalisées",
     "Custom sound": "Son personnalisé",
@@ -300,6 +303,7 @@
     "Ncos Set": "Ncos Set",
     "Never": "Jamais",
     "New Messages": "Nouveaux messages",
+    "New Password": "New Password",
     "New SIP Password": "New SIP Password",
     "New SIP Password confirm": "New SIP Password confirm",
     "New Web Password": "New Web Password",
@@ -356,10 +360,18 @@
     "Parallel Ringing": "Sonnerie parallèle",
     "Parent": "Parent",
     "Password": "Mot de passe",
+    "Password Expired": "Password Expired",
     "Password Retype": "Retaper le mot de passe",
     "Password changed successfully": "Le mot de passe a été modifié avec succès",
     "Password confirm": "Password confirm",
+    "Password is considered weak": "Password is considered weak",
     "Password is not strong enough": "Le mot de passe n'est pas assez fort",
+    "Password is not strong enough, add more digits": "Password is not strong enough, add more digits",
+    "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters",
+    "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters",
+    "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters",
+    "Password must be at least {max} characters long": "Password must be at least {max} characters long",
+    "Password must be at least {min} characters long": "Password must be at least {min} characters long",
     "Passwords must be equal": "Les mots de passe doivent être égaux",
     "Phone model": "Modèle du poste",
     "Phone number": "Numéro de téléphone",
@@ -602,6 +614,7 @@
     "Your number is visible to the callee": "Votre numéro est visible par le destinataire de l'appel",
     "Your number is visible to the callee within own PBX": "Votre numéro est visible par appelant dans son propre PBX",
     "Your password has been changed successfully": "Votre mot de passe a été modifié avec succès",
+    "Your password has expired": "Your password has expired",
     "ago": ".",
     "and": "et",
     "and call from": "et appeler de",
diff --git a/src/i18n/it.json b/src/i18n/it.json
index 61bcf8aa..c6da040b 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -56,6 +56,7 @@
     "Block Mode for outbounds calls": "Modalità di blocco per le chiamate in uscita",
     "Block Outgoing": "Blocca chiamate in uscita",
     "Block anonymous inbound calls": "Blocca chiamate anonime in entrata",
+    "Block outgoing": "Block outgoing",
     "Busy Greeting": "Messaggio per numero occupato",
     "Busy Lamp Field": "Busy Lamp Field",
     "CDR": "CDR",
@@ -117,6 +118,7 @@
     "Create Call Queue": "Crea coda chiamate",
     "Create Config": "Crea configurazione",
     "Create List": "Crea lista",
+    "Create New Password": "Create New Password",
     "Create destination": "Crea destinazione",
     "Create device": "Crea dispositivo",
     "Create group": "Crea gruppo",
@@ -126,6 +128,7 @@
     "Created device {device} successfully": "Dispositivo {device} creato correttamente",
     "Created manager secretary config for {msConfig} successfully": "Configurazione segreteria per {msConfig} creata correttamente",
     "Created sound set {soundSet} successfully": "Set di messaggi audio {soundSet} creato correttamente",
+    "Current Password": "Current Password",
     "Custom Announcement": "Custom Announcement",
     "Custom Announcements": "Custom Announcements",
     "Custom sound": "Audio personalizzato",
@@ -292,6 +295,7 @@
     "Ncos Set": "Ncos Set",
     "Never": "Never",
     "New Messages": "Nuovi messaggi",
+    "New Password": "New Password",
     "New SIP Password": "New SIP Password",
     "New SIP Password confirm": "New SIP Password confirm",
     "New Web Password": "New Web Password",
@@ -348,10 +352,18 @@
     "Parallel Ringing": "Squilli in parallelo",
     "Parent": "Parent",
     "Password": "Password",
+    "Password Expired": "Password Expired",
     "Password Retype": "Password Retype",
     "Password changed successfully": "Password changed successfully",
     "Password confirm": "Password confirm",
+    "Password is considered weak": "Password is considered weak",
     "Password is not strong enough": "Password is not strong enough",
+    "Password is not strong enough, add more digits": "Password is not strong enough, add more digits",
+    "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters",
+    "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters",
+    "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters",
+    "Password must be at least {max} characters long": "Password must be at least {max} characters long",
+    "Password must be at least {min} characters long": "Password must be at least {min} characters long",
     "Passwords must be equal": "Le passwords devono essere identiche",
     "Phone model": "Modello telefono",
     "Phone number": "Numero di telefono",
@@ -590,6 +602,7 @@
     "Your number is visible to the callee": "Il tuo numero è visible al chiamato",
     "Your number is visible to the callee within own PBX": "Your number is visible to the callee within own PBX",
     "Your password has been changed successfully": "Password modificata",
+    "Your password has expired": "Your password has expired",
     "ago": "fa",
     "and": "e",
     "and call from": "and call from",
diff --git a/src/pages/CscChangeExpiredPassword.vue b/src/pages/CscChangeExpiredPassword.vue
new file mode 100644
index 00000000..f74f005c
--- /dev/null
+++ b/src/pages/CscChangeExpiredPassword.vue
@@ -0,0 +1,293 @@
+<template>
+    <q-layout
+        id="csc-layout-login"
+        view="lHh lpR lFf"
+        class="bg-page"
+    >
+        <q-header
+            id="csc-header-login"
+            class="bg-transparent"
+        >
+            <q-toolbar
+                id="csc-header-toolbar-login"
+            >
+                <csc-selection-language />
+            </q-toolbar>
+        </q-header>
+        <q-page-container>
+            <q-page
+                id="csc-page-login"
+                class="flex flex-center row"
+            >
+                <q-card
+                    id="csc-login-card"
+                    class="bg-main-menu no-shadow no-border-radius col-xs-12 col-sm-6 col-md-4 q-pa-sm"
+                >
+                    <q-card-section class="text-h5 text-center">
+                        {{ $t('Create New Password') }}
+                    </q-card-section>
+                    <q-card-section>
+                        <q-banner
+                            dense
+                            inline-actions
+                            class="text-white text-center bg-red-14"
+                        >
+                            {{ $t('Your password has expired') }}
+                        </q-banner>
+                    </q-card-section>
+                    <q-card-section>
+                        <div
+                            v-if="validationGuidelines && validationGuidelines.length > 0"
+                            inline-actions
+                            class="q-mb-md q-pa-md"
+                        >
+                            <p>Suggested password format:</p>
+                            <q-item
+                                v-for="(message, index) in validationGuidelines"
+                                :key="index"
+                                dense
+                            >
+                                <q-item-section>
+                                    <span>
+                                        <q-icon
+                                            name="lock"
+                                            size="1em"
+                                            class="q-pa-xs"
+                                        /> {{ message }}
+                                    </span>
+                                </q-item-section>
+                            </q-item>
+                        </div>
+                    </q-card-section>
+                    <q-card-section>
+                        <q-form key="login-form">
+                            <csc-input
+                                v-model.trim="username"
+                                outlined
+                                :label="$t('Username')"
+                                data-cy="csc-login-username"
+                                :error="v$.username && v$.username.$errors.length > 0"
+                                :error-message="$errMsg(v$.username.$errors)"
+                                @keyup.enter="changePasswordAction"
+                            >
+                                <template
+                                    #prepend
+                                >
+                                    <q-icon
+                                        name="person"
+                                    />
+                                </template>
+                            </csc-input>
+
+                            <csc-input-password
+                                v-model.trim="currentPassword"
+                                outlined
+                                clearable
+                                :label="$t('Current Password')"
+                                data-cy="csc-login-password"
+                                :error="v$.currentPassword && v$.currentPassword.$errors.length > 0"
+                                :error-message="$errMsg(v$.currentPassword.$errors)"
+                                @keyup.enter="changePasswordAction"
+                            />
+
+                            <csc-input-password
+                                v-model.trim="newPassword"
+                                outlined
+                                clearable
+                                :label="$t('New Password')"
+                                data-cy="csc-login-password"
+                                :error="v$.newPassword && v$.newPassword.$errors.length > 0"
+                                :error-message="$errMsg(v$.newPassword.$errors)"
+                                @keyup.enter="changePasswordAction"
+                            />
+
+                            <csc-input
+                                ref="passwordRetypeInput"
+                                v-model.trim="passwordRetype"
+                                outlined
+                                clearable
+                                icon="lock"
+                                color="secondary"
+                                :label="$t('Password Retype')"
+                                data-cy="password-retype-field"
+                                type="password"
+                                autocomplete="new-password"
+                                :error="v$.passwordRetype && v$.passwordRetype.$errors.length > 0"
+                                :error-message="$errMsg(v$.passwordRetype.$errors)"
+                                @keyup.enter="changePasswordAction"
+                            >
+                                <template
+                                    #prepend
+                                >
+                                    <q-icon
+                                        name="lock"
+                                    />
+                                </template>
+                            </csc-input>
+
+                            <div class="row justify-center q-pa-md">
+                                <q-btn
+                                    data-cy="sign-in"
+                                    unelevated
+                                    color="primary"
+                                    icon="arrow_forward"
+                                    :label="$t('Change Password')"
+                                    @click="changePasswordAction"
+                                />
+                            </div>
+                        </q-form>
+                    </q-card-section>
+                </q-card>
+            </q-page>
+        </q-page-container>
+    </q-layout>
+</template>
+
+<script>
+import useValidate from '@vuelidate/core'
+import { required } from '@vuelidate/validators'
+import { mapActions, mapState } from 'vuex'
+import CscSelectionLanguage from 'src/components/CscSelectionLanguage.vue'
+import CscInput from 'src/components/form/CscInput.vue'
+import CscInputPassword from 'src/components/form/CscInputPassword.vue'
+import { RequestState } from 'src/store/common'
+import { mapWaitingActions } from 'vue-wait'
+
+export default {
+    name: 'ChangeExpiredPassword',
+    components: {
+        CscSelectionLanguage,
+        CscInput,
+        CscInputPassword
+    },
+    data () {
+        return {
+            v$: useValidate(),
+            username: '',
+            currentPassword: '',
+            newPassword: '',
+            passwordStrengthScore: null,
+            passwordRetype: '',
+            validationGuidelines: []
+        }
+    },
+    validations () {
+        return {
+            username: {
+                required
+            },
+            currentPassword: {
+                required
+            },
+            newPassword: {
+                required
+            },
+            passwordRetype: {
+                sameAsPassword (val) {
+                    return val === this.newPassword
+                }
+            }
+        }
+    },
+    computed: {
+        ...mapState('user', [
+            'changePasswordState',
+            'changePasswordError'
+        ])
+    },
+    watch: {
+        changePasswordState (state) {
+            if (state === RequestState.succeeded) {
+                this.$q.notify({
+                    position: 'top',
+                    color: 'positive',
+                    icon: 'check',
+                    message: this.$t('Password changed successfully')
+                })
+                this.redirectToLogin()
+            } else if (state === RequestState.failed) {
+                this.$q.notify({
+                    position: 'top',
+                    color: 'negative',
+                    icon: 'error',
+                    timeout: 10000,
+                    message: this.changePasswordError || this.$t('There was an error, please retry later')
+                })
+            }
+        }
+    },
+    async mounted () {
+        const guidelines = await this.getValidationGuidelines()
+        this.validationGuidelines = this.formatValidationGuidelines(guidelines)
+    },
+    methods: {
+        ...mapWaitingActions('user', [
+            'fetchPreLoginPasswordInfo'
+        ]),
+        ...mapActions('user', [
+            'changeExpiredPassword'
+        ]),
+        strengthMeterScoreUpdate (score) {
+            this.passwordStrengthScore = score
+        },
+        async changePasswordAction () {
+            this.v$.$touch()
+            if (this.v$.$errors.length === 0) {
+                return this.changeExpiredPassword({
+                    username: this.username,
+                    old_password: this.currentPassword,
+                    new_password: this.newPassword
+                })
+            }
+        },
+        formatValidationGuidelines (validationRulesObject) {
+            const guidelines = []
+            for (const rule in validationRulesObject) {
+                switch (rule) {
+                case 'min_length':
+                    guidelines.push(`Minimum ${validationRulesObject[rule]} characters long.`)
+                    break
+                case 'max_length':
+                    guidelines.push(`Maximum ${validationRulesObject[rule]} characters long.`)
+                    break
+                case 'musthave_digit':
+                    guidelines.push(`Contains a minimum of ${validationRulesObject[rule]} digits.`)
+                    break
+                case 'musthave_lowercase':
+                    guidelines.push(`Contains a minimum of ${validationRulesObject[rule]} lowercases.`)
+                    break
+                case 'musthave_specialchar':
+                    guidelines.push(`Contains a minimum of ${validationRulesObject[rule]} special characters.`)
+                    break
+                case 'musthave_uppercase':
+                    guidelines.push(`Contains a minimum of ${validationRulesObject[rule]} uppercases.`)
+                    break
+                default:
+                }
+            }
+
+            return guidelines
+        },
+        async getValidationGuidelines () {
+            const defaultGuidelines = {
+                max_length: 40,
+                min_length: 12,
+                musthave_digit: 3,
+                musthave_lowercase: 3,
+                musthave_specialchar: 3,
+                musthave_uppercase: 3
+            }
+            const customGuidelines = await this.fetchPreLoginPasswordInfo()
+
+            return customGuidelines ?? defaultGuidelines
+        },
+        redirectToLogin () {
+            this.$router.push({ path: '/login' })
+        }
+    }
+}
+
+</script>
+
+<style>
+</style>
diff --git a/src/pages/CscPageLogin.vue b/src/pages/CscPageLogin.vue
index f2859739..1a684ee8 100644
--- a/src/pages/CscPageLogin.vue
+++ b/src/pages/CscPageLogin.vue
@@ -157,7 +157,7 @@ export default {
         },
         loginError (error) {
             if (error) {
-                showGlobalError(this.$t('Wrong username or password'))
+                showGlobalError(error)
             }
         }
     },
diff --git a/src/router/routes.js b/src/router/routes.js
index 8c70d6bd..5a6bfcd4 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -8,6 +8,7 @@ import CscPageCallBlockingPrivacy from 'src/pages/CscPageCallBlockingPrivacy'
 import CscPageCallRecording from 'src/pages/CscPageCallRecording'
 import CscPageCallSettings from 'pages/CscPageCallSettings'
 import CscPageCf from 'pages/CscPageCf'
+import CscPageChangePassword from 'src/pages/CscChangeExpiredPassword.vue'
 import CscPageConversations from 'src/pages/CscPageConversations'
 import CscPageCustomerPhonebook from 'src/pages/CscPageCustomerPhonebook'
 import CscPageCustomerPhonebookAdd from 'src/pages/CscPageCustomerPhonebookAdd'
@@ -45,6 +46,8 @@ import CscRecoverPassword from 'src/pages/CscRecoverPassword'
 import CscPageCustomerPreferences from 'src/pages/CscPageCustomerPreferences'
 import { i18n } from 'src/boot/i18n'
 
+export const PATH_CHANGE_PASSWORD = '/changepassword'
+
 const getToken = (route) => {
     return {
         token: route.query.token
@@ -543,6 +546,11 @@ const routes = [
             }
         }
     },
+    {
+        name: 'passwordChange',
+        path: PATH_CHANGE_PASSWORD,
+        component: CscPageChangePassword
+    },
     {
         path: '/recoverpassword',
         component: CscLayoutLogin,
diff --git a/src/store/user.js b/src/store/user.js
index 8eb3f77f..29838fb5 100644
--- a/src/store/user.js
+++ b/src/store/user.js
@@ -6,7 +6,9 @@ import {
 import {
     login,
     getUserData,
-    createAuthToken
+    createAuthToken,
+    changeExpiredPassword,
+    getPreLoginPasswordInfo
 } from '../api/user'
 import {
     changePassword,
@@ -46,6 +48,7 @@ import {
     httpApi,
     apiDownloadFile
 } from 'src/api/common'
+import { PATH_CHANGE_PASSWORD } from 'src/router/routes'
 
 export default {
     namespaced: true,
@@ -340,6 +343,9 @@ export default {
                 await this.$router.push({ name: 'dashboard' })
             } catch (err) {
                 context.commit('loginFailed', err.message)
+                if (err.message === 'Password expired') {
+                    this.$router.push({ path: PATH_CHANGE_PASSWORD })
+                }
             }
         },
         logout () {
@@ -378,6 +384,16 @@ export default {
                 }
             }
         },
+        async changeExpiredPassword (context, newPassword) {
+            context.commit('userPasswordRequesting')
+            try {
+                await changeExpiredPassword(newPassword)
+            } catch (error) {
+                return context.commit('userPasswordFailed', error)
+            }
+
+            context.commit('userPasswordSucceeded')
+        },
         async changePassword (context, newPassword) {
             const subscriberId = getSubscriberId()
             await changePassword(subscriberId, newPassword)
@@ -572,6 +588,9 @@ export default {
                 commit('setQrCode', null)
             }
         },
+        async fetchPreLoginPasswordInfo () {
+            return await getPreLoginPasswordInfo()
+        },
         async generatePasswordUser () {
             const password = await generateGeneralPassword()