Clean Architecture and authentication. Correct way?
Asked Answered
C

2

24

I'm working on an Android app based on Clean Architecture pattern and I have doubts how to implement user authentication in a clean way. Is the following solution clean in terms of clean architecture?

I would create use cases like below (executed from presentation layer):

  • LoginUseCase (for provided login and password fetches api token via remote service and saves in local token source)
  • LogoutUseCase (clears token from LocalTokenSource)

(LocalTokenSource interface would be stored in domain layer and its implementation in data layer - kind of repository)

And in order to perform token refresh at each app launch (it's not a use case from the user perspective, right?) I would create SessionManager component in domain layer. SessionManager would be responsible for refreshing token and saving it in LocalTokenSource. Each time activity is started, from its presenter I would execute refreshToken() on injected SessionManager. What do you think about the solution?

If it's clean, then how to handle passing token to the remote service to execute other API methods which require token? Lets say I have PostsRepository which fetches posts data from remote service. Should I pass token from a use case to the repository method like repo.getPosts(token)? Or inject LocalTokenSource to the repository, so it could read the token on its own? Wouldn't the second option violate Clean Architecture rules, because LocalTokenSource would be used in 2 layers?

Chorea answered 14/2, 2019 at 16:53 Comment(0)
U
32

The central question you would have to decide is: Do you want to model authorization (and so the usage of the token) as an aspect of your business logic OR do you want to consider it as an "implementation detail".

If you decide for the first, having dedicated use cases for it, adding the SessionManager to the domain layer and passing the token to the repositories would be a consistent modeling.

If you decide for the later, login/logout/refresh as well as the existence of the token is probably best kept "behind the scenes", so in the framework or gateway layer.

Both approaches would follow the rules of the Clean Architecture (as long as you do not violate the dependency rule).

Urticaceous answered 23/2, 2019 at 13:36 Comment(1)
Thank you for opinion, now I can work with more confidence ;-)Chorea
C
3

Let's refer to the primary source, the book "Clean Architecture: A Craftsman’s Guide to Software Structure and Design".

First of all, let's establish what layers the clean architecture consists of. Here is the quote from the book:

The architect can employ the Single Responsibility Principle and the Common Closure Principle to separate those things that change for different reasons, and to collect those things that change for the same reasons—given the context of the intent of the system.

What changes for different reasons?

User interfaces change for reasons that have nothing to do with business rules. Business rules themselves may be closely tied to the application, or they may be more general. The database, the query language, and even the schema are technical details that have nothing to do with the business rules or the UI. Thus we find the system divided into decoupled at least four horizontal layers — the application-independent business rules, Application-specific business rules, UI, and the Database.

Later in the book Robert Martin describes in detail how to build these 4 corresponding layers:

  1. Enterprise Business Rules (Entities)
  2. Application Business Rules (Use Cases)
  3. Interface Adapters
  4. Frameworks & Drivers.

enter image description here

Now let's establish what is "Use Case" is. Here is another quote:

A use case is a description of the way an automated system is used. It specifies the input to be provided by the user, the output to be returned to the user, and the processing steps involved in producing that output.

Those elements will be classes or functions or modules that have prominent positions within the architecture, and they will have names that clearly describe their function.

From the use case, it is impossible to tell whether the application is delivered on the web, on a thick client, on a console or is a pure service.

Between the use case interactors and the database are the database gateways. These gateways are polymorphic interfaces that contain methods for every create, read, update, or delete operation that can be performed by the application on the database.

If the application needs to know the last names of all the users who logged in yesterday, then the UserGateway interface will have a method named getLastNamesOfUsersWhoLoggedInAfter that takes a Date as its argument and returns a list of last names.

Now back to your question.

Your description of the "SessionManager" sounds to me like an "AuthInterceptor". In my experience, I usually kept it as a part of the "data" component in the "Frameworks & Drivers" layer. Since it is in the "data" component, it is at the same level (circle) as database and web services. So theoretically there is no harm in accessing remote or local data sources directly (dao and rest client).

However, to be honest, when I did not have a decoupling layer, there were times when I used both and "gateway" and "use cases" in the "AuthInterceptor" (data component), which of probably a violation of the clean architecture, but I believe not as serious, because I still used these interfaces in outer circle, without braking the "boundaries" rule, that inner circle, should not know anything about outer circle.

Since Use Cases must have "input to be provided by the user, the output to be returned to the user", the "refreshToken" function will not be a part of this operation, because it does not involve the user at all, hence it will not be any of those two use cases that you mentioned, "LoginUseCase" and "LogoutUseCase".

Regarding your question on "LocalTokenSource", which you describe as a "repository", which by the Clean Architecture, would be correctly called "Gateway". Gateway interfaces are located in the "Interface Adapters" layer and their implementation is in the Frameworks & Drivers layer, so obviously, usage of the Gateway interface in two layers is not aligned with the clean architecture described in the book, but as I mentioned before, "Frameworks & Drivers" layer is an outermost circle, so it allowed to use anything that he knows about, which is the inner "Interface Adapters" layer.

If you understand the idea of the clean architecture described in the book and the references and experience that I provided, you will understand, that you do not need to violate anything, if you simply follow the basic structure and principles of Robert Martin's clean architecture.

Cloven answered 10/6, 2023 at 3:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.