authentication is a nice topic. There are different ways to look at authentication.
Do you consider authentication a state change? If you do, it should be a command. If you don’t, implement is as a query. A successful result in the query would set the user’s session to “authenticated as user x”.
In my own project, authentication is considered a state change. There is a User aggregate that has a method authenticate(Password). In our case, it returns UserDetails (and applies an event) or throws an exception. The return value is returned to the component submitting the command via a callback. If successful, the user ID is set in the session. The user aggregate is found by querying the user ID for the username provided by the user and loading that ID from the repository. In Axon 0.7 you can use anything as an identifier. You could use the username as the User aggregate identifier, making it simpler to search for it. If you do that, you’ll even get the “username must be unique” guarantee for free. The event store will complain if you create another aggregate with the same identifier.
Note that throwing an exception on failed authentication (although I do it myself) probably isn’t the best solution. It’s better to return regularly, as you can also apply and publish events when authentication failed. This will allow you to block an account for some time when 3 failed attempts were received. In that case, it’s better to return a AuthenticationResult, which may either indicate a success (and provide user details) or a failure.
A third way to do it, and I haven’t tried it yet, is to respond purely on events instead of a return value. When a “UserSuccessfullyAuthenticatedEvent” is published, an EventHandler could find the session for that user and mark it as “authenticated by -whatever user-”. The problem I see here is that authentication is typically a blocking process. You want to wait for the result before doing anything else. So a request-response mechanism seems most appropriate here.
Hope this helps.