Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): allow to invalidate sessions on password change #1747

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/bp/src/core/security/strategy-basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ export class StrategyBasic {
router.post(
'/login/basic/:strategy',
this.asyncMiddleware(async (req: Request, res: Response) => {
const { password, newPassword, channel, target } = req.body
const { password, newPassword, channel, target, invalidateActiveSessions } = req.body
const email = req.body.email.toLowerCase()
const { strategy } = req.params

// Random delay to prevent an attacker from determining if an account exists by the response time. Arbitrary numbers
await Promise.delay(_.random(15, 80))

await this._login(email, password, strategy, newPassword, req.ip)
await this._login(email, password, strategy, newPassword, req.ip, invalidateActiveSessions)
let token: TokenResponse

// If the channel & target is set, we consider that it's a chat user logging in (even if it's with admin credentials)
Expand Down Expand Up @@ -94,7 +94,8 @@ export class StrategyBasic {
password: string,
strategy: string,
newPassword?: string,
ipAddress: string = ''
ipAddress: string = '',
invalidateToken: boolean = true
): Promise<void> {
await this._checkUserAuth(email, strategy, password, newPassword, ipAddress)
const strategyOptions = _.get(await this.authService.getStrategy(strategy), 'options') as AuthStrategyBasic
Expand All @@ -107,6 +108,10 @@ export class StrategyBasic {
this._validatePassword(newPassword, strategyOptions)
const hash = saltHashPassword(newPassword)

if (invalidateToken) {
await this.authService.incrementTokenVersion(email, strategy)
}

await this.authService.updateUser(email, strategy, {
password: hash.hash,
salt: hash.salt,
Expand Down
1 change: 1 addition & 0 deletions packages/ui-admin/src/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"hideToken": "Hide token",
"changeYourPassword": "Change your password",
"confirmPassword": "Confirm Password",
"invalidateActiveSessions": "Invalidate Active Sessions",
"createAccount": "Create Account",
"createMasterAdminAccount": "This is the first time you run Botpress. Please create the master admin account.",
"currentPassword": "Current password",
Expand Down
1 change: 1 addition & 0 deletions packages/ui-admin/src/app/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"hideToken": "Ocultar token",
"changeYourPassword": "Cambiar contraseña",
"confirmPassword": "Confirmar contraseña",
"invalidateActiveSessions": "Invalidar sesiones activas",
"createAccount": "Crear una cuenta",
"createMasterAdminAccount": "Esta es la primera vez que ejecuta Botpress. Cree la cuenta de administrador principal.",
"currentPassword": "Contraseña actual",
Expand Down
1 change: 1 addition & 0 deletions packages/ui-admin/src/app/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"hideToken": "Cacher le jeton",
"changeYourPassword": "Changez votre mot de passe",
"confirmPassword": "Confirmez le mot de passe",
"invalidateActiveSessions": "Invalider les sessions actives",
"createAccount": "Créer un compte",
"createMasterAdminAccount": "C'est la première fois que vous exécutez Botpress. Veuillez créer le compte administrateur principal.",
"currentPassword": "Mot de passe actuel",
Expand Down
25 changes: 23 additions & 2 deletions packages/ui-admin/src/user/UpdatePassword.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Classes, Dialog, FormGroup, InputGroup, Intent } from '@blueprintjs/core'
import { Button, Classes, Dialog, FormGroup, InputGroup, Intent, Checkbox } from '@blueprintjs/core'
import { lang, toast, auth } from 'botpress/shared'
import { UserProfile } from 'common/typings'
import React, { FC, useState } from 'react'
Expand All @@ -15,6 +15,7 @@ const UpdatePassword: FC<Props> = props => {
const [password, setPassword] = useState<string>('')
const [newPassword, setNewPassword] = useState<string>('')
const [confirmPassword, setConfirmPassword] = useState<string>('')
const [invalidateActiveSessions, setInvalidateActiveSessions] = useState<boolean>(false)

const submit = async event => {
event.preventDefault()
Expand All @@ -23,7 +24,16 @@ const UpdatePassword: FC<Props> = props => {
const client = api.getSecured()

try {
await client.post(`/admin/auth/login/${strategyType}/${strategy}`, { email, password, newPassword })
const { data } = await client.post(`/admin/auth/login/${strategyType}/${strategy}`, {
email,
password,
newPassword,
invalidateActiveSessions
})

if (invalidateActiveSessions) {
auth.setToken(data.payload)
}

props.toggle()
toast.success(lang.tr('admin.passwordUpdatedSuccessfully'))
Expand Down Expand Up @@ -91,6 +101,17 @@ const UpdatePassword: FC<Props> = props => {
/>
</FormGroup>
<PasswordStrengthMeter pwdCandidate={newPassword} />

<FormGroup>
<Checkbox
checked={invalidateActiveSessions}
onChange={() => {
setInvalidateActiveSessions(!invalidateActiveSessions)
}}
>
{lang.tr('admin.invalidateActiveSessions')}
</Checkbox>
</FormGroup>
</div>

<div className={Classes.DIALOG_FOOTER}>
Expand Down
Loading