Spring OAuth #EnableResourceServer preventing login page from OAuth server - spring

Browser Response for localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.com is 302 Found, but response for localhost:9999/uaa/login is 401 Unauthorized.
I could get the login token prior to adding the #EnableResourceServer. I am using Spring boot and extending WebSecurityConfigurerAdapter to use authentication Manager with data source. When I tried to add a ResourceServerConfigurerAdapter it wouldn't build. What is the easiest way to allow the login page?
#SpringBootApplication
#RestController
#EnableResourceServer
public class OAuthSvcApplication extends WebMvcConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(OAuthSvcApplication.class);
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
public static void main(String[] args) {
SpringApplication.run(OAuthSvcApplication.class, args);
}
}
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource, Environment env)
throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.checkTokenAccess("hasAuthority('USER')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code",
"refresh_token", "password").scopes("openid");
}
}
}

SpringSecurityFilterChain should always be ordered before other filters.
If you want to add your own authentication for all or some endpoints the best thing to do is add your own WebSecurityConfigurerAdapter with lower order. Modifying the WebSecurityConfigurerAdapter subclass as follows allows the ResourceServer to work with a jdbc authentication mgr:
#Configuration
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager).jdbcAuthentication().dataSource(dataSource);
}
}

Related

url based custom mutiple auth providers calls always one provider only

I have configured multiple custom auth providers,using auth2 and spring boot, but it always executes the CustomInternalAuthenticationProvider only.can you please explain the how to apply ant matcher rules in order?i have used two WebSecurityConfigurerAdapter classes and one is orderded and one is default.guide me on how to handle the antmatcher rules properly?
#EnableResourceServer
#EnableWebSecurity
public class WebSecurityConfig{
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("#order");
http.antMatcher("/../main/**")
.requestMatchers()
.antMatchers("/","/login*", "/oauth/authorize**","/exit","**/logout")
.and().authenticationProvider(daoInternalAuthenticationProvider())
.formLogin().loginPage("/login")
;
}
#Bean
public AuthenticationProvider daoInternalAuthenticationProvider() throws Exception {
return new CustomInternalAuthenticationProvider();
}
}
#Configuration
public static class ApiTokenSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("default");
http.antMatcher("/../user/**")
.requestMatchers()
.antMatchers("/","/login*", "/oauth/authorize**","/exit","**/logout")
.and() .authenticationProvider(daoExternalAuthenticationProvider())
.formLogin().loginPage("/login")
;
}
#Bean
public AuthenticationProvider daoExternalAuthenticationProvider() throws Exception {
return new CustomExternalAuthonticationProvider();
}
}

Problems Injecting Custom Authentication Manager

I'm attempting to use a custom authentication manager but the standard Provider manager is being called to .authenticate. I suspect it has something to do with either the AuthSever or Web Config. Any help is greatly appreciated.
AuthServer configuration:
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private final DataSource dataSource;
#Autowired
public AuthServerConfig(DataSource dataSource){
this.dataSource = dataSource;
}
#Autowired
MicrosJwtConfig microsJwtConfig;
#Autowired
#Qualifier("microsProviderManager")
AuthenticationManager authenticationManager;
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder(10);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenServices(microsJwtConfig.microsTokenServices())
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.allowFormAuthenticationForClients();
security.passwordEncoder(encoder());
security.tokenKeyAccess("permitAll()");
}
}
WebSecurity config:
#EnableWebSecurity
#Configuration
public class WebSecConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private MECAuthenticationProvider mecAuthenticationProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new MicrosProviderManager(clientDetailsService, mecAuthenticationProvider );
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("actuator/health").permitAll().and()
.authorizeRequests().antMatchers("oauth/token").permitAll().and()
.authorizeRequests().antMatchers("actuator/info").permitAll();
}
}

How to configure oAuth2 when Authorization Server is also the Resource server

I'm trying to setup a very basic oAuth2 authentication in spring boot 2.x.x using either authorization code grant or implicit grant but I can't seem to access the Resource server (which resides in the same spring boot app as the Authorization server) after the token is obtained.
Following is the configuration of WebSecurityConfigurerAdapter
#EnableWebSecurity
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String[] IGNORE_URIS = {
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**",
"/resources/**",
"/h2-console/**",
"/common/**",
"/configuration/ui",
"/configuration/security",
"/error"
};
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(IGNORE_URIS);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/product/**")
.hasAnyRole("ADMIN").and()
.httpBasic().and().formLogin().and().authorizeRequests().anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN");
}
#Bean
public PasswordEncoder bCrypt() {
return new BCryptPasswordEncoder();
}
And the AuthorizationServerConfigurerAdapter
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfiguration(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("my-client-id")
.authorizedGrantTypes("authorization_code", "implicit")
.authorities("ADMIN")
.scopes("all")
.resourceIds("product_api")
.secret("{noop}secret").redirectUris("https://google.com").accessTokenValiditySeconds(0);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
So far so good. I am able to reach the default Spring login page by typing the following Url in the browser.
http://localhost:8080/oauth/authorize?response_type=token&client_id=my-client-id&redirect_uri=https://google.com
Then The login page shows up and I enter my credentials.
After I log in I can then grant access to "my-client-id" app.
Eventually after I approve the app I can see the newly generated access token in the URL bar of the browser which is something like this.
https://www.google.com/#access_token=f2153498-6a26-42c6-93f0-80825ef03b16&token_type=bearer&scope=all
My question is that All of this flow won't work when I also configure a Resource Server.
#EnableResourceServer
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("product_api");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/**")
.and().authorizeRequests()
.antMatchers("/**").permitAll();
}
}
What am I doing wrong? When I try to access the oauth/authorize url as before I get the following:
Why? How can one access the login page and retrieve the token? What Am I missing?
You need to use
#Order
Annotation to specify order for WebMvc and ResourceServer classes
#EnableWebSecurity
#Configuration
#Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
and for Resource Server
#EnableResourceServer
#Configuration
#Order(2)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
...
}
If you want to see workable example, you can check it here https://github.com/alex-petrov81/stackoverflow-answers/tree/master/auth-server-also-resource
I've created it from your code example.

Combine Spring HTTP Basic Authentication and Access Token

How to combine Spring HTTP Basic Authentication and Access Token for both would work simultaneously? In my case only configuration with Order(1) does works.
I want that all */api**/* would be accessed only for users with token and */web**/* would be accessed only for login users.
WebSecurityConfig.java
#Configuration
#EnableWebMvcSecurity
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/web/**", "/gopr").authenticated().and().authorizeRequests()
.and()
.formLogin().loginPage("/login").permitAll()
.defaultSuccessUrl("/gopr", true).permitAll().and().logout().logoutSuccessUrl("/login").permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
Application.java
#SpringBootApplication
#EnableResourceServer
#Order(2)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.secret("password")
.accessTokenValiditySeconds(600);
// #formatter:on
}
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/web/**", "/login", "/index", "/").permitAll()
.antMatchers("/api/**").authenticated();
/* antMatchers("/web/**", "/gopr").permitAll().antMatchers("/api/**").authenticated(); */
}
}
}
Always use 'requestMatchers()' when creating security filters. This way when multiple filter chains are created, only the first filter chain will not be used.
Modify both your WebSecurityConfig.java as :
#Configuration
#EnableWebMvcSecurity
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/web/**", "/gopr")
.and()
.authorizeRequests().antMatchers("/web/**", "/gopr").authenticated().
.and()
.formLogin().loginPage("/login").permitAll()
.defaultSuccessUrl("/gopr", true).permitAll().and().logout().logoutSuccessUrl("/login").permitAll();
}
...
}
and your ResourceServer inner class as :
#Configuration
#EnableResourceServer
protected static class ResourceServer extends
ResourceServerConfigurerAdapter {
...
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests().antMatchers("/api/**").authenticated();
}
}
Reference : https://github.com/royclarkson/spring-rest-service-oauth/issues/11

Spring security form logging and outh2 in same app

I have written a sample spring application which have some rest services protected using spring-security-oauth2. Now I want to move these services to the original application which uses spring security form login.
In the original application I want rest services to be protected from spring-security-oauth2 and other spring controllers to be protected using a form login. What I want to know is, is this approach is right or wrong, if right, how could I complete this action.
This is Sample app codes, which uses ouath2,
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService; // Is this really needed?
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources.resourceId(RESOURCE_ID);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
// http.authorizeRequests().antMatchers("/test").not().permitAll();
http.authorizeRequests().antMatchers("/test").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
// #Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// #formatter:off
endpoints.tokenStore(this.tokenStore).authenticationManager(this.authenticationManager);
// #formatter:on
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory().withClient("clientapp").authorizedGrantTypes("password", "refresh_token").authorities("USER")
.scopes("read", "write").resourceIds(RESOURCE_ID).secret("123456");
// #formatter:on
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
}
Here is a part of the original app configuration.
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyStaysureSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.inMemoryAuthentication().withUser("mycompany").password("mypsswd").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/*", "/api-docs/**").permitAll().antMatchers("/**").authenticated().and().formLogin().defaultSuccessUrl("/dashboard").and().csrf().disable();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Spring Security is built on an ordered list of filter chains, and for each request the first one with a matching path handles the authentication. You have 3 filter chains in your combined app, one created by #EnableAuthorizationServer (with default order=0), one created by #EnableResourceServer (with default order=3), and one created by your MyStaysureSecurityConfiguration (also with order=0). You aren't allowed to have 2 filters with the same order so you need to re-arrange them and give them request matchers that make sense for your use case. Maybe you didn't need the #EnableAuthorizationServer anyway (it was unclear from the question)? In any case it is pretty simple - you have 2 choices (roughly):
exclude the oauth2 resources from the request matchers in your MyStaysureSecurityConfiguration and allow them to be handled by the resource server filter.
re-order the resource server filter to a lower order and give it a request matcher that only matches the oauth2 resources.

Resources