Spring Security OAuth2 check_token endpoint - spring

I'm trying to setup a resource server to work with separate authorization server using spring security oauth. I'm using RemoteTokenServices which requires /check_token endpoint.
I could see that /oauth/check_token endpoint is enabled by default when #EnableAuthorizationServer is used. However the endpoint is not accessible by default.
Should the following entry be added manually to whitelist this endpoint?
http.authorizeRequests().antMatchers("/oauth/check_token").permitAll();
This will make this endpoint accessible to all, is this the desired behavior? Or am I missing something.
Thanks in advance,

You have to
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.checkTokenAccess("permitAll()");
}
For more information on this ::
How to use RemoteTokenService?

Just to clarify a couple of points, and to add some more information to the answer provided by Pratik Shah (and by Alex in the related thread):
1- The configure method mentioned is overridden by creating a class that extends AuthorizationServerConfigurerAdapter:
#EnableAuthorizationServer
#Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ger-client-id")
.secret("ger-secret")
.authorizedGrantTypes("password")
.scopes("read", "write");
}
}
2- I suggest reading this Spring guide explaining the automatic configuration carried out by Spring Boot when we include the #EnableAuthorizationServer annotation, including an AuthorizationServerConfigurer bean. If you create a configuration bean extending the AuthorizationServerConfigurerAdapter as I did above, then that whole automatic configuration is disabled.
3- If the automatic configuration suits you just well, and you JUST want to manipulate the access to the /oauth/check_token endpoint, you can still do so without creating an AuthorizationServerConfigurer bean (and therefore without having to configure everything programmatically).
You'll have to add the security.oauth2.authorization.check-token-access property to the application.properties file, for example:
security.oauth2.client.client-id=ger-client-id
security.oauth2.client.client-secret=ger-secret
security.oauth2.client.scope=read,write
security.oauth2.authorization.check-token-access=permitAll()
Of course, you can give it an isAuthenticated() value if you prefer.
You can set the log level to DEBUG to check that everything is being configured as expected:
16:16:42.763 [main] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression 'permitAll()', for Ant [pattern='/oauth/check_token']
There is no much documentation about these properties, but you can figure them out from this autoconfiguration class.
One last thing worth mentioning, even though it seems to be fixed in latest Spring versions, I just submitted an issue in the spring-security-oauth project; it seems that the token_check functionality is enabled by default if you add a trailing slash to the request:
$ curl localhost:8080/oauth/check_token/?token=fc9e4ad4-d6e8-4f57-b67e-c0285dcdeb58
{"scope":["read","write"],"active":true,"exp":1544940147,"authorities":["ROLE_USER"],"client_id":"ger-client-id"}

There are three POST parameters, namely client_id (user name), client_secret (password corresponding to the user name), token (token applied for), client_id, client_secret are different from the parameters in the /oauth/token interface
enter image description here

First, config token access expression:
#Override
public void configure(AuthorizationServerSecurityConfigurer securityConfigurer) throws Exception {
securityConfigurer
.allowFormAuthenticationForClients()
.checkTokenAccess("isAuthenticated()")
.addTokenEndpointAuthenticationFilter(checkTokenEndpointFilter());
}
Then, we need define a filter to process client authentication:
#Bean
public ClientCredentialsTokenEndpointFilter checkTokenEndpointFilter() {
ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter("/oauth/check_token");
filter.setAuthenticationManager(authenticationManager);
filter.setAllowOnlyPost(true);
return filter;
}

Related

Restrict the Rest API Methods except GET on a Spring Boot Project

I made a Rest API project with Spring Boot. There are every standard methods like get, post, put and delete in the Controller.
My aim is to make it possible for me to only be able to access the api calls (except get calls) via my angular app. Other methods (post, put and delete) can not be accessible from outside.
I tried to solve this problem with WebSecurityConfigurerAdapter and configure function but I couldn't get it.
When I first imported the security dependency (spring-boot-starter-security) on pom.xml, then all methods were blocked. I tried to permit the get calls in configure method but then I could not make a post call with basic auth over postman. Everytime I got 403 Forbidden error.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.GET)
.antMatchers("/h2-console/**")
.antMatchers("/login/**");
}
}
By the way, I wanted to make my own username and passwort for spring security in the application.properties file. But I think that doesn't work if I use a SecurityConfig configuration file.
spring.security.user.name=myUsername
spring.security.user.password=myPassword
Regardless of my attempts, how can I actually get this from shortest and easiest way?
Then how can I call the blocked methods (post, put, delete) from my angular application?
Thanks.
If I'm not mistaken, you want your project to have no access restrictions for GET methods and everyone should have access to this method type.
All remaining requests (post, put, delete, etc.) can be accessed with an authentication.
You can achieve this as follows. Assuming you have a controller like below:
#RestController
#RequestMapping("security")
public class SecurityController {
#GetMapping("get")
public ResponseEntity<String> get() {
return ResponseEntity.ok("Get Method");
}
#PostMapping("post")
public ResponseEntity<String> post() {
return ResponseEntity.ok("Post Method");
}
#PutMapping("put")
public ResponseEntity<String> put() {
return ResponseEntity.ok("Put Method");
}
#DeleteMapping("delete")
public ResponseEntity<String> delete() {
return ResponseEntity.ok("delete");
}
}
In this case your WebSecurityConfigurer should be like below:
#EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.GET).permitAll()
.anyRequest().authenticated().and().httpBasic();
}
}
The first thing to do here is to determine that GET, which is an http method, can access without any authorization. It then authorizes the accesses of the remaining HttpMethod's. Finally, we specify that we are using Basic Auth with httpBasic(). This information consists of username and password information defined in your application.properties file.
You can see the difference between HttpSecurity and WebSecurity by examining the question here.
I hope this answer helps you.

how do I enable the /refresh Actuator endpoint and get around CSRF restrictions?

In the WebSecurityConfig.java in our project
public class WebSecurityConfig extends WebSecurityConfigurererAdapter {
#Override
p void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasAnyAuthority("USER", "ADMIN")
.and.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
I have installed the #RefreshScope on my pojo:
#Configuration
#Data
#RefreshScope
public class MyProperties {
private String myName;
private String myAddress;
private String myCity;
.....
}
Spring Boot Actuator is installed. Spring Security is providing https and SSL. I can get to all of the GET actuator endpoints, but when I try to do a call to http://localhost:8080/actuator/refresh (a POST), I get a 403 Refused.
In looking around, I saw that the default stuff doesn't allow POSTs. This works if I disable csrf(), but not if it is enabled (which is a requirement for the product).
Could someone help me understand what is going on? Has anyone else done this? Can this be done without disabling CSRF entirely?
Thanks,
Winona
Just specify what endpoints on which you want to disable CSRF. For example: csrf().ignoringAntMatchers("/actuator/**"). Doing so disables CSRF prorection for any request that starts with /actuator/.

Is possible ask for an acces token oauth2 just with refresh token in spring security? without basic authentication?

I would like to know if in spring oauth2 is possible get a new pair tokens (access token and refresh token) just using another refresh token, without the basic authentication (without clientId and clientSecret, is there any way?
For exemple:
WITH BASIC AUTH
curl -u clientId:clientSecret -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=<CLIENT_ID>&refresh_token=' -v
WITHOUT BASIC AUTH
curl -u -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=<CLIENT_ID>&refresh_token=' -v
I note that sprint BasicAuthenticationFilter in spring uses validation bellow, maybe override this filter and make the authentication just with refresh token.
String header = request.getHeader("Authorization");
if (header == null || !header.toLowerCase().startsWith("basic ")) {
chain.doFilter(request, response);
return;
}
The short answer is no. The class used to manage the Spring Oauth 2 endpoints is the following one:
#FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint
Both requests, I mean, get access token and refresh one use the same endpoint with different parameters. And the method to manage those ones is:
#RequestMapping(
value = {"/oauth/token"},
method = {RequestMethod.POST}
)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, #RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
} else {
String clientId = this.getClientId(principal);
...
As you can see, a Principal object is required (in this case provided by the Basic Authentication).
Even, if you configure the security of your project to permit that url without checking authentication, you will achieve to "enter" in above method but you will receive an InsufficientAuthenticationException because no Authentication instance has been provided.
Why custom authentication will not work
1. Create a custom AuthenticationProvider will not work because the method postAccessToken is invoked before. So you will receive an InsufficientAuthenticationException.
2. Create a OncePerRequestFilter and configure it to execute before process the current request:
#Override
protected void configure(HttpSecurity http) throws Exception {
http...
.anyRequest().authenticated()
.and()
.addFilterBefore(myCustomFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(POST, "/accounts/oauth/**");
}
with a code "similar to":
#Component
public class CustomAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
...
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("existingUser",
"passwordOfExistingUser",
Collections.emptyList()));
...
filterChain.doFilter(request, response);
}
The problem with this approach is the principal in TokenEndpoint comes from the HttpServletRequest not from Spring context, as you can see debugging BasicAuthenticationFilter class.
In your custom filter you can try, using reflection, set a value in userPrincipal property but, as you can verify, request has several "internal request properties" and that could be a "too tricky option".
In summary, Oauth standard needs user/pass to access to the resources, if you want to workaround in almost of provided endpoints maybe that project is not what you are looking for.
Workaround to include your own object in Spring Principal
I do not recommend that but if you still want to go ahead with this approach, there is a way to include your own value inside the principal parameter received by TokenEndpoint class.
It is important to take into account BasicAuthorizationFilter will be still executed, however you will be able to override the Spring principal object by your own one.
For this, we can reuse the previous CustomAuthenticationFilter but now your have to include the filters you need, I mean, allowed urls, parameters, etc You are going to "open the doors", so be careful about what you allow and not.
The difference in this case is, instead of add the configuration in our class that extends WebSecurityConfigurerAdapter we are going to do it in:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private CustomAuthenticationFilter customAuthenticationFilter;
...
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
security.addTokenEndpointAuthenticationFilter(customAuthenticationFilter);
}
...

Combining Spring Security and Active Directory with JWT-Tokens

I am currently working on building an Angular2-Application accessing a Spring Boot backend server for authentication and editing database data via REST-APIs. My current basic configuration to authenticate against our Active Directory looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, "ldap://" + AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
This works so that I can access the basic Spring Boot Actuator-APIs after logging in using the default Boot Login Form.
The next step would be intertwining the activeDirectoryLdapAuthenticationProvider with a token-based authentication solution that can be accessed by Angular2 - for that, i found an example repository on gitHub. It implements HMAC-based authentication using JWT tokens with a custom service structure.
My issue now comes in understanding how the components of Spring Security work together in the background. As i understand the example, it implements a customised version of UserDetailsService and its environment using the UserDTO and LoginDTO. These access a MockUser-Service for example user data - which is what I want to avoid, instead accessing our Active Directory to authenticate users.
I am still digging through the repository trying to understand every customised class implementation and if I can customise the AuthenticationService to fit my needs, but I feel relatively lost.
What I am looking for are experiences in combining these three components, or with Spring Security and how exactly the default UserDetailsService and AuthenticationProviders interact, so I can try and fit the ActiveDirectory-logic into the token solution.
Alternatively, is there a way to combine the default-LDAP/AD-Configuration with a default JWT-Solution such as this?

#EnableOAuth2Sso - How to protect / unprotect resources?

I’m trying to use the #EnableOAuth2Sso feature in Spring Cloud Security. Specifically, I’m attempting to protect some resources with OAuth2 while leaving others publicly accessible. I've managed to get this working, but I'm looking at the resulting code and wondering if there is a cleaner way.
I’m following the documentation here: https://github.com/spring-cloud/spring-cloud-security/blob/master/src/main/asciidoc/spring-cloud-security.adoc AND similar guidance from the Spring Boot reference. I have a tiny code example that illustrates my dilemma: https://github.com/kennyk65/oAuthSsoExample.
In a nutshell, I want the localhost:8080/unprotected resource to be publicly available, and I want the localhost:8080/protected resource to require OAuth2 (via github, as configured). I’m able to get the basic OAuth2 behavior to work just fine, but causing /unprotected to be publicly available is problematic.
First, The docs indicate that you can just use the OAuth2SsoConfigurer’s match() method to specify the resources to protect. I've found this doesn't work; when I try I get an IllegalStateException saying that at least one mapping is required. This appears to be referring to the unimplemented configure(HttpSecurity) method.
Next, I’ve tried to specify a mapping in configure(HttpSecurity) that states that the ‘unprotected’ resources should be unprotected. However, this results in Http basic security being applied to that resource. Curiously, this results in the ‘protected’ resource being completely public!
// This results in “unprotected” being protected by HTTP Basic
// and “protected” being completely open!
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/unprotected/**").permitAll();
}
}
On a whim I tried deliberately adding that the protected resource should be authenticated. This resulted in the protected resource getting OAuth2 protection (hurrah!) but the unprotected resource got http basic security applied (huh?).
// This results in “protected” being protected by OAuth 2
// and “unprotected” being protected by HTTP Basic, even though we say permitAll():
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/protected/**”).authenticated();
.antMatchers("/unprotected/**").permitAll();
}
}
At wit’s end to try to find the magic combination, I tried simply switching HTTP basic authentication off using security.basic.enabled: false. This worked (hurrah!), though I’m still a bit puzzled what the issue is with the mappings.
So I guess my question is, is this correct? What is the best way to protect some resources with OAuth 2 and leave others alone?
If you match on /protected/** then it makes no sense to then add an access rule to /unprotected/** (the path doesn't match so the rule will never be applied). You either need another filter chain for your "unprotected" resources, or a wider match for the SSO one. In the former case, the default one that you get from Spring Security will do if you don't mind switching off the security it is providing. E.g.
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**”).authenticated();
}
}
and set security.basic.enabled=false.

Resources