MaryOverflow
MaryOverflow

Reputation: 11

How to mock authenticated user in Micronaut?

I'm trying to write test for controller method which requires authenticated user, this how it looks:

@Controller("/api/task")
class TaskController(
    private val taskCompletionService: TaskCompletionService,
    private val userSecurityService: UserSecurityService
) {

    @Post("/{id}")
    suspend fun completeTask(
        @PathVariable id: Long,
        @Body request: TaskCompletionRequestDTO
    ) {
        taskCompletionService.completeTask(
            taskId = id,
            userId = userSecurityService.getUserId(),
            action = request.action
        )
    }
}

I'm struggling with mocking UserSecurityService

@Singleton
class UserSecurityService {

    fun getUserId(): Int = getUserIdOrNull() ?: throw UserIdNotFoundException("UserId not found")
    fun getUserIdOrNull(): Int? = getPrincipal()?.attributes?.get(PrincipalAttribute.USER_ID)?.toString()?.toInt()

    @OptIn(ExperimentalStdlibApi::class)
    private fun getPrincipal(): Authentication? =
        (ServerRequestContext.currentRequest<Any?>().get().userPrincipal.getOrNull()) as? Authentication

    @OptIn(ExperimentalStdlibApi::class)
    fun isAuthorized(): Boolean {
        val principal =
            ServerRequestContext.currentRequest<HttpRequest<*>>()
                .getOrNull()
                ?.attributes?.get(HttpAttributes.PRINCIPAL.toString(), Authentication::class.java)

        return principal != null && !principal.isEmpty
    }
}
  1. I was tried to use @MockBean annotation, but it says that I cannot use AOP on final class
  2. I was tried to use @Replaces annotation
 @Replaces(UserSecurityService::class)
    @Bean
    fun userSecurityService(): UserSecurityService = mock()

and stubbing

whenever(userSecurityService().getUserId()).thenReturn(123)

, but somehow getUserId() always returns 0, so I assume that kind of stubbing does not work (

The situation is complicated by the fact that I have several HTTP filters that need to access data about the authenticated user (through UserSecurityService). In the real application, I use a JWT token, but in the test I'm not able to use the production token

micronaut:
  security:
    token:
      jwt:
        claims-validators:
          issuer: https://mysecretproject.com/auth
        signatures:
          secret:
            generator:
              secret: ${SECURITY_TOKEN}

Ideally, I would like to use something similar to @WithMockUser in Spring, where I can mock a user authentication without needing a real JWT token. Is there a way in Micronaut to mock the JWT authentication or bypass the token verification in tests?

Thanks in advance for your help!

Upvotes: 0

Views: 47

Answers (1)

ibrabeicker
ibrabeicker

Reputation: 1839

Not my repository but I successfully implemented that by following this:

https://github.com/lstoeferle/micronaut-jwt-auth-mock

Basically you replace TokenAuthenticationFetcher with your own, and can hard code the authentication with a user and given roles.

Upvotes: 0

Related Questions