Search code examples
securityspring-securityarchitectureentitydto

How to differentiate between a user as an authentication object and a user as an application object?


I need some advice regarding the architecture of an application with some social media functions.

Up ahead, I've searched the internet, but haven't found any helpful. Maybe there are some good examples somewhere in the literature on software architecture, and if so, I would be very happy to hear about them.

The goal is to have an application that has one single entity for users with an associated repository. However, there are two fundamentally different scenarios in which a user is requested from the repository:

  1. The security component of the application must fetch the user as authentication data object during the authentication process. In this case, only email (and or username) and password and roles are required.
  2. An application component requests users as data objects for the purpose of further user information like favorite books, hobbies or cake recipes. In this case, for example, it is not necessary to pass around passwords and other sensitive data.

So my idea would be to implement two different DTOs, an AuthUserDto for the authentication process and a standard UserDto for everything else. This would logically also imply to implement two Services, an AuthUserService and a normal UserService. And to follow the rules of software architecture I know, we would implement two corresponding service interfaces.

And last but not least, the application should also get a UserContextService. To stay true to the naming convention I have used so far, this context service should be called AuthUserContextService, since it is responsible for authentication issues like getCurrentUser() or setCurrentUser().

So in the end the application structure would look something like this:

Application.java
│
├───dto
│   │
│   ├───mapper
│   │       RoleMapper.java
│   │       UserMapper.java
│   │
│   └───model
│           AuthUserDto.java
│           RoleDto.java
│           UserDto.java
│  
├───entity
│       Role.java
│       User.java
│  
├───repository
│       RoleRepository.java
│       UserRepository.java
│
├───service
│   │   DefaultAuthUserContextService.java
│   │   DefaultAuthUserService.java
│   │   DefaultUserService.java
│   │
│   └───interfaces
│           AuthUserContextService.java
│           AuthUserService.java
│           UserService.java
  • When the security component requests a user from the DefaultAuthUserService, an AuthUserDto is delivered.
  • When the security component wants to get or set a user with the help of the DefaultAuthUserContextService, we're dealing with an AuthUserDto.
  • When a social media component requests a user from the DefaultUserService, a UserDto is delivered.

What I am asking now:

Is this approach ok, is there a best practice guide for this or is there a completely different and better way to handle the user entity in a usecase specific way?

Many thanks for any help, links and hints to accomplish a decent base architecture.


Solution

  • This solution looks good to me overall.

    There are still some challenging parts left:

    1. ACLs/roles: make sure you separate Authentication and Authorization, and store authentication credentials (login/email/password) in a dedicated table. It'll make your life way easier if you decide to add login with social networks in the future (you'll add a new authentication table and update the authentication code. The authorization code will stay the same). Authentication vs Authorization.
    2. Sessions: when your users logged in using username/email/password, you'll have to "remember" them by storing some "session" token in their cookies. There are many libraries and solutions for this problem. My favourite approach is using JSON Web Tokens. If used wisely, they can improve app performance significantly and reduce session management pain (especially when you have multiple backends/databases in different regions).
    3. Storing passwords: simple yet complicated part. The best practice is to store them using a strong hashing algorithm designed for it (like Bcrypt or Argon2id). Each password should have an individual salt. PHP has a special function which takes care of everything, Java should have something similar.

    It's all I remember for now. :) Feel free to ping me in comments if you need more details about anything :)

    PS the article for Authentication vs. Authorization is quite long, I'll copy the main idea here:

    The best way to illustrate the differences between the two terms is with a simple example. Let's say you decide to go and visit a friend's home. On arrival, you knock on the door, and your friend opens it. She recognizes you (authentication) and greets you. As your friend has authenticated you, she is now comfortable letting you into her home. However, based on your relationship, there are certain things you can do and others you cannot (authorization). For example, you may enter the kitchen area, but you cannot go into her private office. In other words, you have the authorization to enter the kitchen, but access to her private office is prohibited.