Protect specific resources id with OAuth2 on Spring Boot - spring

I have a working OAUTH2 implementation on Spring Boot, with AuthorizationServer and ResourceServer on the same implementation, using password grant.
About the server:
The TokenStore is custom and uses a WebService to store the token remotely.
I have a custom AuthenticationProvider.
This works for controlling access to resources based on given authorities, for instance, in the ResourceServer config:
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/resource/**")
.hasAnyAuthority("USER", "ADMIN")
.antMatchers("/api/admin/**")
.hasAnyAuthority("ADMIN");
}
Now, I need to control that USER can access to "/api/resource/1" but not "/api/resource/2", this IDs can change and I fetch the list during the authentication.
I've tried to add the ID's list to OAuth2AccessToken additional information and adding a custom filter in the ResourceServer configuration but it always comes empty.
So, How's the proper way for implementing this without using JWT?

After some thinking and research, If someone is trying to achieve something similar, this is what I'll do:
Map the allowed ID's to authorities, e.g. ID_201 and the modules as roles, so I will have a ROLE_ADMIN.
It's possible to refer to path variables in Web Security Expressions, as explained in here. So the idea is to pass the variable (resource id) and check whether it's allowed or not.
public class WebSecurity {
public boolean checkResourceId(Authentication authentication, int id) {
//check if the list of authorities contains ID_{id}.
}
}
And in the Web Security config:
http
.authorizeRequests()
.antMatchers("/resource/{resourceId}/**").access("#webSecurity.checkResourceId(authentication,#resourceId)")
...
If you're working on Spring Boot with spring-security 4.0.4 be sure to upgrade to 4.1.1 as reported in this bug.

Related

Using two API Key on Swagger Security Scheme with Spring Boot

Is it possible to have two API Keys on Swagger and give them different privileges in my API?
For example:
API_KEY_1 : Has access to one Post method
API_KEY_2 : Has access to all of my API
Many thanks
In terms of Spring Security, that all depends on how you authenticate the API keys in your application. Once you've set up the application to validate an API key and create a SecurityContext, your best bet would be to map to one of two different roles, one for limited access, and one for all access. For example:
#Configuration
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests()
.mvcMatchers(HttpMethod.POST, "/some/api").hasAnyRole("BASIC", "ADMIN")
.anyRequest().hasRole("ADMIN");
}
}
More information and examples of authorization can be found in the docs.

Spring boot security, API-key protection OR oauth2 resourceServer for same resources

I have a spring boot 2.4 application where I want to protect it with either an API-key or a resource server. I was thinking that I could use filters here to first check if the api key is given and if so, grant access to the resource, otherwhise a "second chance" should be given to authenticate with an opaque oauth2-token (api key for machine to machine, token for frontend -> backend)
Where I get stuck is that my security config looks like this today (with a resource server activated)
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? =
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer {
it.authenticationManagerResolver(myMultiTenantResolver)
}
.build()
how would I go about to add an API-protection in here which should grant access (if it succeeds) without also invoking the resourceServer-snippet here (if it doesn't succeed, the resourceServer-snippet should be invoked)?
One possible solution can be as following:-
Create both your filters i.e the api-key filter and the auth-token filter.
In your configure(HttpSecurity http) method of ApplicationSecurityConfiguration add the api-key filter before the auth-token filter.
If you pass the api-key, put you authentication details in securityContextHolder. In the next filter(auth-token filter) Override the doFilter, where you need to check that if the previous filter has been authenticated, you do not run the current filter(auth-token filter) by calling chain.doFilter(request, response).
Please let me know if you need the complete implementation.

How to authenticate some URL and ignore anything else

I want to manage security policy like below. ( HTTP Basic Authorization)
Apply authentication following URLs.
"/foo/", "/bar/"
Ignore anything else URLs. ( Even though requests have Authorization field in header)
I know permitall(). But permitall() is not suitable because it apply security policy when request has Authorization field in headers.
If you want ignore particular url then you need to implement this method.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/static/**");
}
}
You can put your url in place of /static/** in which you want no authentication apply.
Your example means that Spring (Web) Security is ignoring URL patterns
that match the expression you have defined ("/static/**"). This URL is
skipped by Spring Security, therefore not secured.
Read the Spring Security reference for more details:
Click here for spring security details

Attach OAuth2 login to existing users in a Spring Boot application

I have a Spring Boot application that is "invitation only". Ie. users are sent a signup link and there is no "Sign up" functionality. This works fine and users log on with their username and password.
I would like to allow logon with FaceBook and Google using OAuth2 as a supplementary logon method. This would involve mapping the existing users to their social account in some way. The users and their passwords are stored in a MySQL database. I have found a number of articles on OAuth2 and Spring Boot, but none that address this exact use-case.
I can create the Google OAuth2 token/client secret etc, but how do I design the flow to allow only the existing users to logon with their social accounts?
The usernames have been chosen by the users themselves, and are therefore not necessarily the same as their email.
Do I need to create a custom authentication flow in this case?
And do I need to change the authentication mechanism from cookies to JWT tokens?
I found myself in a similar situation, where I needed to know if the OAuth request that I'm receiving is coming from an already authenticated user or not.
Although in my case I needed to know that because I want users to be able to "link" their existing account to social ones.
What I ended up doing was implementing an OAuth2UserService which would have a single method:
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException{
// this will be null if the OAuth2UserRequest is not from an authenticated user
// otherwise, it would contain the current user's principle which you can use to check if the OAuth request should be handled or not
Authentication currentAuth = SecurityContextHolder.getContext().getAuthentication();
// for example:
// if(currentAuth == null)
// throw new OAuth2AuthenticationException(OAuth2ErrorCodes.ACCESS_DENIED);
// Use the default service to load the user
DefaultOAuth2UserService defaultService = new DefaultOAuth2UserService();
OAuth2User defaultOAuthUser = defaultService.loadUser(userRequest);
// here you might have extra logic to map the defaultOAuthUser's info to the existing user
// and if you're implementing a custom OAuth2User you should also connect them here and return the custom OAuth2User
return defaultOAuthUser;
}
then just register the custom OAuth2UserService in your security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// your existing config
.oauth2Login()
.userInfoEndpoint()
.userService(oauthUserService())
;
}
#Bean
public OAuth2UserService oauthUserService(){
return new MyCustomOAuth2UserService();
}

Spring Oauth for Web Application

I have a query I am struggling to come to a decent answer with and hoping by vocalizing it someone might be able to guide me or advise me in the right direction.
All our current web applications are build using Spring, which includes the security which is handled by Spring Security module of course.
We are exploring the opportunities of integrating some of our new android projects into these web applications.
After some research and guidance, all flags point towards OAuth2 implementation in Android App and using that to obtain access to relevant parts of the web application server via Restfull calls.
What I am trying to understand now is can we and should we replace our existing Spring Security implementation in our web application and replace it with Spring Oauth2 equivalent.
The overall goal would to be able to have a single security solution that we would use for both website login, app login, and any API implementations that would be exposed to other web applications.
If anyone can also provide a link to a Spring Oauth2 Java Config (not-XML) setup where a user logs in and accesses a page based on their role in a unrestful manner, would also be extremely helpful. Most examples we have found were either extremely complex, focused solely on restfull calls, or only XML configurations.
Seems you did not hear about spring-cloud-security project which extremely simplifies working with oauth2 providers. Take a look at:
http://cloud.spring.io/spring-cloud-security/
There are samples available on github ( right side of above page ). It shows how to set up in case you want to use just one oauth2 provider - check project which shows how to do that for github https://github.com/spring-cloud-samples/sso . You do most of this stuff through configuration in application.yml file + add #EnableOAuth2Sso annotation so all machinery starts.
For 'normal' interaction with your web app its pretty straighforward. If you need your app to work as an api for others then you have to maintain tokens, add them to request etc. If client of your api is also your code which you can change then you may use OAuth2RestTemplate class which take care about tokens, so you do queries to your api as it was usual/not secured endpoint.
If you need to use for example two different providers for different paths then its more complicated, you have to configure them all like:
#Bean
public OAuth2RestTemplate facebookOAuth2RestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(facebookOAuth2ResourceDetails(), clientContext);
}
#Bean
public OAuth2ProtectedResourceDetails facebookOAuth2ResourceDetails() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(tokenUri);
resource.setId("id");
resource.setUserAuthorizationUri(authorizationUri);
resource.setUseCurrentUri(false);
resource.setPreEstablishedRedirectUri(redirectUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setTokenName("tokenname");
resource.setAuthenticationScheme(AuthenticationScheme.query);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
return resource;
}
and decide which instance of OAuth2RestTemplate to use in which case.
//update
If you want to exchange spring security core with authorizing users by some oauth2 provider you can extends OAuth2SsoConfigurerAdapter class:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/admin");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.and()
.logout().logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
}
So /admin will be now protected, and user redirected to any authorization server you specify. It requires oauth configuration in application.yml.
spring:
oauth2:
client: # Sauron
clientId: app_clientid
clientSecret: app_secret
accessTokenUri: http://authorizationserver/oauth/token
userAuthorizationUri: http://authorizationserver/oauth/authorize
clientAuthenticationScheme: header
resource:
tokenInfoUri: http://authorizationserver/oauth/check_token
preferTokenInfo: false
Thats why I wrote before that its easy to use just one auhorization server, but in case you need more then its more complicated.

Resources