Hashing password in an Event Engine application

Hashing password in an Event Engine application

For a few projects we make use of the Event Engine by prooph software which is a biased - in a good way(tm) - way of quickly building event sourced applications. While building a user registration context, I was stuck a bit how and where to properly hash the password for the user. I could have done it in the init() method of the command, that way I would have made sure that the cleartext password is not "visible" anywhere in the application code. In the end, I decided to make use of a provider class. The return value of the provider will be passed to the specified aggregate method and allows me to pass it to the respective event.

First, I created a marker interface for the commands to make sure that only commands that implement the interface can be passed to the provider class:

interface HasUnhashedPassword
{
  /**
   * @return UnhashedPassword
   */
  public function password(): ?UnhashedPassword;
}


As you can see, the password() method returns an UnhashedPassword value object (or null in case no password was provided). Besides having an UnhashedPassword value object, I have also created a HashedPassword value object that gets returned by the provider class. Having a clear distinction between unhashed and hashed passwords makes sense and helps to communicate the intent of method parameters or return values better.

As the next step, a Password Hasher is needed which contains the logic to turn an unhashed password into a HashedPassword. The concrete implementation does not matter now, let's have a look at the interface:

interface PasswordHasher
{
  /** Encrypts the given password */
  public function createHash(UnhashedPassword $password): HashedPassword;

  /** Verifies the given plain text password against the hashed one */
  public function verify(UnhashedPassword $password, 
             HashedPassword $hashedPassword): bool;
}


Password hasher instances get injected into the HashedPasswordProvider - the provider class defined by the Event Engine:

final class HashedPasswordProvider
{
  /** @var PasswordHasher */
  private $passwordHasherer;

  public function __construct(PasswordHasher $passwordHasherer)
  {
      $this->passwordHasherer = $passwordHasherer;
  }

  public function __invoke(HasUnhashedPassword $command): HashedPassword
  {
      return HashedPassword::createFromUnhashedPasswordWithHasher(
             $command->password(), $this->passwordHasherer
      );
  }
}


To register the provider class add a provideContext() method call to the $eventEngine configuration and pass the class name of the HashedPasswordProvider implementation. The Event Engine will take care of executing the provider class and passing the return value to the aggregate:

$eventEngine->process(Command::ATTENDEE_REGISTER)
  ->withNew(self::ATTENDEE)
  ->identifiedBy(Payload::ATTENDEE_ID)
  ->provideContext(HashedPasswordProvider::class)
  ->handle([Attendee::class, 'register'])
  ->recordThat(Event::ATTENDEE_REGISTERED)
  ->apply([Attendee::class, 'whenRegistered'])
  ->storeStateIn(AttendeeResolver::COLLECTION);


The aggregate implementation looks like this:

final class Attendee
{
  public static function register(RegisterAttendee $command, 
        HashedPassword $hashedPassword): Generator
  {
      $attendee = $command->toArray();
      $attendee[Payload::ATTENDEE_PASSWORD] = $hashedPassword->toString();
      yield Registered::fromArray($attendee);
  }
}


This way the aggregate has access to the command object as well as the hashed password and can do whatever needs to be done.


Eintrag von Stephan Hochdörfer am 07.05.2020

Tags: PHP, Eventsourcing

Diese Webseite verwendet Cookies, um die Bedienfreundlichkeit zu erhöhen. Mit der Nutzung unserer Webseite wird das Einverständnis erklärt, dass wir Cookies verwenden. Weitere Informationen.