eugenedrvnk
eugenedrvnk

Reputation: 493

How to handle circular dependency between User and Auth modules?

I have a circular dependency in my app between UserModule and AuthModule.

AuthModule contains AuthGuard class which uses UserService (UserService.findOne) to check user existence and in positive case - let him through. AuthModule also UserService for the user creation process.

At the same time UserModule imports AuthModule as for UserController we need to use AuthGuard to prevent the user from accessing routes that are not intended for him.

I understand that it's a kinda classic problem, but unfortunately, I couldn't find any well-reasoned solutions for this.

Which approach is the most suitable in this case? Because as per my understanding, "forwardRef is definitely not the best way of handling this problem.

UserModule: 
  class UserService {
    isEligible = async (userId: string) => {
      // ... check if the user is existing
      // ... check if user is not banned
    }
    
    create = async (data) => { ... }
  }
  
  @UseGuards(AuthGuard)
  class UserController {
    // ... guard should be applied for all user routes
  }
 
// ------------------------------------------------ 

AuthModule:
  class AuthGuard {
    constructor(private readonly userService: UserService) [} 
  
    check = async (request): Promise<boolean> => {
      return this.userService.isEligible(request.body.userId);
    }
  }
  
  class AuthService {
    authenticate = async (data) => {
      await this.userService.create(data);
      // ... this method depending on "data" creates new user
      // or return data of existing
    }
  }

Upvotes: 2

Views: 437

Answers (2)

Guru Stron
Guru Stron

Reputation: 142923

At the same time UserModule imports AuthModule as for UserController we need to use AuthGuard to prevent the user from accessing routes that are not intended for him.

To be honest I don't understand the reasoning here. Without seeing actual code it is hard to tell, but usual approach would be to inject both AuthModule and UserModule into UserController and remove the dependency of UserModule on AuthModule (and move appropriate logic to the AuthModule).

Upvotes: 0

idanz
idanz

Reputation: 877

I'd like to suggest another approach.

It seems that both services need to do some 'Auth', but it's not the same 'Auth' :) The AuthModule does Authentication while the UserService deals with Authorization. From Okta: Authentication confirms that users are who they say they are. Authorization gives those users permission to access a resource .

So, how about the AuthModule calls the UserService in order to authenticate, and returns some access token as a response which represents the permissions of this user. Then - the user can call the UserService with this access token as a header, and authorization is done within the UserService with this token, such that no need to call AuthModule.

With this approach, the AuthModule is called only once at the beginning by the user, and then calls to UserService (by the user as well) are done with the access token which embodies his/her permissions. I believe this will also cause performance improvement since no need to call the authentication service every time.

Upvotes: 0

Related Questions