-
Notifications
You must be signed in to change notification settings - Fork 6k
spring-security-test @WithMockOidcuser gh-8459 #8461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks for the PR @nenaraab! When we did gh-7618, I think we avoided annotation support for OIDC because it was not very easy for users to customize the Given you find value in this, it is worth us reconsidering. However, I want to ensure that we remember all the concerns we had before we merge this. I'm assigning this to @jzheaux since he worked on the test support for some of the other OAuth bits and he might remember better what we discussed. As a heads up he is out on PTO so his response will be delayed. Thanks again for your PR! |
Hi @rwinch and @jzheaux, that's cool, thanks for initial feedback and reconsidering. In our context the usage of Btw, to make the configuration of Enjoy your day! |
Here is a link to some of our previous discussions around annotation based testing with more complex objects #6557 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR, @nenaraab! I agree that it'd be good to introduce simple annotation support without claims.
I've left some feedback inline.
* | ||
* @author Nena Raab | ||
* @see WithUserDetails | ||
* @since 5.3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go ahead and change this to 5.4
since that's the next minor release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, thanks for the detailed feedback!
** | ||
* @return | ||
*/ | ||
String[] authorities() default { "SCOPE_openid" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you already consider adding a scopes
attribute as well? Doing scopes={ "openid", "profile" }
would mirror roles
in @WithMockUser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mhhh...
-
I dislike mutually exclusive options, which would be the case when introducing scopes in addition to authorities.
Then we have to implement it similar to@WithMockUser
where scopes gets "ignored" in case authorities are defined. -
scopes only is not a good option, as I need the flexibility to add authorities without Spring's prefix.
-
authorities only might confuse the one or the other consumer, because he/she has to add "SCOPE_" prefix, which exposes Spring Security implementation detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, I don't have strong feelings about it, and we can always wait for a community member to request it.
The rest is just for future reference:
mutual exclusivity
Btw, while I see your point about mutual exclusivity, I don't see it as a problem as you do. The DSL allows oauth2ResourceServer().jwt()
OR oauth2ResourceServer().opaqueToken()
, for example.
scopes only
agreed
authorities only ... add "SCOPE_" prefix
The application only has to do this if it's using the "SCOPE_" prefix in its authorization rules, in which case the tester already knows about it. I don't see this as an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then - lets provide both :-)
* The password to be used. The default is "clientId". | ||
* @return | ||
*/ | ||
String clientId() default "clientId"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think let's leave out any additional claims for the time being.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, for sure!
final Instant issuedAt = new GregorianCalendar().toInstant().minusSeconds(3); | ||
|
||
OidcIdTokenFactory(String userId, String clientId, String userIdClaimName) { | ||
claims.put("client_id", clientId); // mandatory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify why this is mandatory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, its not required (any more) it can be removed. Thanks!
return context; | ||
} | ||
|
||
private class OidcIdTokenFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a separate class necessary? I'm wondering if you'd get the same outcome using methods.
|
||
List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); | ||
for (String authority : withUser.authorities()) { | ||
grantedAuthorities.add(new SimpleGrantedAuthority(authority)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To align this with the OidcUser
that OidcUserService
creates, I think an OidcUserAuthority
should also be added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing it does now: it adds another GrantedAuthority
with "ROLE_USER", not sure why required...
|
||
OidcIdTokenFactory(String userId, String clientId, String userIdClaimName) { | ||
claims.put("client_id", clientId); // mandatory | ||
claims.put("iat", issuedAt.getEpochSecond()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to maintain if these used the claim names defined in IdTokenClaimNames
.
|
||
private class OidcIdTokenFactory { | ||
private Map<String, Object> claims = new HashMap<>(); | ||
final Instant expiredAt = new GregorianCalendar().toInstant().plusSeconds(600); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason that you are using new GregorianCalendar().toInstant()
instead of Instant.now()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
factory.createSecurityContext(withUser); | ||
} | ||
|
||
public void valueDefaultsUserIdWhenUserNameIsNull() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you please re-arrange the method names so that they look like valueWhenUserNameIsNullThenDefaultsUserId
? In new classes, the team has standardized on methodNameWhenCircumstanceThenResult
.
* {@link TestExecutionEvent#TEST_METHOD} which occurs during | ||
* {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)} | ||
* @return the {@link TestExecutionEvent} to initialize before | ||
* @since 5.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder to remove this @since
, which probably came over from a copy-paste
thanks for the detailed and valuable review!
@jzheaux |
@nenaraab thanks so much for this contribution and for responding to my change requests. After letting this sit for some time now, I feel like we don't want to add claims to the annotation. Without claims, it provides no information that isn't already available in public String method(Authentication authentication) {
String name = authentication.getName();
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
} in which case tying the application to Given that, I'm going to close this, but please feel free to comment further if I've missed a use case of yours. |
Adds
@WithMockOidcUser
tospring-security-test
library that allows you to fill the SecurityContext properly when usingoauth2Login
as I was used to with@WithMockUser
Example Web Mvc test:
Example other Spring Tests:
Github issue
Find further details discussed here: ##8459