Webatrice: support hashed passwords in register and resetPassword (#4549)

* support hashed passwords in register and resetPassword

* lint

* support hashedPasswords for accountActivation

* use salt in post-register login step

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
Jeremy Letto 2022-01-30 22:09:16 -06:00 committed by GitHub
parent 92f941a54c
commit 992e28797f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 15 deletions

View file

@ -139,7 +139,29 @@ export class SessionCommands {
switch (raw.responseCode) { switch (raw.responseCode) {
case webClient.protobuf.controller.Response.ResponseCode.RespOk: { case webClient.protobuf.controller.Response.ResponseCode.RespOk: {
const passwordSalt = raw['.Response_PasswordSalt.ext']?.passwordSalt; const passwordSalt = raw['.Response_PasswordSalt.ext']?.passwordSalt;
SessionCommands.login(passwordSalt);
switch (webClient.options.reason) {
case WebSocketConnectReason.REGISTER: {
SessionCommands.register(passwordSalt);
break;
}
case WebSocketConnectReason.ACTIVATE_ACCOUNT: {
SessionCommands.activateAccount(passwordSalt);
break;
}
case WebSocketConnectReason.PASSWORD_RESET: {
SessionCommands.resetPassword(passwordSalt);
break;
}
case WebSocketConnectReason.LOGIN:
default: {
SessionCommands.login(passwordSalt);
}
}
break; break;
} }
case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationRequired: { case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationRequired: {
@ -155,19 +177,24 @@ export class SessionCommands {
}); });
} }
static register(): void { static register(passwordSalt?: string): void {
const { userName, password, email, country, realName } = webClient.options as unknown as ServerRegisterParams; const { userName, password, email, country, realName } = webClient.options as unknown as ServerRegisterParams;
const registerConfig = { const registerConfig: any = {
...webClient.clientConfig, ...webClient.clientConfig,
clientid: 'webatrice', clientid: 'webatrice',
userName, userName,
password,
email, email,
country, country,
realName, realName,
}; };
if (passwordSalt) {
registerConfig.hashedPassword = hashPassword(passwordSalt, password);
} else {
registerConfig.password = password;
}
const CmdRegister = webClient.protobuf.controller.Command_Register.create(registerConfig); const CmdRegister = webClient.protobuf.controller.Command_Register.create(registerConfig);
const sc = webClient.protobuf.controller.SessionCommand.create({ const sc = webClient.protobuf.controller.SessionCommand.create({
@ -176,7 +203,7 @@ export class SessionCommands {
webClient.protobuf.sendSessionCommand(sc, raw => { webClient.protobuf.sendSessionCommand(sc, raw => {
if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAccepted) { if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAccepted) {
SessionCommands.login(); SessionCommands.login(passwordSalt);
return; return;
} }
@ -219,7 +246,7 @@ export class SessionCommands {
}); });
}; };
static activateAccount(): void { static activateAccount(passwordSalt?: string): void {
const { userName, token } = webClient.options as unknown as AccountActivationParams; const { userName, token } = webClient.options as unknown as AccountActivationParams;
const accountActivationConfig = { const accountActivationConfig = {
@ -238,7 +265,7 @@ export class SessionCommands {
webClient.protobuf.sendSessionCommand(sc, raw => { webClient.protobuf.sendSessionCommand(sc, raw => {
if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespActivationAccepted) { if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespActivationAccepted) {
SessionPersistence.accountActivationSuccess(); SessionPersistence.accountActivationSuccess();
SessionCommands.login(); SessionCommands.login(passwordSalt);
} else { } else {
SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed'); SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed');
SessionCommands.disconnect(); SessionCommands.disconnect();
@ -311,17 +338,22 @@ export class SessionCommands {
}); });
} }
static resetPassword(): void { static resetPassword(passwordSalt?: string): void {
const { userName, token, newPassword } = webClient.options as unknown as ForgotPasswordResetParams; const { userName, token, newPassword } = webClient.options as unknown as ForgotPasswordResetParams;
const forgotPasswordResetConfig = { const forgotPasswordResetConfig: any = {
...webClient.clientConfig, ...webClient.clientConfig,
clientid: 'webatrice', clientid: 'webatrice',
userName, userName,
token, token,
newPassword,
}; };
if (passwordSalt) {
forgotPasswordResetConfig.hashedNewPassword = hashPassword(passwordSalt, newPassword);
} else {
forgotPasswordResetConfig.newPassword = newPassword;
}
const CmdForgotPasswordReset = webClient.protobuf.controller.Command_ForgotPasswordReset.create(forgotPasswordResetConfig); const CmdForgotPasswordReset = webClient.protobuf.controller.Command_ForgotPasswordReset.create(forgotPasswordResetConfig);
const sc = webClient.protobuf.controller.SessionCommand.create({ const sc = webClient.protobuf.controller.SessionCommand.create({

View file

@ -3,6 +3,7 @@ import { Room, StatusEnum, User, WebSocketConnectReason } from 'types';
import { SessionCommands } from '../commands'; import { SessionCommands } from '../commands';
import { RoomPersistence, SessionPersistence } from '../persistence'; import { RoomPersistence, SessionPersistence } from '../persistence';
import { ProtobufEvents } from '../services/ProtobufService'; import { ProtobufEvents } from '../services/ProtobufService';
import { passwordSaltSupported } from '../utils';
import webClient from '../WebClient'; import webClient from '../WebClient';
export const SessionEvents: ProtobufEvents = { export const SessionEvents: ProtobufEvents = {
@ -122,18 +123,25 @@ function serverIdentification(info: ServerIdentificationData) {
switch (webClient.options.reason) { switch (webClient.options.reason) {
case WebSocketConnectReason.LOGIN: case WebSocketConnectReason.LOGIN:
SessionCommands.updateStatus(StatusEnum.LOGGING_IN, 'Logging In...'); SessionCommands.updateStatus(StatusEnum.LOGGING_IN, 'Logging In...');
// Intentional use of Bitwise operator b/c of how Servatrice Enums work if (passwordSaltSupported(serverOptions, webClient)) {
if (serverOptions & webClient.protobuf.controller.Event_ServerIdentification.ServerOptions.SupportsPasswordHash) {
SessionCommands.requestPasswordSalt(); SessionCommands.requestPasswordSalt();
} else { } else {
SessionCommands.login(); SessionCommands.login();
} }
break; break;
case WebSocketConnectReason.REGISTER: case WebSocketConnectReason.REGISTER:
SessionCommands.register(); if (passwordSaltSupported(serverOptions, webClient)) {
SessionCommands.requestPasswordSalt();
} else {
SessionCommands.register();
}
break; break;
case WebSocketConnectReason.ACTIVATE_ACCOUNT: case WebSocketConnectReason.ACTIVATE_ACCOUNT:
SessionCommands.activateAccount(); if (passwordSaltSupported(serverOptions, webClient)) {
SessionCommands.requestPasswordSalt();
} else {
SessionCommands.activateAccount();
}
break; break;
case WebSocketConnectReason.PASSWORD_RESET_REQUEST: case WebSocketConnectReason.PASSWORD_RESET_REQUEST:
SessionCommands.resetPasswordRequest(); SessionCommands.resetPasswordRequest();
@ -142,7 +150,11 @@ function serverIdentification(info: ServerIdentificationData) {
SessionCommands.resetPasswordChallenge(); SessionCommands.resetPasswordChallenge();
break; break;
case WebSocketConnectReason.PASSWORD_RESET: case WebSocketConnectReason.PASSWORD_RESET:
SessionCommands.resetPassword(); if (passwordSaltSupported(serverOptions, webClient)) {
SessionCommands.requestPasswordSalt();
} else {
SessionCommands.resetPassword();
}
break; break;
default: default:
SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Reason: ' + webClient.options.reason); SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Reason: ' + webClient.options.reason);

View file

@ -24,3 +24,8 @@ export const generateSalt = (): string => {
return salt; return salt;
} }
export const passwordSaltSupported = (serverOptions, webClient): number => {
// Intentional use of Bitwise operator b/c of how Servatrice Enums work
return serverOptions & webClient.protobuf.controller.Event_ServerIdentification.ServerOptions.SupportsPasswordHash;
}