Im trying to split the resource server from the authorization server in spring-boot. I have two different applications that i'm running separately. In the authorization server i can get the bearer token from oauth/token but when i'm trying to get access to the resource(sending the token in header) i'm getting an invalid token error. My intention is to use the InMemoryTokenStore and the bearer token. Can anyone tell me what is wrong in my code?
Authorization Server:
#SpringBootApplication
public class AuthorizationServer extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServer.class, args);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("hasAuthority('ROLE_USER')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("user")
.secret("password")
.authorities("ROLE_USER")
.authorizedGrantTypes("password")
.scopes("read", "write")
.accessTokenValiditySeconds(1800);
}
}
Resource Server:
#SpringBootApplication
#RestController
#EnableOAuth2Resource
#EnableWebSecurity
#Configuration
public class ResourceServer extends WebSecurityConfigurerAdapter {
public static void main(String[] args){
SpringApplication.run(ResourceServer.class, args);
}
#RequestMapping("/")
public String home(){
return "Hello Resource World!";
}
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId("user");
tokenServices.setClientSecret("password");
tokenServices.setTokenName("tokenName");
tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
return tokenServices;
}
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(tokenService());
return authenticationManager;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/","/home")
.and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
TokenStore tokenStore = new InMemoryTokenStore();
resources.resourceId("Resource Server");
resources.tokenStore(tokenStore);
}
}
You have created 2 instances of InMemoryTokenStore. If you want to share tokens between the auth server and resource server they need the same store.
Related
I'm working on OAuth2 Authorization in Spring and try to implement authorization code grant flow. Now I have two applications. Client side and authorization server side. When I open secured /client/hello it redirect me to oauth2 login page, after that a get /oauth/authorize link, but in redirect_uri value always is login page on client side and it even doesn't change manually in browser. How I can change it? If i change redirect uri to /client/login in auth server config it redirects and gives me authorization code, but invokes unauthorized error.
Client
Controller:
#RestController
public class Controller {
#GetMapping("/hello")
public String hello() {
return "Hello world!!";
}
#GetMapping("/public")
public String publicPage() {
return "This is public!!";
}
#GetMapping("/callback")
public String login(#RequestParam("code") String code) {
return code;
}
}
Client security config:
#Configuration
#EnableOAuth2Sso
public class ClientSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error**", "/public**").permitAll()
.anyRequest().authenticated();
}
}
Client properties:
security.oauth2.client.client-id=007314
security.oauth2.client.client-secret=MDA3MzE0
security.oauth2.client.grant-type=password
security.oauth2.client.scope=read
security.oauth2.client.pre-established-redirect-uri=http://localhost:8081/client/public
security.oauth2.client.access-token-uri=http://localhost:8082/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8082/auth/oauth/authorize
security.oauth2.client.authentication-scheme=form
security.oauth2.resource.user-info-uri=http://localhost:8081/client/hello
security.oauth2.resource.id=resource-server-rest-api
server.port=8081
server.servlet.context-path=/client
Authorization Server
Server config:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
private final PasswordEncoder passwordEncoder;
#Qualifier("authenticationManagerBean")
private final AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServer(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager) {
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(passwordEncoder)
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("007314")
.secret(passwordEncoder.encode("007314"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read")
.resourceIds("resource-server-rest-api")
.autoApprove(true)
.redirectUris("http://localhost:8081/client/hello");
}
#Bean
public TokenStore tokenStore(){
return new JwtTokenStore(defaultAccessTokenConverter());
}
#Bean
public JwtAccessTokenConverter defaultAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.accessTokenConverter(defaultAccessTokenConverter())
.authenticationManager(authenticationManager);
}
}
Server security config:
#EnableWebSecurity
#Order(1)
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())
.withUser("qwerty")
.password(passwordEncoder().encode("12345"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error**", "/login**", "/oauth/authorize**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
Resource Server
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource-server-rest-api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/client/hello").access("#oauth2.hasScope('read')");
}
}
Server properties:
server.port=8082
server.servlet.context-path=/auth
Add also: security.oauth2.client.useCurrentUri=false into client.properties.
I am trying to secure REST api in my Spring Boot application by oAuth2 standard. I created classes extending AuthorizationServerConfigurerAdapter, ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter and unfortunately it works only with login scenario. /oauth/token request returns access_token etc, but in every other case I get 401, Unauthorized, Bad credentials. I thought that wrong cliendId/secret was the case, but it would also affect login, right?
P.S Application is connected to MySql database.
I already ran out of ideas, so maybe you guys are able to help me.
WebSecurityConfig.java :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthorizationServerConfig.java :
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("client-credentials", "password","refresh_token")
.authorities("ROLE_CLIENT", "ROLE_ANDROID_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(5000)
.secret("secret")
.refreshTokenValiditySeconds(50000);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}
ResourceServerConfig.java :
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated();
}
}
MainApplication.java :
#SpringBootApplication
#ComponentScan({"com.user.app"})
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
#Autowired
public void authenticationManaget(AuthenticationManagerBuilder builder, UserRepository repository) throws Exception {
builder.userDetailsService(new UserDetailsServiceImpl());
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ok, somehow oAuth2 configuration was overwritten by base configuration in some places. Those places still weren't protected by oAuth, so that's why "Bad Credentials" error was occurring even after providing proper access token. Commenting configure() method inside WebSecurityConfig solved the problem.
I'm slowly moving into understanding Spring Cloud Security. I've created an authorization service and it works when authorizing and returning a token, but doesn't return any current user details when using that token, when getting these from OAuth2Authentication. This two lines return an NPE:
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
OAuth2Authentication user isn't instantiated and is null, while I understood that it should be by default instantiated by Spring Security. Maybe I am missing some configuration beans? Thanks in advance!
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class AuthorizationServiceApplication {
#RequestMapping(value = {"/user"}, produces = "application/json")
public Map <String, Object> user (OAuth2Authentication user) {
Map <String, Object> userInfo = new HashMap <>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.class
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Value("${token.secret}")
private String secret;
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
public OAuth2Config (AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.class
#Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean () throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
// TODO: implemented DB stuff
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("deniss").password("deniss1").roles("USER")
.and()
.withUser("oksana").password("oksana").roles("USER, ADMIN");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository());
}
}
In the end I got it working like this:
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
public class AuthorizationServiceApplication {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping("/user")
public Principal user(Principal user) {
log.info("User information display for User: " + user.getName());
return user;
}
#Bean
UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("deniss").password("deniss").roles("USER").build());
return manager;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
//TODO: refactor to recieve this info from config server
#Value("${token.secret}")
private String secret;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}
SecurityConfigurer.class
#Configuration
#EnableGlobalAuthentication
public class SecurityConfigurer extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
// TODO: implemented DB stuff
#Override
public void init(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
}
I met the same problem, it seems a bug of new version. I changed Spring Boot 1.5.9.RELEASEļ¼Spring Cloud Edgware.RELEASE backed to Spring Boot 1.4.4.RELEASEļ¼Spring Cloud Camden.SR5, the problem disappeared.
Set security.oauth2.resource.filter-order=3 configuration property to restore the ordering used in previous versions. See enter link description here for more details.
iam getting this error message when build simple app
Getting user info from: http://localhost:9999/uaa/user
Could not fetch user details: class org.springframework.web.client.RestClientException, Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [text/html;charset=UTF-8]
however i sure the content type is json
this is the github repository https://github.com/ashraf-revo/oauth
this is server
#SpringBootApplication
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
}
#Order(1)
#Configuration
#EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("ashraf").password("ashraf").roles("user");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/user").authenticated().and().formLogin();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
class OAuth2ServerConfig {
private static final String RESOURCE_IDS = "revo";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_IDS);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/user").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
TokenStore tokenStore;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("revo")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("authorization_code", "implicit")
.authorities("ROLE_CLIENT")
.scopes("read", "write")
.secret("revo");
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
}
}
this is the client
#SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
#Configuration
#EnableOAuth2Sso
class SecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().mvcMatchers("/home").authenticated();
}
}
#Configuration
class MvcConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/home").setViewName("home");
}
}
you may consider spring-boot version and spring-cloud version,choose a not latest version,like spring-boot-1.4.4.RELEASE and spring-cloud-Camden.SR5;because i had same trouble and resolved by this way
If you can control the server response, modify it to set the Content-type to application/json.
Please try,
#RequestMapping(value = "/user", method = RequestMethod.GET,
produces = "application/json; charset=utf-8")
I'm following the basic Spring Boot OAuth2 example from Dave Syer: https://github.com/dsyer/sparklr-boot/blob/master/src/main/java/demo/Application.java
#Configuration
#ComponentScan
#EnableAutoConfiguration
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// Just for laughs, apply OAuth protection to only 2 resources
.requestMatchers().antMatchers("/","/admin/beans").and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("sparklr");
}
}
#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")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("sparklr")
.accessTokenValiditySeconds(60)
.and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.resourceIds("sparklr")
.redirectUris("http://anywhere?key=value")
.and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT")
.scopes("read")
.resourceIds("sparklr")
.secret("secret");
// #formatter:on
}
}
}
The example works very well for both types of grants, but the password grant uses the Spring Boot default security user (the one that echo's out "Using default security password: 927ca0a0-634a-4671-bd1c-1323a866618a" during startup).
My question is how do you override the default user account and actually rely on a WebSecurityConfig? I've added a section like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder)
throws Exception {
authManagerBuilder.inMemoryAuthentication().withUser("user")
.password("password").roles("USER");
}
}
But it does not seem to override the default Spring user/password even though the documentation suggests that it should.
What am I missing to get this working?
As I'm still on 2.0.3, I tried a few more things and this appears to be working:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER").and()
.withUser("admin1").password("password1").roles("ADMIN");
}
#Bean
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
By explicitly defining the authenticationManager bean, the built-in user authentication went away and it started relying on my own inMemoryAuthentication. When 2.0.4 is released, I'll re-evaluate the solution that Dave posted above as it looks like it will be more elegant.
#Configuration
protected static class AuthenticationManagerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("min").password("min").roles("USER");
}
}