MT#62982 Add 2FA for Subscribers

Subscriber can now use 2FA with API v1.
2FA can be enabled from the admin portal:
- at domain level from domain preferences.
- at subscriber level from subscriber preferences.

Flow when 2fa is enabled:
- subscriber attempts login with username and PW
- scenario1 --> 1st LOGIN:
   * request returns 403 Invalid OTP
   * UI shows QR code (with the possibility to
     visualize it as a string), instructions on
     how to use 2FA and a input for OTP token
   * once relevant info are submitted the user is logged in
- scenario2 --> NOT 1st LOGIN:
   * request returns 400 no OTP
   * UI shows only input for OTP token
   * once relevant info are submitted the user is logged in

Change-Id: Ief1ed703d3d1cfa896fe62a2c7a55897260d2cfe
mr13.4
Debora Crescenzo 4 months ago
parent d8c1f097cc
commit 7651502818

@ -9,13 +9,14 @@ import {
} from 'src/api/common' } from 'src/api/common'
import { getFaxServerSettings } from 'src/api/fax' import { getFaxServerSettings } from 'src/api/fax'
export function login (username, password) { export function login ({ username, password, otp = null }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let jwt = null let jwt = null
let subscriberId = null let subscriberId = null
httpApi.post('login_jwt', { httpApi.post('login_jwt', {
username, username,
password password,
...(otp && { otp })
}).then((result) => { }).then((result) => {
jwt = result.data.jwt jwt = result.data.jwt
subscriberId = `${result.data.subscriber_id}` subscriberId = `${result.data.subscriber_id}`

@ -0,0 +1,4 @@
export async function parseBlobToObject (blob) {
const text = await blob.text()
return JSON.parse(text)
}

@ -61,8 +61,8 @@
"Block List for outbounds calls": "Blockliste für ausgehende Anrufe", "Block List for outbounds calls": "Blockliste für ausgehende Anrufe",
"Block Mode for inbound calls": "Blockmodus für eingehende Anrufe", "Block Mode for inbound calls": "Blockmodus für eingehende Anrufe",
"Block Mode for outbounds calls": "Blockmodus für ausgehende Anrufe", "Block Mode for outbounds calls": "Blockmodus für ausgehende Anrufe",
"Block anonymous inbound calls": "Anonyme eingehende Anrufe blockieren",
"Block Outgoing": "Ausgehende Anrufe blockieren", "Block Outgoing": "Ausgehende Anrufe blockieren",
"Block anonymous inbound calls": "Anonyme eingehende Anrufe blockieren",
"Busy Greeting": "Begrüßung, wenn besetzt", "Busy Greeting": "Begrüßung, wenn besetzt",
"Busy Lamp Field": "Besetztlampenfeld", "Busy Lamp Field": "Besetztlampenfeld",
"CDR": "CDR", "CDR": "CDR",
@ -172,7 +172,10 @@
"Disable": "Deaktivieren", "Disable": "Deaktivieren",
"Disable phone web interface": "Webinterface des Telefons deaktivieren", "Disable phone web interface": "Webinterface des Telefons deaktivieren",
"Display Name": "Anzeigename", "Display Name": "Anzeigename",
"Display as QR code": "Als QR-Code anzeigen",
"Display as text": "Als Text anzeigen",
"Do not ring primary number": "Die Hauptnummer nicht klingeln lassen", "Do not ring primary number": "Die Hauptnummer nicht klingeln lassen",
"Download App": "App herunterladen",
"Download CSV": "CSV herunterladen", "Download CSV": "CSV herunterladen",
"Download fax": "Fax herunterladen", "Download fax": "Fax herunterladen",
"Download voicemail": "Voicemail herunterladen", "Download voicemail": "Voicemail herunterladen",
@ -188,6 +191,7 @@
"Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Aktivieren des strengen Modus, der verlangt, dass alle mail2fax-E-Mails in der ersten Zeile den Geheimschlüssel gefolgt von einer Leerzeile enthalten. Der Schlüssel wird nach dem Abgleich aus der E-Mail entfernt.", "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Aktivieren des strengen Modus, der verlangt, dass alle mail2fax-E-Mails in der ersten Zeile den Geheimschlüssel gefolgt von einer Leerzeile enthalten. Der Schlüssel wird nach dem Abgleich aus der E-Mail entfernt.",
"End time": "Endzeit", "End time": "Endzeit",
"English": "Englisch", "English": "Englisch",
"Enter OTP": "OTP-Code eingeben",
"Enter a number to dial": "Geben Sie eine Telefonnummer zum Anwählen an", "Enter a number to dial": "Geben Sie eine Telefonnummer zum Anwählen an",
"Entity belongs to admin": "Diese Einheit gehört dem Admin", "Entity belongs to admin": "Diese Einheit gehört dem Admin",
"Expires": "Läuft aus", "Expires": "Läuft aus",
@ -266,7 +270,9 @@
"Input a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein", "Input a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
"Input is required": "Eingabe erforderlich", "Input is required": "Eingabe erforderlich",
"Input must be a valid number": "Die Eingabe muss eine gültige Zahl sein", "Input must be a valid number": "Die Eingabe muss eine gültige Zahl sein",
"Install Google Authenticator app on your mobile device or use your preferred Authenticator app.": "Installieren Sie die Google Authenticator-App auf Ihrem mobilen Gerät oder verwenden Sie Ihre bevorzugte Authenticator-App.",
"Interval when the secret key is automatically renewed.": "Intervall, nach dem der geheime Schlüssel automatisch erneuert wird.", "Interval when the secret key is automatically renewed.": "Intervall, nach dem der geheime Schlüssel automatisch erneuert wird.",
"Invalid OTP Code": "Ungültiger OTP-Code",
"Italian": "Italiano", "Italian": "Italiano",
"January": "Januar", "January": "Januar",
"Join conference": "Konferenz beitreten", "Join conference": "Konferenz beitreten",
@ -354,6 +360,8 @@
"Number list": "Nummernliste", "Number list": "Nummernliste",
"Number list name": "Name der Nummernliste", "Number list name": "Name der Nummernliste",
"Numbers": "Nummern", "Numbers": "Nummern",
"OTP Code": "OTP-Code",
"OTP Verification": "OTP-Verifizierung",
"October": "Oktober", "October": "Oktober",
"Office Hours Announcement": "Ansage zu den Bürozeiten", "Office Hours Announcement": "Ansage zu den Bürozeiten",
"On weekdays": "An Werktagen", "On weekdays": "An Werktagen",
@ -361,6 +369,7 @@
"Only none decimal numbers are allowed": "Nur Ganzzahlen erlaubt", "Only none decimal numbers are allowed": "Nur Ganzzahlen erlaubt",
"Only once": "Nur einmal", "Only once": "Nur einmal",
"Only outgoing calls to listed numbers are allowed": "Nur ausgehende Anrufe an aufgelistete Nummern erlaubt", "Only outgoing calls to listed numbers are allowed": "Nur ausgehende Anrufe an aufgelistete Nummern erlaubt",
"Open the Authenticator app to register your NGCP account.": "Öffnen Sie die Authenticator-App, um Ihr NGCP-Konto zu registrieren.",
"Out": "Aus", "Out": "Aus",
"Outgoing": "Ausgehend", "Outgoing": "Ausgehend",
"PBX Configuration": "PBX-Konfiguration", "PBX Configuration": "PBX-Konfiguration",
@ -466,6 +475,7 @@
"Saturday": "Samstag", "Saturday": "Samstag",
"Save": "Speichern", "Save": "Speichern",
"Save new password": "Neues Passwort speichern", "Save new password": "Neues Passwort speichern",
"Scan QR code": "QR-Code scannen",
"Scan to login sip:phone": "Zur Anmeldung in sip:phone scannen", "Scan to login sip:phone": "Zur Anmeldung in sip:phone scannen",
"Seat": "Nebenstelle", "Seat": "Nebenstelle",
"Seat Display Name": "Nebenstelle Anzeigename", "Seat Display Name": "Nebenstelle Anzeigename",
@ -554,6 +564,7 @@
"Unblock Incoming/Outgoing": "Ein-/Ausgehende Anrufe entsperren", "Unblock Incoming/Outgoing": "Ein-/Ausgehende Anrufe entsperren",
"Unblock Outgoing": "Ausgehende Anrufe entsperren", "Unblock Outgoing": "Ausgehende Anrufe entsperren",
"Undo": "Rückgängig machen", "Undo": "Rückgängig machen",
"Unexpected error": "Unerwarteter Fehler",
"Unknown error": "Unbekannter Fehler", "Unknown error": "Unbekannter Fehler",
"Unknown name": "Unbekannter Name", "Unknown name": "Unbekannter Name",
"Unmute": "Stummschaltung aufheben", "Unmute": "Stummschaltung aufheben",
@ -570,6 +581,7 @@
"Use as default for all seats and groups": "Standard für alle Nebenstellen und Gruppen", "Use as default for all seats and groups": "Standard für alle Nebenstellen und Gruppen",
"Use custom number": "Benutzerdefinierte Nummer verwenden", "Use custom number": "Benutzerdefinierte Nummer verwenden",
"Use language specific preset": "Sprachspezifische Voreinstellungen verwenden", "Use language specific preset": "Sprachspezifische Voreinstellungen verwenden",
"Use the Authenticator app to generate the verification code.": "Verwenden Sie die Authenticator-App, um den Bestätigungscode zu generieren.",
"User Agent": "User Agent", "User Agent": "User Agent",
"User config priority over provisioning": "Benutzerkonfiguration hat Vorrang vor Provisionierung", "User config priority over provisioning": "Benutzerkonfiguration hat Vorrang vor Provisionierung",
"User settings": "Benutzereinstellungen", "User settings": "Benutzereinstellungen",

@ -60,8 +60,8 @@
"Block List for outbounds calls": "Block List for outbounds calls", "Block List for outbounds calls": "Block List for outbounds calls",
"Block Mode for inbound calls": "Block Mode for inbound calls", "Block Mode for inbound calls": "Block Mode for inbound calls",
"Block Mode for outbounds calls": "Block Mode for outbounds calls", "Block Mode for outbounds calls": "Block Mode for outbounds calls",
"Block anonymous inbound calls": "Block anonymous inbound calls",
"Block Outgoing": "Block Outgoing", "Block Outgoing": "Block Outgoing",
"Block anonymous inbound calls": "Block anonymous inbound calls",
"Busy Greeting": "Busy Greeting", "Busy Greeting": "Busy Greeting",
"Busy Lamp Field": "Busy Lamp Field", "Busy Lamp Field": "Busy Lamp Field",
"CDR": "CDR", "CDR": "CDR",
@ -169,7 +169,10 @@
"Disable": "Disable", "Disable": "Disable",
"Disable phone web interface": "Disable phone web interface", "Disable phone web interface": "Disable phone web interface",
"Display Name": "Display Name", "Display Name": "Display Name",
"Display as QR code": "Display as QR code",
"Display as text": "Display as text",
"Do not ring primary number": "Do not ring primary number", "Do not ring primary number": "Do not ring primary number",
"Download App": "Download App",
"Download CSV": "Download CSV", "Download CSV": "Download CSV",
"Download fax": "Download fax", "Download fax": "Download fax",
"Download voicemail": "Download voicemail", "Download voicemail": "Download voicemail",
@ -185,6 +188,7 @@
"Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.", "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.",
"End time": "End time", "End time": "End time",
"English": "English", "English": "English",
"Enter OTP": "Enter OTP",
"Enter a number to dial": "Enter a number to dial", "Enter a number to dial": "Enter a number to dial",
"Entity belongs to admin": "Entity belongs to admin", "Entity belongs to admin": "Entity belongs to admin",
"Expires": "Expires", "Expires": "Expires",
@ -262,7 +266,9 @@
"Input a valid phone number": "Input a valid phone number", "Input a valid phone number": "Input a valid phone number",
"Input is required": "Input is required", "Input is required": "Input is required",
"Input must be a valid number": "Input must be a valid number", "Input must be a valid number": "Input must be a valid number",
"Install Google Authenticator app on your mobile device or use your preferred Authenticator app.": "Install Google Authenticator app on your mobile device or use your preferred Authenticator app.",
"Interval when the secret key is automatically renewed.": "Interval when the secret key is automatically renewed.", "Interval when the secret key is automatically renewed.": "Interval when the secret key is automatically renewed.",
"Invalid OTP Code": "Invalid OTP Code",
"Italian": "Italiano", "Italian": "Italiano",
"January": "January", "January": "January",
"July": "July", "July": "July",
@ -340,6 +346,8 @@
"Number list": "Number list", "Number list": "Number list",
"Number list name": "Number list name", "Number list name": "Number list name",
"Numbers": "Numbers", "Numbers": "Numbers",
"OTP Code": "OTP Code",
"OTP Verification": "OTP Verification",
"October": "October", "October": "October",
"Office Hours Announcement": "Office Hours Announcement", "Office Hours Announcement": "Office Hours Announcement",
"On weekdays": "On weekdays", "On weekdays": "On weekdays",
@ -347,6 +355,7 @@
"Only none decimal numbers are allowed": "Only none decimal numbers are allowed", "Only none decimal numbers are allowed": "Only none decimal numbers are allowed",
"Only once": "Only once", "Only once": "Only once",
"Only outgoing calls to listed numbers are allowed": "Only outgoing calls to listed numbers are allowed", "Only outgoing calls to listed numbers are allowed": "Only outgoing calls to listed numbers are allowed",
"Open the Authenticator app to register your NGCP account.": "Open the Authenticator app to register your NGCP account.",
"Out": "Out", "Out": "Out",
"Outgoing": "Outgoing", "Outgoing": "Outgoing",
"PBX Configuration": "PBX Configuration", "PBX Configuration": "PBX Configuration",
@ -451,6 +460,7 @@
"Saturday": "Saturday", "Saturday": "Saturday",
"Save": "Save", "Save": "Save",
"Save new password": "Save new password", "Save new password": "Save new password",
"Scan QR code": "Scan QR code",
"Scan to login sip:phone": "Scan to login sip:phone", "Scan to login sip:phone": "Scan to login sip:phone",
"Seat": "Seat", "Seat": "Seat",
"Seat Display Name": "Seat Display Name", "Seat Display Name": "Seat Display Name",
@ -537,6 +547,7 @@
"Unblock Incoming/Outgoing": "Unblock Incoming/Outgoing", "Unblock Incoming/Outgoing": "Unblock Incoming/Outgoing",
"Unblock Outgoing": "Unblock Outgoing", "Unblock Outgoing": "Unblock Outgoing",
"Undo": "Undo", "Undo": "Undo",
"Unexpected error": "Unexpected error",
"Unknown error": "Unknown error", "Unknown error": "Unknown error",
"Unknown name": "Unknown name", "Unknown name": "Unknown name",
"Updated {field} for call queue {callQueue} successfully": "Updated {field} for call queue {callQueue} successfully", "Updated {field} for call queue {callQueue} successfully": "Updated {field} for call queue {callQueue} successfully",
@ -551,6 +562,7 @@
"Use as default for all seats and groups": "Use as default for all seats and groups", "Use as default for all seats and groups": "Use as default for all seats and groups",
"Use custom number": "Use custom number", "Use custom number": "Use custom number",
"Use language specific preset": "Use language specific preset", "Use language specific preset": "Use language specific preset",
"Use the Authenticator app to generate the verification code.": "Use the Authenticator app to generate the verification code.",
"User Agent": "User Agent", "User Agent": "User Agent",
"User config priority over provisioning": "User config priority over provisioning", "User config priority over provisioning": "User config priority over provisioning",
"User settings": "User settings", "User settings": "User settings",

@ -61,8 +61,8 @@
"Block List for outbounds calls": "Block List for outbounds calls", "Block List for outbounds calls": "Block List for outbounds calls",
"Block Mode for inbound calls": "Block Mode for inbound calls", "Block Mode for inbound calls": "Block Mode for inbound calls",
"Block Mode for outbounds calls": "Block Mode for outbounds calls", "Block Mode for outbounds calls": "Block Mode for outbounds calls",
"Block anonymous inbound calls": "Block anonymous inbound calls",
"Block Outgoing": "Bloquear Salientes", "Block Outgoing": "Bloquear Salientes",
"Block anonymous inbound calls": "Block anonymous inbound calls",
"Busy Greeting": "Saludo de Ocupado", "Busy Greeting": "Saludo de Ocupado",
"Busy Lamp Field": "Campo de lámpara ocupado", "Busy Lamp Field": "Campo de lámpara ocupado",
"CDR": "CDR", "CDR": "CDR",
@ -172,7 +172,10 @@
"Disable": "Desactivar", "Disable": "Desactivar",
"Disable phone web interface": "Deshabilitar la interfaz web del teléfono", "Disable phone web interface": "Deshabilitar la interfaz web del teléfono",
"Display Name": "Display Name", "Display Name": "Display Name",
"Display as QR code": "Mostrar como código QR",
"Display as text": "Mostrar como texto",
"Do not ring primary number": "No llamar al número principal", "Do not ring primary number": "No llamar al número principal",
"Download App": "Descargar la aplicación",
"Download CSV": "Download CSV", "Download CSV": "Download CSV",
"Download fax": "Descargar fax", "Download fax": "Descargar fax",
"Download voicemail": "Descargar correo de voz", "Download voicemail": "Descargar correo de voz",
@ -188,6 +191,7 @@
"Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Activar modo estricto requiere que todos los correos electrónicos de Correo a Fax tengan la clave secreta como la primera línea del correo electrónico y una línea vacía. La clave se elimina del correo electrónico una vez que coincide.", "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Activar modo estricto requiere que todos los correos electrónicos de Correo a Fax tengan la clave secreta como la primera línea del correo electrónico y una línea vacía. La clave se elimina del correo electrónico una vez que coincide.",
"End time": "Hora de finalización", "End time": "Hora de finalización",
"English": "English", "English": "English",
"Enter OTP": "Introduce el código OTP",
"Enter a number to dial": "Introduzca un número para marcar", "Enter a number to dial": "Introduzca un número para marcar",
"Entity belongs to admin": "Esta entidad pertenece a admin", "Entity belongs to admin": "Esta entidad pertenece a admin",
"Expires": "Expira", "Expires": "Expira",
@ -266,7 +270,9 @@
"Input a valid phone number": "Ingrese un número de teléfono válido", "Input a valid phone number": "Ingrese un número de teléfono válido",
"Input is required": "El campo es obligatorio", "Input is required": "El campo es obligatorio",
"Input must be a valid number": "El campo debe ser un número válido", "Input must be a valid number": "El campo debe ser un número válido",
"Install Google Authenticator app on your mobile device or use your preferred Authenticator app.": "Instala la aplicación Google Authenticator en tu dispositivo móvil o usa tu aplicación de autenticación preferida.",
"Interval when the secret key is automatically renewed.": "Intervalo en el que la clave secreta se renueva automáticamente.", "Interval when the secret key is automatically renewed.": "Intervalo en el que la clave secreta se renueva automáticamente.",
"Invalid OTP Code": "Código OTP no válido",
"Italian": "Italiano", "Italian": "Italiano",
"January": "Enero", "January": "Enero",
"Join conference": "Unirse a la conferencia", "Join conference": "Unirse a la conferencia",
@ -353,6 +359,8 @@
"Number list": "Lista de números", "Number list": "Lista de números",
"Number list name": "Nombre de la lista de números", "Number list name": "Nombre de la lista de números",
"Numbers": "Números", "Numbers": "Números",
"OTP Code": "Código OTP",
"OTP Verification": "Verificación OTP",
"October": "Octubre", "October": "Octubre",
"Office Hours Announcement": "Anuncio de Horarios de Oficina", "Office Hours Announcement": "Anuncio de Horarios de Oficina",
"On weekdays": "En días de la semana", "On weekdays": "En días de la semana",
@ -360,6 +368,7 @@
"Only none decimal numbers are allowed": "Sólo se permiten números no decimales", "Only none decimal numbers are allowed": "Sólo se permiten números no decimales",
"Only once": "Solo una vez", "Only once": "Solo una vez",
"Only outgoing calls to listed numbers are allowed": "Permitir solo los números listados", "Only outgoing calls to listed numbers are allowed": "Permitir solo los números listados",
"Open the Authenticator app to register your NGCP account.": "Abre la aplicación de autenticación para registrar tu cuenta NGCP.",
"Out": "Out", "Out": "Out",
"Outgoing": "Saliente", "Outgoing": "Saliente",
"PBX Configuration": "Configuración PBX", "PBX Configuration": "Configuración PBX",
@ -466,6 +475,7 @@
"Saturday": "Sábado", "Saturday": "Sábado",
"Save": "Guardar", "Save": "Guardar",
"Save new password": "Guardar nueva contraseña", "Save new password": "Guardar nueva contraseña",
"Scan QR code": "Escanear código QR",
"Scan to login sip:phone": "Escanear para iniciar sesión Sip:phone", "Scan to login sip:phone": "Escanear para iniciar sesión Sip:phone",
"Seat": "Asiento", "Seat": "Asiento",
"Seat Display Name": "Seat Display Name", "Seat Display Name": "Seat Display Name",
@ -556,6 +566,7 @@
"Unblock Incoming/Outgoing": "Desbloquear Entrantes/Salientes", "Unblock Incoming/Outgoing": "Desbloquear Entrantes/Salientes",
"Unblock Outgoing": "Desbloquear Salientes", "Unblock Outgoing": "Desbloquear Salientes",
"Undo": "Deshacer", "Undo": "Deshacer",
"Unexpected error": "Error inesperado",
"Unknown error": "Error desconocido", "Unknown error": "Error desconocido",
"Unknown name": "Nombre desconocido", "Unknown name": "Nombre desconocido",
"Unmute": "Activar sonido", "Unmute": "Activar sonido",
@ -572,6 +583,7 @@
"Use as default for all seats and groups": "Usar por defecto para todos los asientos y grupos", "Use as default for all seats and groups": "Usar por defecto para todos los asientos y grupos",
"Use custom number": "Use custom number", "Use custom number": "Use custom number",
"Use language specific preset": "Usar un preajuste específico de idioma", "Use language specific preset": "Usar un preajuste específico de idioma",
"Use the Authenticator app to generate the verification code.": "Usa la aplicación de autenticación para generar el código de verificación.",
"User Agent": "Agente de usuario", "User Agent": "Agente de usuario",
"User config priority over provisioning": "Prioridad de la configuración del usuario sobre la provisión", "User config priority over provisioning": "Prioridad de la configuración del usuario sobre la provisión",
"User settings": "Ajustes de usuario", "User settings": "Ajustes de usuario",

@ -61,8 +61,8 @@
"Block List for outbounds calls": "Liste de blocage pour les appels sortants", "Block List for outbounds calls": "Liste de blocage pour les appels sortants",
"Block Mode for inbound calls": "Mode blocage pour les appels entrants", "Block Mode for inbound calls": "Mode blocage pour les appels entrants",
"Block Mode for outbounds calls": "Mode blocage pour les appels sortants", "Block Mode for outbounds calls": "Mode blocage pour les appels sortants",
"Block anonymous inbound calls": "Bloquer les appels entrants anonymes",
"Block Outgoing": "Bloquer les Sortants", "Block Outgoing": "Bloquer les Sortants",
"Block anonymous inbound calls": "Bloquer les appels entrants anonymes",
"Busy Greeting": "Message d'accueil en cas d'occupation", "Busy Greeting": "Message d'accueil en cas d'occupation",
"Busy Lamp Field": "Champ de la lampe occupée", "Busy Lamp Field": "Champ de la lampe occupée",
"CDR": "CDR", "CDR": "CDR",
@ -172,7 +172,10 @@
"Disable": "Désactiver", "Disable": "Désactiver",
"Disable phone web interface": "Désactiver l'interface Web du téléphone", "Disable phone web interface": "Désactiver l'interface Web du téléphone",
"Display Name": "Display Name", "Display Name": "Display Name",
"Display as QR code": "Afficher sous forme de code QR",
"Display as text": "Afficher sous forme de texte",
"Do not ring primary number": "Ne pas appeler le numéro principal", "Do not ring primary number": "Ne pas appeler le numéro principal",
"Download App": "Télécharger lapplication",
"Download CSV": "Télécharger CSV", "Download CSV": "Télécharger CSV",
"Download fax": "Télécharger le Fax", "Download fax": "Télécharger le Fax",
"Download voicemail": "Télécharger le message vocal", "Download voicemail": "Télécharger le message vocal",
@ -188,6 +191,7 @@
"Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Activez le mode strict qui exige que tous les e-mails de mail2fax aient la clé secrète comme toute première ligne de l'e-mail + une ligne vide. La clé est supprimée de l'e-mail une fois qu'elle a été trouvée.", "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Activez le mode strict qui exige que tous les e-mails de mail2fax aient la clé secrète comme toute première ligne de l'e-mail + une ligne vide. La clé est supprimée de l'e-mail une fois qu'elle a été trouvée.",
"End time": "Date de fin", "End time": "Date de fin",
"English": "English", "English": "English",
"Enter OTP": "OTP-Code eingeben",
"Enter a number to dial": "Enter a number to dial", "Enter a number to dial": "Enter a number to dial",
"Entity belongs to admin": "Cette entité appartient à ladministrateur", "Entity belongs to admin": "Cette entité appartient à ladministrateur",
"Expires": "Expire", "Expires": "Expire",
@ -266,7 +270,9 @@
"Input a valid phone number": "Saisissez un numéro de téléphone valide", "Input a valid phone number": "Saisissez un numéro de téléphone valide",
"Input is required": "Le champ est obligatoire", "Input is required": "Le champ est obligatoire",
"Input must be a valid number": "Le champ doit être un nombre valide", "Input must be a valid number": "Le champ doit être un nombre valide",
"Install Google Authenticator app on your mobile device or use your preferred Authenticator app.": "Installez lapplication Google Authenticator sur votre appareil mobile ou utilisez votre application dauthentification préférée.",
"Interval when the secret key is automatically renewed.": "Intervalle où la clé secrète est automatiquement renouvelée.", "Interval when the secret key is automatically renewed.": "Intervalle où la clé secrète est automatiquement renouvelée.",
"Invalid OTP Code": "Code OTP invalid",
"Italian": "Italiano", "Italian": "Italiano",
"January": "Janvier", "January": "Janvier",
"Join conference": "Rejoindre la conférence", "Join conference": "Rejoindre la conférence",
@ -354,6 +360,8 @@
"Number list": "Liste de numéros", "Number list": "Liste de numéros",
"Number list name": "Nom de la liste de numéros", "Number list name": "Nom de la liste de numéros",
"Numbers": "Nombres", "Numbers": "Nombres",
"OTP Code": "Code OTP",
"OTP Verification": "Vérification OTP",
"October": "Octobre", "October": "Octobre",
"Office Hours Announcement": "Office Hours Announcement", "Office Hours Announcement": "Office Hours Announcement",
"On weekdays": "Les jours de semaine", "On weekdays": "Les jours de semaine",
@ -361,6 +369,7 @@
"Only none decimal numbers are allowed": "Seuls les nombres non décimaux sont autorisés", "Only none decimal numbers are allowed": "Seuls les nombres non décimaux sont autorisés",
"Only once": "Une seule fois", "Only once": "Une seule fois",
"Only outgoing calls to listed numbers are allowed": "Seuls les appels sortants vers les numéros listés sont autorisés", "Only outgoing calls to listed numbers are allowed": "Seuls les appels sortants vers les numéros listés sont autorisés",
"Open the Authenticator app to register your NGCP account.": "Ouvrez lapplication dauthentification pour enregistrer votre compte NGCP.",
"Out": "Out", "Out": "Out",
"Outgoing": "Sortant", "Outgoing": "Sortant",
"PBX Configuration": "Configuration du PBX", "PBX Configuration": "Configuration du PBX",
@ -466,6 +475,7 @@
"Saturday": "Samedi", "Saturday": "Samedi",
"Save": "Enregistrer", "Save": "Enregistrer",
"Save new password": "Sauvegarder le nouveau mot de passe", "Save new password": "Sauvegarder le nouveau mot de passe",
"Scan QR code": "Scanner le code QR",
"Scan to login sip:phone": "Scan to login sip:phone", "Scan to login sip:phone": "Scan to login sip:phone",
"Seat": "Siège", "Seat": "Siège",
"Seat Display Name": "Seat Display Name", "Seat Display Name": "Seat Display Name",
@ -554,6 +564,7 @@
"Unblock Incoming/Outgoing": "Débloquer les entrants/sortants", "Unblock Incoming/Outgoing": "Débloquer les entrants/sortants",
"Unblock Outgoing": "Débloquer les sortants", "Unblock Outgoing": "Débloquer les sortants",
"Undo": "Annuler", "Undo": "Annuler",
"Unexpected error": "Erreur inattendue",
"Unknown error": "Erreur inconnue", "Unknown error": "Erreur inconnue",
"Unknown name": "Nom inconnu", "Unknown name": "Nom inconnu",
"Unmute": "Réactiver le son", "Unmute": "Réactiver le son",
@ -570,6 +581,7 @@
"Use as default for all seats and groups": "Utiliser par défaut pour tous les sièges et groupes", "Use as default for all seats and groups": "Utiliser par défaut pour tous les sièges et groupes",
"Use custom number": "Use custom number", "Use custom number": "Use custom number",
"Use language specific preset": "Utiliser un préréglage spécifique à la langue", "Use language specific preset": "Utiliser un préréglage spécifique à la langue",
"Use the Authenticator app to generate the verification code.": "Utilisez lapplication dauthentification pour générer le code de vérification.",
"User Agent": "User Agent", "User Agent": "User Agent",
"User config priority over provisioning": "Priorité de la configuration utilisateur sur le provisionnement", "User config priority over provisioning": "Priorité de la configuration utilisateur sur le provisionnement",
"User settings": "Paramètres utilisateur", "User settings": "Paramètres utilisateur",

@ -59,8 +59,8 @@
"Block List for outbounds calls": "Lista numeri bloccati in uscita", "Block List for outbounds calls": "Lista numeri bloccati in uscita",
"Block Mode for inbound calls": "Modalità di blocco per le chiamate in entrata", "Block Mode for inbound calls": "Modalità di blocco per le chiamate in entrata",
"Block Mode for outbounds calls": "Modalità di blocco per le chiamate in uscita", "Block Mode for outbounds calls": "Modalità di blocco per le chiamate in uscita",
"Block anonymous inbound calls": "Blocca chiamate anonime in entrata",
"Block Outgoing": "Blocca Chiamate in Uscita", "Block Outgoing": "Blocca Chiamate in Uscita",
"Block anonymous inbound calls": "Blocca chiamate anonime in entrata",
"Busy Greeting": "Messaggio per numero occupato", "Busy Greeting": "Messaggio per numero occupato",
"Busy Lamp Field": "Busy Lamp Field", "Busy Lamp Field": "Busy Lamp Field",
"CDR": "CDR", "CDR": "CDR",
@ -170,7 +170,10 @@
"Disable": "Disabilita", "Disable": "Disabilita",
"Disable phone web interface": "Disabilita interfaccia web del telefono", "Disable phone web interface": "Disabilita interfaccia web del telefono",
"Display Name": "Nome visualizzato", "Display Name": "Nome visualizzato",
"Display as QR code": "Mostra come codice QR",
"Display as text": "Mostra come testo",
"Do not ring primary number": "Non squillare sul numero principale", "Do not ring primary number": "Non squillare sul numero principale",
"Download App": "Scarica l'app",
"Download CSV": "Scarica CSV", "Download CSV": "Scarica CSV",
"Download fax": "Scarica fax", "Download fax": "Scarica fax",
"Download voicemail": "Scarica messaggio vocale", "Download voicemail": "Scarica messaggio vocale",
@ -185,6 +188,7 @@
"Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Abilita la modalità rigorosa che richiede che tutte le email mail2fax contengano la chiave segreta come prima riga + una riga vuota. La chiave viene rimossa una volta riconosciuta.", "Enable strict mode that requires all mail2fax emails to have the secret key as the very first line of the email + an empty line. The key is removed from the email once matched.": "Abilita la modalità rigorosa che richiede che tutte le email mail2fax contengano la chiave segreta come prima riga + una riga vuota. La chiave viene rimossa una volta riconosciuta.",
"End time": "Orario di fine", "End time": "Orario di fine",
"English": "English", "English": "English",
"Enter OTP": " Inserisci il codice OTP",
"Enter a number to dial": "Inserisci un numero da chiamare", "Enter a number to dial": "Inserisci un numero da chiamare",
"Entity belongs to admin": "L'entità appartiene all'amministratore", "Entity belongs to admin": "L'entità appartiene all'amministratore",
"Expires": "Scade", "Expires": "Scade",
@ -263,7 +267,9 @@
"Input a valid phone number": "Inserire un numero di telefono valido", "Input a valid phone number": "Inserire un numero di telefono valido",
"Input is required": "Il campo è obbligatorio", "Input is required": "Il campo è obbligatorio",
"Input must be a valid number": "Il campo deve essere un numero valido", "Input must be a valid number": "Il campo deve essere un numero valido",
"Install Google Authenticator app on your mobile device or use your preferred Authenticator app.": "Installa l'app Google Authenticator sul tuo dispositivo mobile o usa la tua app di autenticazione preferita.",
"Interval when the secret key is automatically renewed.": "Intervallo di rinnovo automatico della chiave segreta.", "Interval when the secret key is automatically renewed.": "Intervallo di rinnovo automatico della chiave segreta.",
"Invalid OTP Code": "Codice OTP non valido",
"Italian": "Italiano", "Italian": "Italiano",
"January": "Gennaio", "January": "Gennaio",
"Join conference with name": "Partecipa alla conferenza con nome", "Join conference with name": "Partecipa alla conferenza con nome",
@ -347,6 +353,8 @@
"Number list": "Lista numeri", "Number list": "Lista numeri",
"Number list name": "Nome della lista numeri", "Number list name": "Nome della lista numeri",
"Numbers": "Numeri", "Numbers": "Numeri",
"OTP Code": "Codice OTP",
"OTP Verification": "Verifica OTP",
"October": "Ottobre", "October": "Ottobre",
"Office Hours Announcement": "Annuncio degli Orari di Ufficio", "Office Hours Announcement": "Annuncio degli Orari di Ufficio",
"On weekdays": "Nei giorni feriali", "On weekdays": "Nei giorni feriali",
@ -354,6 +362,7 @@
"Only none decimal numbers are allowed": "Sono ammessi solo numeri non decimali", "Only none decimal numbers are allowed": "Sono ammessi solo numeri non decimali",
"Only once": "Solo una volta", "Only once": "Solo una volta",
"Only outgoing calls to listed numbers are allowed": "Sono ammesse solo le chiamate in uscita verso i numeri elencati", "Only outgoing calls to listed numbers are allowed": "Sono ammesse solo le chiamate in uscita verso i numeri elencati",
"Open the Authenticator app to register your NGCP account.": "Apri l'app di autenticazione per registrare il tuo account NGCP.",
"Out": "Out", "Out": "Out",
"Outgoing": "In uscita", "Outgoing": "In uscita",
"PBX Configuration": "Configurazione PBX", "PBX Configuration": "Configurazione PBX",
@ -459,6 +468,7 @@
"Saturday": "Sabato", "Saturday": "Sabato",
"Save": "Salva", "Save": "Salva",
"Save new password": "Salva nuova password", "Save new password": "Salva nuova password",
"Scan QR code": "Scansiona il codice QR",
"Scan to login sip:phone": "Scansiona per accedere sip:phone", "Scan to login sip:phone": "Scansiona per accedere sip:phone",
"Seat": "Postazione", "Seat": "Postazione",
"Seat Display Name": "Nome visualizzato postazione", "Seat Display Name": "Nome visualizzato postazione",
@ -546,6 +556,7 @@
"Unblock Incoming/Outgoing": "Sblocca entrata/uscita", "Unblock Incoming/Outgoing": "Sblocca entrata/uscita",
"Unblock Outgoing": "Sblocca chiamate in uscita", "Unblock Outgoing": "Sblocca chiamate in uscita",
"Undo": "Annulla", "Undo": "Annulla",
"Unexpected error": "Errore imprevisto",
"Unknown error": "Errore sconosciuto", "Unknown error": "Errore sconosciuto",
"Unknown name": "Nome sconosciuto", "Unknown name": "Nome sconosciuto",
"Updated {field} for call queue {callQueue} successfully": "Aggiornato {field} per la coda chiamate {callQueue}", "Updated {field} for call queue {callQueue} successfully": "Aggiornato {field} per la coda chiamate {callQueue}",
@ -560,6 +571,7 @@
"Use as default for all seats and groups": "Usa come predefinito per tutte le postazioni e i gruppi", "Use as default for all seats and groups": "Usa come predefinito per tutte le postazioni e i gruppi",
"Use custom number": "Usa numero personalizzato", "Use custom number": "Usa numero personalizzato",
"Use language specific preset": "Usa predefiniti per la lingua", "Use language specific preset": "Usa predefiniti per la lingua",
"Use the Authenticator app to generate the verification code.": "Usa l'app di autenticazione per generare il codice di verifica.",
"User Agent": "User Agent", "User Agent": "User Agent",
"User config priority over provisioning": "Priorità della configurazione utente sul provisioning", "User config priority over provisioning": "Priorità della configurazione utente sul provisioning",
"User settings": "Impostazioni utente", "User settings": "Impostazioni utente",

@ -59,6 +59,163 @@
clearable clearable
@keypress.enter="login()" @keypress.enter="login()"
/> />
<div
v-if="showOTP"
class="row q-mb-md"
>
<q-card
flat
bordered
class="q-mt-lg"
>
<q-card-section
v-if="showOTPSecret"
class="text-center q-mt-none"
>
<q-icon
name="key"
size="4rem"
color="primary"
/>
<h4 class="text-h4 h4 text-center q-mt-sm q-mb-sm">
{{ $t('OTP Verification') }}
</h4>
</q-card-section>
<q-card-section v-if="showOTPSecret">
<q-list>
<q-item>
<div>
<h6 class="q-ma-sm">
<q-icon
name="download"
size="2rem"
color="primary"
class="q-mr-sm"
/>
{{ $t('Download App') }}
</h6>
<p class="q-ml-md">
{{ $t('Install Google Authenticator app on your mobile device or use your preferred Authenticator app.') }}
</p>
</div>
</q-item>
<div
align="center"
class="q-pa-md"
>
<a
class="q-ma-md"
target="_blank"
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1"
><img
alt="Get it on Google Play"
src="google-play-badge.png"
class="app-badge google-play-badge"
></a>
<a
class="q-ma-md"
target="_blank"
href="https://apps.apple.com/us/app/google-authenticator/id388497605?itsct=apps_box_badge&itscg=30200"
><img
src="apple-store-badge.svg"
alt="Download on the App Store"
class="app-badge apple-store-badge"
></a>
</div>
<q-item>
<div>
<h6 class="q-ma-sm">
<q-icon
name="qr_code"
size="2rem"
color="primary"
class="q-mr-sm"
/> {{ $t('Scan QR code') }}
</h6>
<p class="q-ml-md">
{{ $t('Open the Authenticator app to register your NGCP account.') }}
</p>
</div>
</q-item>
<div
v-if="OTPSecret && OTPSecret.type === 'qr'"
class="text-center q-pa-md"
>
<q-img
:src="OTPSecret.data"
class="qr-code bg-white"
/>
<q-btn
color="primary"
small
class="q-mt-md full-width"
@click="getOTPAsText"
>
{{ $t('Display as text') }}
</q-btn>
</div>
<div
v-if="OTPSecret && OTPSecret.type === 'text'"
class="text-center q-pa-md"
>
<q-card class="q-pa-sm">
<p class="text-bold text-green">
{{ OTPSecret.data }}
</p>
</q-card>
<q-btn
color="primary"
small
class="q-mt-md full-width"
@click="getOTPAsQrCode"
>
{{ $t('Display as QR code') }}
</q-btn>
</div>
</q-list>
</q-card-section>
<q-card-section>
<q-list>
<q-item>
<div>
<h6 class="q-ma-sm">
<q-icon
name="password"
size="2rem"
color="primary"
class="q-mr-sm"
/>{{ $t('Enter OTP') }}
</h6>
<p class="q-ml-md">
{{ $t('Use the Authenticator app to generate the verification code.') }}
</p>
</div>
</q-item>
<q-item class="justify-center">
<q-input
v-model="otp"
color="primary"
label-color="primary"
data-cy="otp-code"
:loading="loginRequesting"
:disable="loginRequesting"
:label="$t('OTP Code')"
:error="OTPError"
:error-message="loginError"
@input-clear="clearOTP"
@input="focusOTP"
@keypress.enter="login"
/>
</q-item>
</q-list>
</q-card-section>
</q-card>
</div>
</form> </form>
</q-card-section> </q-card-section>
<q-card-actions <q-card-actions
@ -123,7 +280,9 @@ export default {
return { return {
username: '', username: '',
password: '', password: '',
showDialog: false showDialog: false,
otp: null,
OTPError: false
} }
}, },
computed: { computed: {
@ -140,8 +299,16 @@ export default {
...mapGetters('user', [ ...mapGetters('user', [
'loginRequesting', 'loginRequesting',
'loginSucceeded', 'loginSucceeded',
'loginError' 'loginError',
]) 'loginWaitingOTPCode',
'OTPSecret'
]),
showOTP () {
return this.loginWaitingOTPCode || this.loginError === 'Invalid OTP Code'
},
showOTPSecret () {
return this.OTPSecret !== null || (this.OTPSecret !== null && this.loginError === 'Invalid OTP Code')
}
}, },
watch: { watch: {
loginSucceeded (loggedIn) { loginSucceeded (loggedIn) {
@ -152,6 +319,9 @@ export default {
loginError (error) { loginError (error) {
if (error) { if (error) {
showGlobalError(error) showGlobalError(error)
const isInvalidOTPError = this.loginError === 'Invalid OTP Code'
this.OTPError = isInvalidOTPError
this.otp = null
} }
} }
}, },
@ -164,12 +334,33 @@ export default {
methods: { methods: {
login () { login () {
this.$store.dispatch('user/login', { this.$store.dispatch('user/login', {
username: this.username,
password: this.password,
...(this.otp && { otp: this.otp }),
type: 'qr'
})
},
getOTPAsText () {
this.$store.dispatch('user/getOTPSecretAsText', {
username: this.username,
password: this.password
})
},
getOTPAsQrCode () {
this.$store.dispatch('user/getOTPSecret', {
username: this.username, username: this.username,
password: this.password password: this.password
}) })
}, },
showRetrievePasswordDialog () { showRetrievePasswordDialog () {
this.showDialog = true this.showDialog = true
},
focusOTP () {
this.OTPError = false
},
clearOTP () {
this.otp = null
this.OTPError = false
} }
} }
} }
@ -179,4 +370,10 @@ export default {
#csc-login-card #csc-login-card
margin: 0 margin: 0
margin-top: $header-height * -2 margin-top: $header-height * -2
.qr-code
width: 200px
height: 200px
.app-badge
height: 50px
object-fit: contain
</style> </style>

@ -3,7 +3,7 @@ import { i18n } from 'boot/i18n'
import _ from 'lodash' import _ from 'lodash'
import QRCode from 'qrcode' import QRCode from 'qrcode'
import { date } from 'quasar' import { date } from 'quasar'
import { apiDownloadFile, httpApi } from 'src/api/common' import { apiDownloadFile, apiGet, httpApi } from 'src/api/common'
import { callInitialize } from 'src/api/ngcp-call' import { callInitialize } from 'src/api/ngcp-call'
import { import {
changePassword, changePassword,
@ -41,6 +41,7 @@ import {
} from 'src/auth' } from 'src/auth'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants' import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { getSipInstanceId } from 'src/helpers/call-utils' import { getSipInstanceId } from 'src/helpers/call-utils'
import { parseBlobToObject } from 'src/helpers/parse-blob-to-object'
import { qrPayload } from 'src/helpers/qr' import { qrPayload } from 'src/helpers/qr'
import { PATH_CHANGE_PASSWORD } from 'src/router/routes' import { PATH_CHANGE_PASSWORD } from 'src/router/routes'
import { setLocal } from 'src/storage' import { setLocal } from 'src/storage'
@ -61,6 +62,8 @@ export default {
loginRequesting: false, loginRequesting: false,
loginSucceeded: false, loginSucceeded: false,
loginError: null, loginError: null,
loginWaitingOTPCode: false,
OTPSecret: null,
userDataRequesting: false, userDataRequesting: false,
userDataSucceeded: false, userDataSucceeded: false,
userDataError: null, userDataError: null,
@ -135,6 +138,12 @@ export default {
loginError (state) { loginError (state) {
return state.loginError return state.loginError
}, },
loginWaitingOTPCode (state) {
return state.loginWaitingOTPCode
},
OTPSecret (state) {
return state.OTPSecret
},
passwordRequirements (state) { passwordRequirements (state) {
return state.platformInfo?.security?.password || [] return state.platformInfo?.security?.password || []
}, },
@ -227,6 +236,8 @@ export default {
state.loginRequesting = true state.loginRequesting = true
state.loginSucceeded = false state.loginSucceeded = false
state.loginError = null state.loginError = null
state.loginWaitingOTPCode = false
state.OTPSecret = null
}, },
loginSucceeded (state, options) { loginSucceeded (state, options) {
state.jwt = options.jwt state.jwt = options.jwt
@ -234,11 +245,15 @@ export default {
state.loginRequesting = false state.loginRequesting = false
state.loginSucceeded = true state.loginSucceeded = true
state.loginError = null state.loginError = null
state.loginWaitingOTPCode = false
state.OTPSecret = null
}, },
loginFailed (state, error) { loginFailed (state, error) {
state.loginRequesting = false state.loginRequesting = false
state.loginSucceeded = false state.loginSucceeded = false
state.loginError = error state.loginError = error
state.loginWaitingOTPCode = false
state.OTPSecret = null
}, },
userDataRequesting (state) { userDataRequesting (state) {
state.resellerBranding = null state.resellerBranding = null
@ -329,13 +344,27 @@ export default {
if (index > -1) { if (index > -1) {
state.subscriberPhonebook[index].shared = value state.subscriberPhonebook[index].shared = value
} }
},
loginWaitingForOTPCode (state) {
state.loginWaitingOTPCode = true
state.OTPSecret = null
state.loginRequesting = false
},
storeOTPSecret (state, payload) {
state.loginWaitingOTPCode = true
state.OTPSecret = payload
state.loginRequesting = false
} }
}, },
actions: { actions: {
async login (context, options) { async login (context, options) {
context.commit('loginRequesting') context.commit('loginRequesting')
try { try {
const result = await login(options.username, options.password) const result = await login({
username: options.username,
password: options.password,
...(options.otp && { otp: options.otp })
})
setJwt(result.jwt) setJwt(result.jwt)
setSubscriberId(result.subscriberId) setSubscriberId(result.subscriberId)
context.commit('loginSucceeded', { context.commit('loginSucceeded', {
@ -345,6 +374,16 @@ export default {
await context.dispatch('initUser') await context.dispatch('initUser')
await this.$router.push({ name: 'dashboard' }) await this.$router.push({ name: 'dashboard' })
} catch (err) { } catch (err) {
if (err.message === 'Invalid OTP') {
if (context.state.loginWaitingOTPCode) {
context.commit('loginFailed', i18n.global.t('Invalid OTP Code'))
throw err
}
return context.dispatch('getOTPSecret', {
username: options.username,
password: options.password
})
}
context.commit('loginFailed', err.message) context.commit('loginFailed', err.message)
if (err.message === 'Password expired') { if (err.message === 'Password expired') {
this.$router.push({ path: PATH_CHANGE_PASSWORD }) this.$router.push({ path: PATH_CHANGE_PASSWORD })
@ -357,6 +396,64 @@ export default {
deleteJwt() deleteJwt()
document.location.href = document.location.pathname document.location.href = document.location.pathname
}, },
async getOTPSecret ({ commit }, options) {
try {
const token = `${options.username}:${options.password}`
const encodedToken = btoa(token).toString('base64')
const headers = {
Authorization: `Basic ${encodedToken}`,
'Cache-Control': 'no-cache',
Accept: 'image/png'
}
const res = await apiGet(
{
path: 'api/otpsecret',
config: {
responseType: 'blob',
headers
}
})
const url = URL.createObjectURL(res.data)
commit('storeOTPSecret', { type: 'qr', data: url })
} catch (err) {
try {
const errorData = await parseBlobToObject(err.response.data)
if ([400].includes(errorData?.code) && ['no OTP'].includes(errorData?.message)) {
return commit('loginWaitingForOTPCode')
}
} catch (err) {
commit('loginFailed', i18n.global.t('Unexpected error'))
throw err
}
}
},
async getOTPSecretAsText ({ commit }, options) {
try {
const token = `${options.username}:${options.password}`
const encodedToken = btoa(token).toString('base64')
const headers = {
Authorization: `Basic ${encodedToken}`,
'Cache-Control': 'no-cache',
Accept: 'text/plain'
}
const res = await apiGet(
{
path: 'api/otpsecret',
config: { headers }
})
commit('storeOTPSecret', { type: 'text', data: res.data })
} catch (err) {
try {
const errorData = await parseBlobToObject(err.response.data)
if ([400].includes(errorData?.code) && ['no OTP'].includes(errorData?.message)) {
return commit('loginWaitingForOTPCode')
}
} catch (err) {
commit('loginFailed', i18n.global.t('Unexpected error'))
throw err
}
}
},
async initUser (context) { async initUser (context) {
if (!context.getters.userDataSucceeded) { if (!context.getters.userDataSucceeded) {
try { try {

Loading…
Cancel
Save