Decouple Authorisation server and resource server with RemoteTokenServices - oauth

I am trying spring security and spring oauth2 in my project and have separated my authorization server and resource server. I didn't want to share a token store between these two servers so I decided to use RemoteTokenServices and the check_token endpoint. Everything was fine except when I used an access token to query the resource server, I got "401 Unauthorized" error as follows:
2015-10-19 11:50:10.291 DEBUG 2590 --- [nio-8080-exec-1] o.s.web.client.RestTemplate : POST request for "http://localhost:9080/uaa/oauth/check_token/" resulted in 401 (Unauthorized); invoking error handler
2015-10-19 11:50:10.293 DEBUG 2590 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2015-10-19 11:50:10.293 DEBUG 2590 --- [nio-8080-exec-1] o.s.web.filter.RequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade#41f4867a
2015-10-19 11:50:10.297 ERROR 2590 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[jerseyServlet] : Servlet.service() for servlet [jerseyServlet] in context with path [] threw exception
org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
The code for the Authorisation server:
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Bean
public DefaultAccessTokenConverter defaultAccessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(this.tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(defaultAccessTokenConverter());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
And the security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication();
// .withUser("John").roles("ADMIN").password("password")
// .and()
// .withUser("Mary").roles("BASIC").password("password");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated()
.and().httpBasic().realmName("OAuth Server");
http.csrf().disable();
}
}
The Resource Server is set up as follows:
#Configuration
#EnableResourceServer
public class ResourceConfiguration extends ResourceServerConfigurerAdapter {
private static String RESOURCE_ID = "xn-resource-id";
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
public RemoteTokenServices remoteTokenServices(final #Value("${auth.server.url}") String checkTokenUrl,
final #Value("${auth.server.client_id}") String clientId,
final #Value("${auth.server.client_secret}") String clientSecret) {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl);
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
}
I tested the security settings with curl and used client_credentials grant type.
Does anyone help me figure out what's the issue with the above code?

Looks like you are using incorrect url. Try to repleace it with:
http://localhost:9080/uaa/oauth/check_token
(notice that url is not ended with /)

Related

Single Sign On with Spring Security OAuth2 and JWT

I am trying to implement Single Sign On with Spring Security OAuth2 and JWT.
I use two separate applications:
An Authorization Server – which is the central authentication mechanism
Client Application: the applications using SSO
When a user tries to access a secured page in the client app, they’ll be redirected to authenticate first, via the Authentication Server.
And I am using the Authorization Code grant type out of OAuth2 to drive the delegation of authentication.
Authorization server:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
public static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationServerConfigurerAdapter.class);
#Autowired
private AuthenticationManager authenticationManager;
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("abcd");
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
#Override
public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
clientDetailsServiceConfigurer
.inMemory()
.withClient("webapp")
.secret("Pass")
.authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code")
.scopes("user_info")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager);
}
}
Security Configuration on Authorization Server
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${ldap.url}")
private String ldapUrl;
#Value("${ldap.userDnPatterns}")
private String ldapUserDnPatterns;
#Autowired
private PersonService personService;
#Autowired
private RoleService roleService;
#Override
protected void configure(HttpSecurity http) throws Exception { // #formatter:off
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
} // #formatter:on
#Bean(name = "authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(this.ldapAndDatabaseAuthenticationProvider());
}
#Bean(name="ldapAuthenticationProvider")
public AuthenticationProvider ldapAndDatabaseAuthenticationProvider(){
LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper();
userDetailsMapper.setRoleAttributes(new String[]{"groupMembership"});
LdapAndDatabaseAuthenticationProvider provider =
new LdapAndDatabaseAuthenticationProvider(
this.ldapAuthenticator(),
this.ldapAuthoritiesPopulator(),
this.personService);
provider.setUserDetailsContextMapper(userDetailsMapper);
return provider;
}
#Bean( name = "ldapAuthoritiesPopulator" )
public LdapAndDatabaseAuthoritiesPopulator ldapAuthoritiesPopulator(){
return new LdapAndDatabaseAuthoritiesPopulator(this.contextSource(), "", roleService);
}
#Bean( name = "ldapAuthenticator" )
public LdapAuthenticator ldapAuthenticator() {
BindAuthenticator authenticator = new BindAuthenticator( this.contextSource() );
authenticator.setUserDnPatterns(new String[]{"cn={0},ou=prod,o=TEMP"});
return authenticator;
}
#Bean( name = "contextSource" )
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource =
new DefaultSpringSecurityContextSource( ldapUrl );
return contextSource;
}
}
application.properties:
server.port=8888
server.context-path=/auth
security.basic.enabled=false
When I login the client application, It correctly forwards to Authorization Server for Single Sign On.
I enter the user credentials. User successfully get authenticated, but then I see the below error on browser:
OAuth Error
error="invalid_grant", error_description="A redirect_uri can only be
used by implicit or authorization_code grant types."
URL Shows:
http://localhost:8888/auth/oauth/authorize?client_id=webapp&redirect_uri=http://localhost:8080/jwt/webapp&response_type=code&state=LGvAzj
I also see the below at the log:
02:14:43.610 [http-nio-8888-exec-6] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping/getHandlerInternal Looking up handler method for path /oauth/authorize
02:14:43.614 [http-nio-8888-exec-6] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping/getHandlerInternal Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.Str ing>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)]
02:14:43.849 [http-nio-8888-exec-6] INFO o.s.s.o.p.e.AuthorizationEndpoint/handleOAuth2Exception Handling OAuth2 error: error="invalid_grant", error_description="A redirect_uri can only be used by implicit or authorization_code grant types."
Can you please help me to find the problem?
UPDATE
Actually, Dur is right. This configuration is correct and works fine. I had another configuration file which configures JdbcClientDetails and it was overwriting the clientDetailsService created with inmemory in this configuration.

spring boot OAuth2 role based authorization

We have a dedicated authorization server extending AuthorizationServerConfigurerAdapter, where we have set authorities overriding void configure(ClientDetailsServiceConfigurer clients) method.
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Value('${oauth.clientId}')
private String clientId
#Value('${oauth.secret:}')
private String secret
#Value('${oauth.resourceId}')
private String resourceId
#Autowired
#Qualifier('authenticationManagerBean')
private AuthenticationManager authenticationManager
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("permitAll()")
oauthServer.allowFormAuthenticationForClients()
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter())
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.secret(secret)
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("USER", "ADMIN")
.scopes("read", "write", "trust")
.resourceIds(resourceId)
}
Now how to use the authorities in the resource server for role based authorization.
We are able to authenticate via authorization server generated token.
Need help.
In the resource server you should extend the ResourceServerConfigurerAdapter to configure the requestMatchers and set the role for each resource.
#Configuration
#EnableResourceServer
public class OAuth2Config extends ResourceServerConfigurerAdapter {
#Value("${keys.public}")
private String publicKey;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/**")
.and()
.authorizeRequests()
.antMatchers("/service1/**").access("#oauth2.hasScope('ADMIN')")
.antMatchers("/service2/**").access("#oauth2.hasScope('USER')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
tokenConverter.setVerifierKey(publicKey);
return tokenConverter;
}
}
You have received a token from the auth server. You can now use that token to make another request to the auth server to retrieve the user object. This json object would contain roles(authority).
The request would look like as follows.
curl -H "Authorization: Bearer 2a953581-e9c9-4278-b42e-8af925f49a99"
http://localhost:9999/uaa/user
In order to do this, you need to create user service endpoint and implement UserDetailsService also.
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#Bean
UserDetailsService userDetailsService.....
The role list is created and set in the org.springframework.security.core.userdetailsin the UserDetailsService.User as follows.
AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN"));

usernamepasswordauthenticationfilter not getting invoked in spring security with oauth2 and formlogin

Trying to make spring security oauth2 and form login work in a spring boot application.
I took cues from
https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2/sparklr
and
https://github.com/royclarkson/spring-rest-service-oauth/issues/11
Below is my configuration
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
#Inject
private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Inject
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/scripts/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/i18n/**")
.antMatchers("/assets/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/api/register")
.antMatchers("/api/activate")
.antMatchers("/api/account/reset_password/init")
.antMatchers("/api/account/reset_password/finish")
.antMatchers("/api/home/**")
.antMatchers("/api/product/**")
.antMatchers("/test/**")
.antMatchers("/devadmin/**")
.antMatchers("/signin")
.antMatchers("/static/api-guide.html");
}
#Override
#Order(Ordered.HIGHEST_PRECEDENCE)
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable().authorizeRequests().antMatchers("/web/**","/for","/admin","/here").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/web/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.logoutUrl("/web/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.exceptionHandling()
;
}
#Configuration
#EnableAuthorizationServer
#EnableConfigurationProperties(SecurityConfigurationProperties.class)
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Inject
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Inject
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
#Order(2)
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().antMatchers("/api /**").access("#oauth2.hasScope('read') and hasRole('ROLE_USER')");
// #formatter:on
}
}
}
With above configuration oauth2 login works fine when I try authenticating via
/oauth/token
However login via
/web/authentication
always shows
405 Request method 'POST' not supported
usernamepasswordauthenticationfilter is not getting invoked.
After commenting the ResourceServerConfiguration part in above code,
form login via
/web/authentication
is working fine.
Also I can see usernamepasswordauthenticationfilter being invoked from the logs.
My question is that even the end points are configured different for oauth2 resource server and form login, why oauth2 is overriding the httpsecurity for formlogin and why not usernamepasswordauthenticationfilter being called when resource server is configured?

Spring Oauth2 separate resource server configuration

I am trying to configure separate auth and resource servers for oauth2.
I am able to configure authrization server successfully and able to authenticate and generate access tokens. Now I want to configure a resource server which can talk to auth server with api end point to validate the access tokens.
Below is my resource server configuration.
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class Oauth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Oauth2SecurityConfiguration before");
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/v1/**").authenticated();
System.out.println("Oauth2SecurityConfiguration after");
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9000/authserver/oauth/check_token");
remoteTokenServices.setClientId("clientId");
remoteTokenServices.setClientSecret("clientSecret");
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
#Override
#Bean
public AuthenticationManager authenticationManager() throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(remoteTokenServices());
return authenticationManager;
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
System.out.println("http.csrf().disable()");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/v1/**").fullyAuthenticated();
System.out.println("http.authorizeRequests().anyRequest().authenticated()");
}
}
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
Question :
1. why I AuthenticationManager at resource server while all the authentication is delegated to auth server. (I had to add it to load application context)
Apart from this I am facing below issues.
Even though I am not passing authorization headers and access token with the request. It's going through.
http GET "http://localhost:8080/DataPlatform/api/v1/123sw/members"
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Mon, 19 Oct 2015 19:45:14 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"entities": [],
"errors": [],
"message": null
}
The filters are only invoked at once I don't see the logs for following requests. Does it cache authorization somewhere?
I am new to spring oauth Please let me know if I am doing anything wrong. I am using
spring-security-oauth2 : 2.0.7.RELEASE
spring-security-core : 4.0.1.RELEASE
java : 1.8
The main point make separate endpoints for auth-server and resource-server, that they can services them separately, each his own.
As show bellow "/user/getEmployeesListRole/**" -access through auth-server, "/user/getEmployeesListOAuth2/**" - access through resource-server by token, which generated by aouth2-server.Also please pay attention that auth-server and oauth2-server has the same auth-manager
Configuration of spring-boot aouth2-server, resource-server, auth-server in one spring-boot app
1.Entry Point:/*AuthApplication.java*/
#SpringBootApplication
#EnableDiscoveryClient
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}}
2. Config of aouth2-server:/*OAuth2AuthorizationConfig.java*/
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("userDetailsServiceBean")
private UserDetailsService userDetailsService;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("browser")
.authorizedGrantTypes("password", "refresh_token")
.scopes("ui", "read:ui", "write:ui");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}}
2.1 aouth2-server auth-request [post with basic auth]:http://localhost:5000/uaa/oauth/token?grant_type=password&scope=ui write:ui&username=user&password=123456&client_id=browser3.Config resource-server:
/*ResourceServer.java*/
#Configuration
#EnableResourceServer
class ResourceServer extends ResourceServerConfigurerAdapter {
//Here we specify to allow the request to the
// url /user/getEmployeesList with valid access token and scope read
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/user/getEmployeesList/**")
.antMatchers("/user/getEmployeesListOAuth2/**")
.and().authorizeRequests().anyRequest().access("#oauth2.hasScope('ui')");
}}
4. Config auth-server:
/*WebSecurityConfig.java*/
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/getEmployeesListRole/**")
.access("hasAuthority('WRITE_DATA') && hasAuthority('READ_DATA')")
.anyRequest().permitAll()
.and().formLogin().permitAll()
.and().logout().permitAll()
.and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin")
.password("admin")
.authorities("WRITE_DATA", "READ_DATA");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
You don't need #EnableWebSecurity on Oauth2SecurityConfiguration #EnableResourceServer is enough.
You should also replace extends WebSecurityConfigurerAdapter with extends ResourceServerConfigurerAdapter.
If you want to use your RemoteTokenServices instance I recommend you override ResourceServerConfigurerAdapter public void configure(ResourceServerSecurityConfigurer resources) throws Exception with
#Override
public void configure( ResourceServerSecurityConfigurer resources ) throws Exception
{
resources.tokenServices( serverConfig.getTokenServices() );
}

Spring OAuth2, access ROLE with userInfoUri?

I'm in the process of building separate resource server and authhorization server. For now i'm using the user-info-uri in resource server to extract the Principal from the authorization server matching the access-token, with config:
spring:
oauth2:
resource:
userInfoUri: http://localhost:9999/uaa/user
In the resource server I have protected endpoints, based on role, as follows:
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
When I manually access the user-info-uri, I can see that the the authorities contain:
"authority": "ROLE_END_USER"
But when I try to access the /invoices resource I get an Access-Denied exception, and in the log I see:
OAuth2Authentication#bc5074a8: Principal: my-login; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER
Authoriteis = "ROLE_USER". Where does that come from, should'nt it be "ROLE_END_USER" at this point also?
I've seen implementations using a shared database for the storage of tokens, is that really necessary for what I want to achive?
Resource Server:
#SpringBootApplication
#EnableOAuth2Resource
public class EndUserResourceServiceApplication extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
}
public static void main(String[] args) {
SpringApplication.run(EndUserResourceServiceApplication.class, args);
}
}
Auth Server:
#SpringBootApplication
#RestController
#EnableResourceServer
public class ApiAuthServerApplication extends ResourceServerConfigurerAdapter {
#Configuration
#EnableWebSecurity
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Bean(name = "authenticationManagerBean")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(this.authenticationManager)
.userDetailsService(this.userDetailsService)
.tokenStore(this.tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.secret("our_s3cret")
.authorities("ROLE_CLIENT")
.authorizedGrantTypes("implicit", "password", "refresh_token")
.redirectUris("http://anywhere")
.scopes("read")
.autoApprove(true);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
public static void main(String[] args) {
SpringApplication.run(ApiAuthServerApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
}
Summary:
Can I use user-info-uri for validating access-tokens and to use "hasRole"
Is it necessary to use shared database for token storage when using separate resource server and authorization server?
I ended up using a shared token store, which works well at the moment

Resources