Spring Boot JWT authorization throws 401 even with a valid token - spring-boot

Below is my web security config class,
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "authority")
#Profile({"cloud", "development", "release"})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private Map<String, String> data = new LinkedHashMap<>();
private List<String> whiteList = new ArrayList<>();
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/v1/students").authenticated()
.and().authorizeRequests().antMatchers("/api/v1/students/*").authenticated()
.and().authorizeRequests().antMatchers("/api/v1/students/*").access("hasRole('learning_management_admin_app1000000')")
.and().authorizeRequests().antMatchers("/api/v1/students").access("hasRole('learning_management_admin_app1000000')");
http.csrf().disable().authorizeRequests().antMatchers(getWhiteList().toArray(new String[0]))
.permitAll();
http.cors();
Okta.configureResourceServer401ResponseBody(http);
}
public List<String> getWhiteList() {
..
And In the controller,
#Validated
#CrossOrigin("*")
#RestController
#RequestMapping("/api/v1")
#SecurityRequirement(name = "BearerAuth")
#PreAuthorize("isAuthenticated()")
public class StudentsController {
#GetMapping("change-requests")
#PreAuthorize("hasAuthority('learning_management_admin_app1000000')")
public ResponseEntity<List<Student>> getStudents(
#RequestParam(required = false) String name) {
Here even with a valid token, it throws 401 Unauthorized exception from every endpoint. Even in the swagger ui, it throws 401 unauthorized with a valid token.

Related

Authenticate and get to Reddit resource

I wanted to call https://oauth.reddit.com/api/v1/me endpoint, so I created follwing REST controller:
#RestController
#RequestMapping("/reddit")
public class RedditController {
#Autowired
private OAuth2RestTemplate redditRestTemplate;
#Value("${secured.service.url:https://oauth.reddit.com/api/v1/me}")
private String endpoint;
#RequestMapping(value = "/message", method = RequestMethod.GET)
public String getMessageFromSecuredService(){
ResponseEntity<String> entity = redditRestTemplate.getForEntity(endpoint, String.class);
return entity.getBody();
}
}
To configure authentication I created following configuration:
#Configuration
#EnableOAuth2Client
#EnableWebSecurity
public class KeycloakClientCredentialsConfig extends WebSecurityConfigurerAdapter {
//...
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
}
#Bean
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("reddit");
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("identity", "edit", "flair", "history", "modconfig", "modflair", "modlog", "modposts", "modwiki", "mysubreddits", "privatemessages", "read", "report", "save", "submit", "subscribe", "vote", "wikiedit", "wikiread"));
details.setPreEstablishedRedirectUri("http://localhost:8080");
details.setUseCurrentUri(false);
return details;
}
#Bean
public OAuth2RestTemplate createRestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(oAuth2ProtectedResourceDetails(), clientContext);
}
}
However each time I am not getting JSON result, but HTML page so it seems that authentication didn't work.
Do you know if my configuration is not set correctly?
Maybe my REST template should be built on configuration for invoking refresh token endpoint instead of authorize endpoint?

How to pass authentication in spring test case

I am using spring boot 1.5.6 with OAuth 2.0. The problem is that when I run below test case, I get Null pointer exception on below line for authentication object.
System.out.println(authentication.getName());
Test Class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = WebApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource( locations = "classpath:test-application.properties")
public class ProfileControllerTest {
......
......
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
...........
.........
}
#Test
public void profileTest() throws Exception {
String userId = "12345678";
mockMvc.perform(get("user/" + userId + "/profile"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
Rest Controller
#RequestMapping(value = "user/{userId}/profile",
method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> getProfile(
#PathVariable(value = "userId", required = true) String userId,
HttpServletRequest request, Authentication authentication) {
System.out.println(authentication.getName());
}
Security Configuration
#Configuration
#EnableOAuth2Sso
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String OAUTH_COOKIE = "OAUTH-ACCESS-TOKEN";
private UserService userService;
private OAuth2TokenAuthenticationFilter ssoFilter;
private JwtAccessTokenConverter accessTokenConverter;
/**
* WebAppSecurity overloaded constructor.
*/
public WebAppSecurityConfig(UserService userService,
OAuth2TokenAuthenticationFilter ssoFilter,
JwtAccessTokenConverter accessTokenConverter) {
this.userService = userService;
this.ssoFilter = ssoFilter;
this.accessTokenConverter = accessTokenConverter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous()
.disable()
.authorizeRequests()
.antMatchers("/logout-success")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterAfter(ssoFilter,
AbstractPreAuthenticatedProcessingFilter.class).logout()
.logoutUrl("/logout").logoutSuccessUrl("/logout-success").permitAll()
.deleteCookies(OAUTH_COOKIE);
}
#Autowired
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
configure((DefaultAccessTokenConverter) accessTokenConverter
.getAccessTokenConverter());
}
protected void configure(DefaultAccessTokenConverter accessTokenConverter) {
DefaultUserAuthenticationConverter userTokenConverter =
new DefaultUserAuthenticationConverter();
userTokenConverter.setUserDetailsService(userDetailsService());
accessTokenConverter.setUserTokenConverter(userTokenConverter);
}
#Override
protected UserDetailsService userDetailsService() {
return userService;
}
How to pass Authentication object in above test case?
You can use the #WithMockUser annotation on your test.
For example:
#Test
#WithMockUser(username = "admin", roles = { "ADMIN" })
public void profileTest() throws Exception {
String userId = "12345678";
mockMvc.perform(get("user/" + userId + "/profile"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
Assuming you have spring-security configured and enabled for the "user/" + userId + "/profile" endpoint then System.out.println(authentication.getName()); in your controller will print: admin.
More details in the Spring Securty docs.

Spring Security: Token based authentication and JSR 250

#RestController
public class ApplicationController {
#PermitAll
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "Greetings from ContextConfig Boot!";
}
#RolesAllowed({"ADMIN"})
#RequestMapping(value = "/secured", method = RequestMethod.GET)
public String secured() {
return "Secured :)";
}
}
Token is send in header "X-AUTH-TOKEN".
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
}
}
This actual spring security configuration. How to configure spring security when user send token in header and hase role "ADMIN" he will be allowed to access "secured"?

Custom AuthenticationProvider is not called

I want to have a basic auth-protected REST app. I followed the general instructions from http://www.baeldung.com/spring-security-authentication-provider in order to get the security working.
I ended up creating my implementation of AuthenticationProvider, but it never gets called by Spring. All requests end up with an error:
{"timestamp":1460199213227,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/test"}
without the AuthenticationProvider ever doing anything.
The app is annotation-based and here are the relevant bits:
Security setup
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authenticationProvider(authenticationProvider)
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
}
AuthenticationProvider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserDAO userDAO;
#Autowired
private Authenticator authenticator;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// This never gets called, I checked with debugger
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userDAO.findByUsername(username);
User authenticatedUser = authenticator.authenticate(user, password);
if (authenticatedUser == null){
throw new RESTAuthenticationException("Auth failed");
}
List<GrantedAuthority> authorityList = new ArrayList<>();
return new UsernamePasswordAuthenticationToken(user, authorityList);
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
Controller
#RestController
public class UserController {
#RequestMapping(value = "/test")
public ResponseEntity test(#AuthenticationPrincipal User user) {
return ResponseEntity.ok().body(user);
}
}
You receive a response with status code 401. This is the "unauthorized" http status code. It is probably caused by a missing/malformed Authorization header in your request.
You are using Http-Basic: it requires the following header in the request :
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
where the string QWxhZGRpbjpPcGVuU2VzYW1l is the string <user>:<password> base64 encoded.

spring-boot OAuth2 client configuration

I try to implement OAuth2 client using authorization-code grant flow by spring-boot.
But it does not work.
"http://external_server/oauth/authorize" was called, but no GET arguments added.
Does anyone know what is wrong in below configuration?
Auth provider is implemented by doorkeeper and it's already working.
so URL constants in WebSecurityConfiguration are correct.
#Configuration
#EnableWebMvcSecurity
#EnableOAuth2Client
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String AUTH_ENDPOINT = "http://external_server";
private static final String LOGIN_URL = AUTH_ENDPOINT + "/users/sign_in";
private static final String LOGOUT_URL = AUTH_ENDPOINT + "/sign_out";
private static final String AUTH_URL = AUTH_ENDPOINT + "/oauth/authorize";
private static final String ACCESS_TOKEN_URL = AUTH_ENDPOINT + "/oauth/token";
#Autowired OAuth2ClientContext oAuth2ClientContext;
/**
* for specific api
*/
#Bean public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* for accessing protected resource
*/
#Bean public OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(resource(), oAuth2ClientContext);
}
#Bean protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setClientId("_xxx_");
resource.setClientSecret("_yyy_");
resource.setUserAuthorizationUri(AUTH_URL);
resource.setAccessTokenUri(ACCESS_TOKEN_URL);
return resource;
}
#Override public void configure(WebSecurity web) throws Exception {
web.debug(true).ignoring().antMatchers("/webjars/**", "/css/**");
}
#Override protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.csrf().disable().authorizeRequests()
.antMatchers("/", "/callback")
.permitAll()
.anyRequest()
.authenticated();
http.formLogin()
.loginPage(AUTH_URL)
.loginProcessingUrl(LOGIN_URL);
http.httpBasic()
.disable();
//#formatter:on
}
}
By default only POST Method is enabled. You may need to include GET Method on AuthorizationConfig.
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
Will be like this:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
....
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}
On source code of Spring Oauth we have:
private Set<HttpMethod> allowedTokenEndpointRequestMethods() {
// HTTP POST should be the only allowed endpoint request method by default.
if (allowedTokenEndpointRequestMethods.isEmpty()) {
allowedTokenEndpointRequestMethods.add(HttpMethod.POST);
}
return allowedTokenEndpointRequestMethods;
}

Resources