How to get proper user from SecurityContextHolder Authentication within integration test using rest assured? - spring-boot

I cannot successfully get the right username within integration test using Rest assured. I do not understand why but Authentication from SecurityContextHolder returns "anonymousUser" instead of "myuser". What is wrong with my config?
Response body doesn't match expectation. Expected: is "Authentication
successful for user myuser" Actual: Authentication successful for
user anonymousUser
Controller:
#RestController
#RequestMapping(value = "/api/ping", produces = TEXT_PLAIN_VALUE)
public class PingController {
#GetMapping
public #ResponseBody String ping() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "Authentication successful for user " + authentication.getName();
}
}
Security config:
http.authorizeRequests()
.antMatchers("/api/ping").permitAll()
.antMatchers("/api/**").hasRole(ROLE_API)
//...another roles...
.anyRequest().permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(basicRestAuthenticationEntryPoint)
.and()
.headers().frameOptions().disable()
.and().cors()
.and()
.csrf().disable();
Test:
#Test
void test() {
given()
.contentType(TEXT)
.auth().basic("myuser", "password").
when()
.get("/api/ping").
then()
.statusCode(200)
.body(is("Authentication successful for user: myuser"));
}

Related

How do I redirect to a specific uri after Google oauth using Spring Boot

I'm implementing a server using Spring Boot. After the user do an oauth login, I want the user to go redirect to a specific uri so I can let the user register or login. The Google OAuth login seems like it is working fine but it keeps going to "/" uri. I want to user to be redirected to "/api/v1/member/oauth"
This is my Spring Security setup.
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs")
.permitAll()
.anyRequest()
.permitAll()
.and()
.oauth2Login()
.defaultSuccessUrl("/api/v1/member/oauth")
.userInfoEndpoint()
.userService(customOAuth2MemberService);
}
...
This is the OAuth service that a user is directed to. (This works fine)
#Service
#RequiredArgsConstructor
public class CustomOAuth2MemberService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User;
try {
oAuth2User = delegate.loadUser(userRequest);
} catch (OAuth2AuthenticationException e) {
throw new CustomException(OAUTH_FAIL);
}
return new DefaultOAuth2User(oAuth2User.getAuthorities(), oAuth2User.getAttributes(), "sub");
}
}
I want to get the DefaultOAuth2User which is returned from the above to this uri.
#PostMapping("/api/v1/member/oauth")
public Object registerOrLogin(DefaultOAuth2User defaultOAuth2user) {
return ResponseEntity.status(200)
.body(DefaultResponseDto.builder()
.responseCode("MEMBER_LOGIN")
.build());
}
It currently is not going to this uri and is redirected to "/".
NEW: I redirected it by having .defaultSuccessUrl() but now the DefaultOAuth2User is not sent with the redirection, causing the parameter of redirected api to be null. How do I fix this problem?
Try to use
.oauth2Login()
.defaultSuccessUrl("/api/v1/member/oauth")
this should override post-authentication behavior and redirect to the desired page after successful login. Also, there is a similar method for setting redirection URL for failed authentication .failureUrl("url").
Spring-Security AbstractAuthenticationProcessingFilter class has successfulAuthentication() methos, which defines what happens when a User is successfully authenticated. You can register your success handler and put your redirect logic there.
But here is a catch, when using OAuth2.0, we need to specify redirect-uri to which user will be landed after client receives an access-token.
If you are okay with this Oauth's redirect-uri, do not alter the redirect in success handler or if you need to redirect irrespective of that, use response.sendRedirect("/social-login-sample/some-page");
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs")
.permitAll()
.anyRequest()
.permitAll()
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2MemberService)
.and()
.successHandler(
new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
// authentication.getName() : Principal Name
CustomOAuth2User oauthUser = (CustomOAuth2User) authentication.getPrincipal();
// Check if user is registered in your Database, if not, register new user
//userService.processAuthenticatedUser(oauthUser.getEmail());
// Get actual redirect-uri set in OAuth-Provider(Google, Facebook)
String redirectUri =
UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replaceQuery(null)
.build()
.toUriString();
log.info("redirectUri: {}", redirectUri);
// Ignore redirect-uri, and send user to a different page instead...
// response.sendRedirect("/social-login-sample/some-ther-page");
}
})
}

Spring receive login credentials in Post endpoint via #RequestBody and return JSESSIONID

I'm creating a Spring JPA application with Spring Security with Basic Authentication for a project.
I want to send login credentials of a user in body, then return JSESSIONID back to user
This is my endpoint that returns a default api endpoint for specific logged in user.
#GetMapping(value = "/success" ,produces = MediaType.APPLICATION_JSON_VALUE)
public java.util.Map<String, String> index() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken))
return Collections.singletonMap("href" ,loginSuccessHandler(((MyUserPrincipal)auth.getPrincipal()).getUser().getId()));
return Collections.singletonMap("href" ,"/login/error");
}
I have attempted to make custom login with following endpoint (idea is to have a single hardcodable "/success" endpoint after login to give the actual user specific endpoint to frontend).
#RequestMapping(value = "/login", method = RequestMethod.POST)
public Authentication login(#RequestBody ObjectNode JSONObject) {
String username = JSONObject.get("username").asText();
String pwd = JSONObject.get("password").asText();
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, pwd));
boolean isAuthenticated = isAuthenticated(authentication);
if (isAuthenticated) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return authentication;
}
With following SecurityConfig:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().configurationSource(corsConfigurationSource()).and()
.authorizeRequests()
// ID ról
// admin = 2
// student = 1
// caretaker = 3
// teacher = 4
.antMatchers("/api/**").access("hasAnyAuthority('1','2','3','4')")
.antMatchers("/login").anonymous()
.antMatchers("/login").permitAll()
.and()
.formLogin()
.loginPage("/login").permitAll()
// .loginProcessingUrl("/login").permitAll() //tried to use this but it does nothing
.defaultSuccessUrl("/success", true)
.and()
.logout().and()
.httpBasic();
}
Example postman request to send login credentials to Spring:
Postman post request with credentials in body
But said request never enters the "/login" post endpoint.
The only response is whatever I put in the #get "/login" endpoint.
I wish to know how to set up the configuration and the /login endpoints in order for them to authorize user based on credentials from #RequestBody.
Answer:
#CrossOrigin(origins = "http://localhost:3000")
#GetMapping("/login")
public java.util.Map<String, String> getLogin(#RequestBody ObjectNode JSONObject)
{
String username = JSONObject.get("username").asText();
String pwd = JSONObject.get("password").asText();
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, pwd));
boolean isAuthenticated = isAuthenticated(authentication);
if (isAuthenticated) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return Collections.singletonMap("href" ,loginSuccessHandler(((MyUserPrincipal)authentication.getPrincipal()).getUser().getId()));
}
get mapping does authorization like here.
Postman result: Postman request authorized
By getting the cookie from that via(in Tests tab):
var a = pm.cookies.get("JSESSIONID")
pm.globals.set("JSESSIONID", a)
Access is possible to secured endpoint: Working secured endpoint

SAML with Spring security anonymousUser

We have to implement SSO using SAML in SpringBoot. I have achieved to redirect the user to the identity provider login page and make the login. The problem is that after the login, when I try to get the user info with SecurityContextHolder.getContext().getAuthentication() I get anonymousUser, and not the logged user data.
Here is my SecurityConfig
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().and()
.authorizeRequests()
.antMatchers("/saml/**).permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.userDetailsService(samlUserDetailsServiceImpl)
.serviceProvider()
.protocol("http")
.hostname("localhost:8080")
.basePath("/")
.keyStore()
.storeFilePath("classpath:metadata/samlKeystore.jks")
.keyPassword(keystorePass)
.keyname(keystoreAlias)
.and()
.and()
.identityProvider()
.metadataFilePath("classpath:metadata/idp.xml")
.discoveryEnabled(false)
.and()
.and();
And the implementation of SAMLUserDetailService
#Service
public class SamlUserServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) {
String userID = credential.getNameID().getValue();
GrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_GESTOR");
return new User(userID, "DUMMY", Collections.singletonList(userAuthority));
}
I have debugged the code and in the SAMLUserDetailsService implementation I receive the user data after logging.
Also, is there a way to indicate the redirection url when the user has logged? Now it redirects to same url.
Thanks in advance

Spring Security LDAP Auth from Ionic

I developed a Back End with using Java Spring and I added LDAP Authentication with extending WebSecurityConfigurerAdapter. I can get authenticated from POSTMAN but I can't from Ionic.
My Spring side ;
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//TODO: add other endpoints like /events in order to permit un-authenticated users
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login**").anonymous()
.antMatchers("/resources/**").permitAll()
.antMatchers("/assets/**").permitAll()
.antMatchers("/").authenticated()
.antMatchers("/home").authenticated()
.antMatchers("/events/**").authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll()
.and()
.cors()
.and()
.csrf()
.disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource()
.url("ldap://ldap.forumsys.com/dc=example,dc=com")
.and()
.userSearchFilter("(uid={0})")
.userSearchBase("ou=mathematicians")
.userDnPatterns("uid={0}");
}
Login Controller;
#RequestMapping(value = "/")
public String home() throws NamingException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
return "hey, nice! = " + currentPrincipalName;
}
And my Postman Login;
Postman Screenshot
Lastly, my client (ionic) side auth code;
authenticate(event) {
event.preventDefault();
const data = new URLSearchParams();
data.append("username",this.state.username);
data.append("password",this.state.password);
fetch(host + "/login", {
mode: 'no-cors',
method: 'POST',
body: data,
redirect :"follow",
headers: {
'Accept': '*/*'
},
keepalive: true
})
}
But from my Ionic side, I can't get "hey, nice! = euler" response as I get from POSTMAN. I think that I handled with CORS but I didn't figure out whats the problem.
I answered my question.
I added proxy to my package.json and added credentials: "include" to post request header at front end.

Spring Security: Referer always login page after authentication

I have sprint security setup like this:
http.authorizeRequests()
.antMatchers("/MyAccount").authenticated()
.antMatchers("/additem").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.successHandler(authenticationSuccessHandler)
.and().csrf().disable();
When I start on page Foo and click to /additem (not logged in) it redirects me (302) to /login. When I submit the login form REFERER is set to the login page not FOO or /additem. Thus I'm brought back to the login page albeit logged in. What am I missing?
Here is the code for the auth Handler:
public AuthSuccessHandler() {
super();
log.info("empty constructor called");
setDefaultTargetUrl("/popular-links");
setAlwaysUseDefaultTargetUrl(false);
//setUseReferer(true); <-- causes issues
//https://stackoverflow.com/questions/53026801/spring-security-referer-always-login-page-after-authentication
}
#Override
public void onAuthenticationSuccess( HttpServletRequest request,
HttpServletResponse response, Authentication authentication ){
String email = authentication.getName();
UserDetails userDetails = (UserDetails)authentication.getPrincipal();
log.info("Successful auth : "+email);
String token = tokenProvider.createToken(email, userDetails.getAuthorities());
log.info("Token: "+token);
sessionInfo.setJwtToken(token);
try {
handle(request,response,authentication);
} catch (Exception e){
log.error("An error occured "+e.toString());
e.printStackTrace();
}
}

Resources