ch4mpy / spring-addons

Ease spring OAuth2 resource-servers configuration and testing
Apache License 2.0
521 stars 84 forks source link

Add note on how to use with @SpringBootTest if non-mocked #147

Closed wimdeblauwe closed 11 months ago

wimdeblauwe commented 11 months ago

I had a hard time to figure out why my @SpringBootTest(webEnvironment = RANDOM_PORT) was not working as expected.

It turned out that com.c4_soft.springaddons.security.oauth2.test.webmvc.AddonsWebmvcTestConf was enabled by default. The fix was easy once I understood the problem, I just added an exclusion like this:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableAutoConfiguration(exclude = AddonsWebmvcTestConf.class)

Maybe you can take the answer you gave at https://github.com/ch4mpy/spring-addons/discussions/76 in the section "How do I run my integration tests in non mocked environment" and add it to the readme and also include that people should add this exclusion to make sure everything works properly.

ch4mpy commented 11 months ago

Or, maybe, this test configuration should not be automatically applied. Actually, when writing the 7.0.0, I intended the addons auto-configuration to be (explicitly) declarative in tests. Requalifying in bug.

You are running your test with a real authorization server in a container, issuing real tokens, and use a regular REST client to send requests authorized with this tokens and going through a real JWT decoder, right?

wimdeblauwe commented 11 months ago

Yeah, that would be even better.

Yes, I am using Keycloak in testcontainers via testcontainers-keycloak. I use REST Assured to do the requests.

Only drawback is that I have to use deprecated password grant, instead of the actual Authorization Code grant that things should be using. Well, maybe I don't have to, but I have not figured it out how to simulate how it works in Postman with opening a browser and handing the token back to Postman. But for the test itself, that does not matter that the password grant is used.

ch4mpy commented 11 months ago

@wimdeblauwe I removed the Spring Boot auto loading resource from spring-addons-starter-oidc-test at the same time I released 7.1.9 for bump to Boot 3.1.4.

Would you try with 4.1.9 if you can remove your exclusion?

Also, did you give a try to the recently added @WithJwt? It uses most of the runtime security beans to build the test security context from the JSON payload of JWTs. Only the decoding is skipped. I haven't run performance evaluation tests, but I believe integration tests with regular REST client & containers to be much slower than those with MockMvc (as done for instance there).

wimdeblauwe commented 11 months ago

Thanks, it works with 7.1.9 without the exclusion!

I saw the @WithJwt, but for this particular test (which tests that there is an error response if the payload size is too big), I needed to have the actual Tomcat running, it is not possible to use MockMvc.

About that @WithJwt: How does that work with time expiration? If I would enable validation of the expiry time (not sure if that is enabled by default), wouldn't that make it impossible to use this as all the tokens will be expired? If not, how would I use them to test expiry?

ch4mpy commented 11 months ago

The expiry as well as token signature are always checked by the JWT decoder.

Issuer and audience are optionally checked inside the JWT decoder.

With @WithJwt, the process starts just after the token decoding, before the authentication manager and converter. So no, neither expiry, signature, audience or issuer will be checked. But the actual type of Authentication as well as authorities mapping (and other claims if anything special is done during JWT to authentication conversion), happens as normal, as opposed to the former annotations where the authentication implementation was forced and authorities were declared (and could be incoherent with the claims).

I'm closing since your problem with test containers is solved. Thank you for reporting and testing. Do not hesitate to open a discussion or another issue if usage of the new annotations is not clear.

ch4mpy commented 10 months ago

@wimdeblauwe just thinking of it, but instead of the password flow in your test, you could consider using client_credentials.

If you want to emulate a user login with authorization_code flow, then maybe should you consider using protractor or another framework designed to emulate user interaction with browser, mouse and keyboard.

But as a resource server just doesn't care about the flow used to get a token, client_credentials seems the most adapted.

wimdeblauwe commented 10 months ago

But with client_credentials, you log on as an application, not as a user like with authorization_code or password flows.

ch4mpy commented 10 months ago

Who cares? The flow used is a concern only for the OAuth2 client and the OAuth2 authorization server. As I just wrote, from the resource server point of view an access token is an access token, and all that matters is if this token is valid (and if it should grant access to the resource based on the claims in the token). How the token was obtained (client_credentials, authorization_code, refresh_token, etc. is of no importance, and actually, the resource server has no easy way to know).

Also, your unit tests are an application (not a real user)...

wimdeblauwe commented 10 months ago

True. I would just need to check to give that client the roles the user would have.