I'm using Spring Security OAuth2 to create my own authorization server. In my case I want to enable a Angular client (SPA) to use the Authorization Code Grant.
The client can use the oauth/authorize endpoint, the user can log in and the browser is redirect to the SPA. Now the client wants to get the token via oauth/token. But this endpoint is secured and needs client id and client secret. The security is enabled by default in Spring.
In the docs I could find the following:
The token endpoint is protected for you by default by Spring OAuth in the #Configuration support using HTTP Basic authentication of the client secret. This is not the case in XML (so it should be protected explicitly).
As far as I know there shouldn't be a client secret used in public clients. But that means, that the oauth/token endpoint should not be secure.
Question: Is it a good practice to disable auth for oauth/token? If not, how should I solve this?
This is my WebSecurityConfig:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
protected UserDetailsService userDetailsService() {
// WARN: Do not use the default password encoder in production environments!
return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder()
.username("user-a")
.password("password")
.roles("USER_ROLE")
.build()
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http = http
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.cors()
.and()
.authorizeRequests()
.antMatchers("/.well-known/**")
.permitAll()
.and();
super.configure(http);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(singletonList("*"));
configuration.setAllowedMethods(asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Related
My Spring Webflux application provides multiple authentication methods for the APIs, the user either presents a JWT token or he presents a userid and password. I understand that each authentication method is a separate SecurityWebFilterChain. In my security config I defined 2 Beans, one for basic auth and one for JWT. Setting up each one for different endpoints works fine using a SecurityMatcher, but how do I setup both for the same endpoint. I want either basic auth or JWT token to authenticate for a specific endpoint. All my attempts result in the first authentication method failing and returning a 401 unauthorized without attempting to try the second method. How do I get it not to fail but to try the second SecurityWebFilterChain bean?
Here is the code from my security config
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
#Autowired private SecurityContextRepository securityContextRepository;
#Bean
SecurityWebFilterChain webHttpSecurity(
ServerHttpSecurity http, BasicAuthenticationManager authenticationManager) {
http.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/something/**"))
.authenticationManager(authenticationManager)
.authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated())
.httpBasic()
.and()
.csrf()
.disable();
return http.build();
}
#Bean
SecurityWebFilterChain springWebFilterChain(
ServerHttpSecurity http, AuthenticationManager authenticationManager) {
String[] patterns =
new String[] {
"/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/v3/api-docs/**",
"/webjars/**",
};
return http.cors()
.disable()
.exceptionHandling()
.authenticationEntryPoint(
(swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)))
.accessDeniedHandler(
(swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN)))
.and()
.csrf()
.disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers(patterns)
.permitAll()
.pathMatchers(HttpMethod.OPTIONS)
.permitAll()
.anyExchange()
.authenticated()
.and()
.build();
}
The first Bean sets up basic auth for one specific endpoint using a custom authentication manager which veruifies the userid and password, the second bean sets up JWT auth for all other endpoints (with a custom AuthenticationManager that verifies the token etc.) except those that are excluded. Lets say I have the following endpoints
/api/something
/api/whatever
.....
endpoint 1 I want to authenticate with either basic auth or JWT
endpoint 2,3 ,n I want only JWT
As I have it now endpoint 1 is using only basicAuth and all other endpoints use JWT. How can I add JWT to endpoint 1 as well?
I'm trying to create form login with spring boot webflux. I can login and after login I'm redirectored successfully. But when I browse to a page that requires authentication, I'm getting error. If I remove the page from security config and get principal from ReactiveSecurityContextHolder I'm getting the user details.
Here is my security config:
public class SecurityConfig {
#Autowired
private UserService userService;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/user/account")
.authenticated()
.anyExchange().permitAll()
.and()
.formLogin()
.loginPage("/user/login")
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/"))
.authenticationManager(reactiveAuthenticationManager())
.and()
.logout()
.and()
.build();
}
#Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return authentication -> userService.loginUser(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication.getName())))
.map(user -> new UsernamePasswordAuthenticationToken(user, null));
}
}
Do I need to do anything else in the ReactiveAuthenticationManager? Is that even required?
In this repository : https://github.com/mohamedanouarbencheikh/dashboard-auth-microservice
you have a complete example of spring security implementation with jwt in microservice architecture using spring cloud routing (gateway) which is based on reactive programming and Netty as application server, and angular as frontend
Answering to my own question so that anyone facing same problem can get some help:
The issue was resolved when I've changed the UsernamePasswordAuthenticationToken constructor and passed the authority parameter as null. This is really ridiculous. Here is the updated code:
#Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return authentication -> userService.loginUser(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication.getName())))
.map(user -> new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()));
}
I've also simplified the config by removing authenticationSuccessHandler and authenticationManager from the config. Spring automatically redirects to /. For authenticationManager it automatically checks for a ReactiveAuthenticationManager bean and uses if found. Here is my updated config:
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/user/account")
.authenticated()
.anyExchange().permitAll()
.and()
.formLogin()
.loginPage("/user/login")
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessHandler(logoutSuccessHandler("/user/bye"))
.and()
.build();
}
I have an application where users/applications can authenticate either with an OpenID provider or with a JWT token.
Here is my spring security configuration class.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService()).and()
.and()
.oauth2ResourceServer()
.jwt();
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
return oidcUserRequest -> {
OidcUserService oidcUserService = new OidcUserService();
OidcUser oidcUser = oidcUserService.loadUser(oidcUserRequest);
return oidcUser;
};
}
}
It's working as expected but I would like to disable session creation for the JWT authorization part. Do I need to split this into multiple configurations? I understand that if we have multiple configuration classes we need to differentiate based on URL pattern which I can't do in my case as a user authenticated via OpenId or via JWT still should be able to access the same URLs.
Here is the complete sample code in Github.
I solved by splitting the configuration into two classes. One for OAuth login and the other for the resource server. Configured
http.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
on the resource server Configuration class and made it's Order as 1 and Open Id configuration order as 2. In Resource server configuration I have disabled session creation.
In this way, if any external clients are calling with a JWT token with header 'Authorization' then it will be handled by Resource server configuration or else it will be handled by the second/OAuth configuration.
I want to build a spring cloud infrastructure with several oauth2 resource servers supporting multiple identity provider (like google, facebook, github, ....) and also one self implemented authorization mechanism.
Oauth2 Resource Server example
Security config
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class Oauth2ResourceServerConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
}
application.yml
security:
oauth2:
resource:
user-info-uri: https://api.github.com/user
As you can see this example is using github as identity provider and its working fine.
Oauth2 Authorization Server example:
Config
#Configuration
#EnableAuthorizationServer
public class Oauth2AuthorizationServerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("USER", "ADMIN");
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}
application.yml
security:
oauth2:
client:
client-id: user
client-secret: user
authorized-grant-types: password,client_credentials,authorization_code,refresh_token
scope: read,write
I can change my oauth2 resource server to use the authorization server:
security:
oauth2:
resource:
#user-info-uri: https://api.github.com/user
user-info-uri: http://${AUTH_HOST:localhost}:${AUTH_PORT:9000}/user
And it works just fine.
But what do I have to do if I now want to use both of them, github and my own authorization server?
Do I simply need a different configuration in my Oauth2 resource server to provide multiple user-info-uri or do I have to do more?
Can I extend my own authorization service to support github & co?
I would prefer not to use Auth0 and Co because I really dont like to outsource the most important part of my application: security. Even though I would like to try it. But I could not find any working example so far for auth0 + spring cloud gateway + token authentication for all underlying services.
I've been working on securing a Restful Service using Spring Security Oauth. I've been banging my head trying to secure the /oauth/token endpoint using SSL and only allowing for POST calls.
I'm using #EnableAuthorizationServer which states
Convenience annotation for enabling an Authorization Server (i.e. an
AuthorizationEndpoint and a TokenEndpoint) in the current application
context, which must be a DispatcherServlet context. Many features of
the server can be customized using #Beans of type
AuthorizationServerConfigurer (e.g. by extending
AuthorizationServerConfigurerAdapter). The user is responsible for
securing the Authorization Endpoint (/oauth/authorize) using normal
Spring Security features (#EnableWebSecurity etc.), but the Token
Endpoint (/oauth/token) will be automatically secured using HTTP Basic
authentication on the client's credentials. Clients must be registered
by providing a ClientDetailsService through one or more
AuthorizationServerConfigurers.
Which is great, but I can't seem to override the token endpoint piece or enforce POST-only calls, like with the intercept-url xml syntax
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore()
}
#Autowired
AuthenticationManager authenticationManager
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient('testApp')
.scopes("read", "write")
.authorities('ROLE_CLIENT')
.authorizedGrantTypes("password","refresh_token")
.secret('secret')
.accessTokenValiditySeconds(7200)
}
}
I secured my Resource server with
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.requiresChannel().anyRequest().requiresSecure()
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
}
}
Is there a similar builder syntax for the Authorization Servers TokenEndpoint security that uses requiresChannel?
I ended up creating my own config using
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration
Since i'm using Spring boot I just autowired the SecurityProperties and added this line for the SSL on the Oauth endpoints
if (this.security.isRequireSsl()) {
http.requiresChannel().anyRequest().requiresSecure();
}
And for the POST requirement
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,tokenEndpointPath).fullyAuthenticated()
.antMatchers(HttpMethod.GET,tokenEndpointPath).denyAll()
Afterwards removed the #EnableAuthorizationServer so it would use my config.