OAuth2AuthenticationEntryPoint is deprecated - spring

I have some filter that return 401 when some header is exist in the request.
my Filter is:
#Component
#Order(1)
public class BlockHeaderFilter implements Filter {
private static final AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
...
if (headerExist) {
Exception failed = new Exception("Unauthorized request. x-private header is forbidden");
authenticationEntryPoint.commence(
httpServletRequest,
httpServletResponse,
new InsufficientAuthenticationException(failed.getMessage(), failed)
);
}
...
}
}
This is working fine, but since Spring 5, seems that OAuth2AuthenticationEntryPoint is deprecated.
'org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint' is deprecated
/**
* If authentication fails and the caller has asked for a specific content type response, this entry point can send one,
* along with a standard 401 status. Add to the Spring Security configuration as an {#link AuthenticationEntryPoint} in
* the usual way.
*
* <p>
* #deprecated See the OAuth 2.0 Migration Guide for Spring Security 5.
*
* #author Dave Syer
*
*/
#Deprecated
public class OAuth2AuthenticationEntryPoint extends AbstractOAuth2SecurityExceptionHandler implements
AuthenticationEntryPoint {
...
}
Looking at the migration guide (https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide) or anywhere else, I can't find the modern pattern for the same task.
Does anyone knows what was replacing OAuth2AuthenticationEntryPoint?
Thanks!

AuthenticationEntryPoint is a spring interface.
And if you look in the api you can see under the all known implementing classes you can find the implementation BearerTokenAuthenticationEndpoint
Baerer token is more neutral then writing oauth2 since there are other authentication standards that also use tokens.

Related

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);
}
...

With Spring Security how do i determine if the current api request should be authenticated or not?

With spring security you can have public api endpoints that are accessible by everyone and endpoints that need to be authenticated before getting a response. In my app users authenticate via a jwt token. For logged in users right now the token is always checked, regardless of whether a public api endpoint gets the request or not.
I would like to know how to check if the current endpoint is a public endpoint or a authenticated one, that way i can alter the code so that the token checking is only done when the endpoint requires authentication.
I could add all public endpoints in a hashset and compare the current request endpoint with the public ones but that isn't efficient and also, some of the public endpoints contain wildcards (**) so that would make comparing a bit of a hassle.
This is the only information i could find:
Spring Security - check if web url is secure / protected
but its about JSP.
I can't get the request information from SecurityContextHolder.getContext() either. My guess is that i should get the information from org.springframework.security.config.annotation.web.builders.HttpSecurity because that is the same class used to define which endpoints don't require authentication. (with anthMatchers().permitall()). But i don't know which method to invoke and i'm not sure if HttpSecurity can even be autowired into another class. Can anyone give me some pointers?
Thank you
Assuming that you're using a separate filter for the token check, you can avoid the token check for public endpoints by overriding the protected boolean shouldNotFilter(HttpServletRequest request) method of the OncePerRequestFilter in your JwtTokenFilter. By default, this method will always return false. So all requests will get filtered. Overriding this method to return true for the public endpoints will give you the desired functionality.
And to check the requests with the wildcards(**), you can use AntPathRequestMatcher. So, you can do something like below.
public class JwtTokenFilter extends OncePerRequestFilter {
private static RequestMatcher requestMatcher;
public static void ignorePatterns(String... antPatterns) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, null));
}
requestMatcher = new OrRequestMatcher(matchers);
}
static {
final String[] publicEndPoints = {"/public-api/**","/resources/**"};
ignorePatterns(publicEndPoints);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return requestMatcher.matches(request);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
....
}
}
Hope this helps!!

REST API with both apiKey and username/password authentication

I'm a Spring's beginner and I'm trying to build a REST API, connected to a React frontend in order to learn these technologies.
In order to secure this API, I added an apiKey mechanism with Spring Security, by creating a filter that checks a specific header key (API-KEY in this case), and that only allows requests that match the correct api key value.
I added this filter in my security config, which extends WebSecurityConfigurerAdapter. However, I'd like to add another authentication mechanism just to authenticate my users, in a traditional username/password way. I'm a bit lost, I read a lot of articles but all of these are using the same mechanism (filter + configure the security component). But I really don't know how to gather these two mechanisms.
I would like all requests are intercepted to check the API-KEY value, but I also would like to have an anonymous and authenticated parts in my app.
How could I achieve this ? I found some elements like interceptors but it seems to be only available for spring-mvc app.
Here's the filter I'm using :
public class ApiKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
/**
* The request header we want to check with our apiKey
*/
private String principalRequestHeader;
public ApiKeyAuthFilter(String principalRequestHeader) {
this.principalRequestHeader = principalRequestHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
And here's my security config :
#Configuration
#EnableWebSecurity
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* The header corresponding to our apiKey
*/
#Value("${application.security.requestKey}")
private String apiKeyHeader;
/**
* The api key value we want to test with the header value
*/
#Value("${application.security.apiKey}")
private String apiKeyValue;
Logger logger = LoggerFactory.getLogger(ApiSecurityConfig.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
ApiKeyAuthFilter filter = new ApiKeyAuthFilter(this.apiKeyHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String principal = (String) authentication.getPrincipal();
if (!apiKeyValue.equals(principal)) {
throw new BadCredentialsException("The API key was not found or doesn't match the correct value");
}
logger.info("Connexion autorisée");
authentication.setAuthenticated(true);
return authentication;
}
});
http.cors().and().
antMatcher("/api/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().
addFilter(filter).
authorizeRequests().anyRequest().authenticated();
}
}
Do you have any clue to setup this kind of authentication ? I saw that we could define an order in our filter with methods like addFilterAfter() or addFilterBefore(), but I don't know how to setup this with my usecase.
I also found this post : How to config multiple level authentication for spring boot RESTful web service?
which seems to have the same requirements, I tried the solution provided but the authentication isn't dynamic (it's only using a string "valid-user" for its authentication filter, and I need to authenticate through my User entity stored in an in-memory h2 database. How to achieve this ?
Thank's a lot for your answers and have a nice day !

How to create servlet filter to authorize the request?

How to create spring servlet filter to authorize the request.
Need to add a filter in the spring security filter chain that updates the user with few details (re-loading permissions or anything) for every request, if they need to be changed.
Need some sample code snippet to follow or understand.
Thanks in advance.
To add a custom filter you should extend the org.springframework.web.filter.GenericFilterBean, like so:
public class MySecurityFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
The next step is to actually register the filter by overriding the configure method of WebSecurityConfigurerAdapter:
#Configuration
public class CustomWebSecurityConfigurerAdapter
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(
new MySecurityFilter(), BasicAuthenticationFilter.class);
}
}
As you can see the filter is added by adding the filter to the HttpSecurity object. The method that is used is the addFilterAfter which basically allocates your filter after the one that you provide in the second argument, in this example is the BasicAuthenticationFilter, so your filter will be executed after this one, in the spring secuirty chain of filters.
Update
Refer to this link to personalize the authorization of your servlet. The method gives you an Authentication object, through which you can obtain your User object and perform additional checks.

Multiple WebSecurityConfigurerAdapter: one as a library, in the other users can add their own security access

I am creating a Spring Security configuration to be used as a library by any developer who wants to create a Stormpath Spring application secured by Spring Security.
For that I have sub-classed WebSecurityConfigurerAdapter and defined the Stormpath Access Controls in configure(HttpSecurity) as well as the Stormpath AuthenticationProvider by means of configure(AuthenticationManagerBuilder). All this can be seen in this abstract class and its concrete sub-class:
#Order(99)
public abstract class AbstractStormpathWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Removed properties and beans for the sake of keeping focus on the important stuff
/**
* The pre-defined Stormpath access control settings are defined here.
*
* #param http the {#link HttpSecurity} to be modified
* #throws Exception if an error occurs
*/
protected void configure(HttpSecurity http, AuthenticationSuccessHandler successHandler, LogoutHandler logoutHandler)
throws Exception {
if (loginEnabled) {
http
.formLogin()
.loginPage(loginUri)
.defaultSuccessUrl(loginNextUri)
.successHandler(successHandler)
.usernameParameter("login")
.passwordParameter("password");
}
if (logoutEnabled) {
http
.logout()
.invalidateHttpSession(true)
.logoutUrl(logoutUri)
.logoutSuccessUrl(logoutNextUri)
.addLogoutHandler(logoutHandler);
}
if (!csrfProtectionEnabled) {
http.csrf().disable();
} else {
//Let's configure HttpSessionCsrfTokenRepository to play nicely with our Controllers' forms
http.csrf().csrfTokenRepository(stormpathCsrfTokenRepository());
}
}
/**
* Method to specify the {#link AuthenticationProvider} that Spring Security will use when processing authentications.
*
* #param auth the {#link AuthenticationManagerBuilder} to use
* #param authenticationProvider the {#link AuthenticationProvider} to whom Spring Security will delegate authentication attempts
* #throws Exception if an error occurs
*/
protected void configure(AuthenticationManagerBuilder auth, AuthenticationProvider authenticationProvider) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Configuration
public class StormpathWebSecurityConfiguration extends AbstractStormpathWebSecurityConfiguration {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
In short, we are basically defining our login and logout mechanisms and integrating our CSRF code to play nicely with Spring Security's one.
Up to this point everything works OK.
But this is just the "library" and we want users to build their own applications on top of it.
So, we have created a Sample application to demonstrate how a user will use our library.
Basically users will want to create their own WebSecurityConfigurerAdapter. Like this:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/**
* {#inheritDoc}
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
}
In case this is actually needed, the WebApplicationInitializer looks like this:
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringSecurityWebAppConfig.class);
context.register(StormpathMethodSecurityConfiguration.class);
sc.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Stormpath Filter
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
EnumSet<DispatcherType> types =
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filter.addMappingForUrlPatterns(types, false, "/*");
//Spring Security Filter
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
All this code boots up correctly. If I go to localhost:8080 I see the welcome screen. If I go to localhost:8080/login I see the login screen. But, if I go to localhost:8080/restricted I should be redirected to the login page since we have this line: http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();. However I am seeing the Access Denied page instead.
Then, if I add the login url in the App's access control, like this:
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login")
.and()
.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
It now redirects me to the login page but as soon as I submit the credentials I get an CSRF problem meaning that all our configuration is not actually part of this filter chain.
When I debug it all it seems that each WebApplicationInitializer is having its own instance with its own Filter Chain. I would expect them to be concatenated somehow but it seems that it is not actually happening...
Anyone has ever tried something like this?
BTW: As a workaround users can do public class SpringSecurityWebAppConfig extends StormpathWebSecurityConfiguration instead of SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter. This way it works but I want users to have pure Spring Security code and extending from our StormpathWebSecurityConfiguration diverges from that goal.
All the code can be seen here. The Stormpath Spring Security library for Spring is under extensions/spring/stormpath-spring-security-webmvc. The Sample App using the library is under examples/spring-security-webmvc.
It is very simple to run... You just need to register to Stormpath as explained here. Then you can checkout the spring_security_extension_redirect_to_login_not_working branch and start the sample app like this:
$ git clone git#github.com:mrioan/stormpath-sdk-java.git
$ git checkout spring_security_extension_redirect_to_login_not_working
$ mvn install -DskipTests=true
$ cd examples/spring-security-webmvc
$ mvn jetty:run
Then you can go to localhost:8080/restricted to see that you are not being redirected to the login page.
Any help is very much appreciated!
In my experience there are issues with having multiple WebSecurityConfigurers messing with the security configuration on startup.
The best way to solve this is to make your library configuration into SecurityConfigurerAdapters that can be applied where appropriate.
public class StormpathHttpSecurityConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
}
public class StormpathAuthenticationManagerConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
You then have your users apply these in their own configuration:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/restricted").fullyAuthenticated()
.and()
.apply(new StormPathHttpSecurityConfigurer(...))
;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new StormPathAuthenticationManagerConfigurer(...));
}
}
It's definitely the problem regarding either order of your Antmatchers or hasn't specified ROLES of users that you permit to access the URL.
What do you have anything above "/restricted"?
Is something completely blocking anything below that URL? You should specify more specific URLS first then, generalised URLs.
Try configuring above URL properly (or tell me what it is so I can help you out), perhaps apply "fullyAuthenticated" also "permitAll" ROLEs on the parent URL of "/restricted".

Resources