I'm trying to wrap my head around OAuth2 and Spring Security OAuth, especially OAuth Provider service. I'm trying to implement the following:
All these modules are independent from each other, i.e. separated in different projects and will be hosted on different domains, such as (1) http://oauth.web.com, (2) http://rest.web.com, (3) http://web.com
My two questions are:
A. How do I implement a Web Client project so that when user is logging in on the protected page or clicks on the Login button, be redirected to OAuth Provider url, login, and be authenticated on the Web Client with all user roles and also as well need to know which client was used. @EnableResourceServer
(the same way Resource Server is implemented; see code below) in this project to get user's details? Do I have to manage Access Token and always include it in the call to the Resource Server or it can be done somehow automatically?
B. What is the best way to implement security on the mobile apps that I'll be developing. Should I be using password grand for this authentication, since the apps will be build by me where I'll have a user name and password be in the native screen and then be send to the server as a basic authentication over SSL? Are there any samples that I can take a look at that talk to Spring Security OAuth and return user details.
Here is my implementation of OAuth Project (1) and Resource Project (2):
OAuth2 Server Configs (most of the code was taken from HERE)
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
DataSource dataSource;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.approvalStore(approvalStore())
.authorizationCodeServices(authorizationCodeServices())
;
}
@Bean
public JdbcClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("permitAll()");
}
}
Web Security Config
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // TODO. Enable this!!!
http.authorizeRequests()
.and()
.formLogin()
// .loginPage("/login") // manually defining page to login
// .failureUrl("/login?error") // manually defining page for login error
.usernameParameter("email")
.permitAll()
.and()
.logout()
// .logoutUrl("/logout")
.logoutSuccessUrl("/")
.permitAll();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customUserDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
UserDetailsService (customUserDetailsService)
@Service
public class CustomUserDetailsService implements UserDetailsService{
private final UserService userService;
@Autowired
public CustomUserDetailsService(UserService userService) {
this.userService = userService;
}
public Authority loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userService.getByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User with email=%s was not found", email)));
return new Authority(user);
}
}
Configuration (most of the skeleton code was take from THIS example)
@Configuration
@EnableResourceServer
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter{
@Autowired
DataSource dataSource;
String RESOURCE_ID = "data_resource";
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
TokenStore tokenStore = new JdbcTokenStore(dataSource);
resources
.resourceId(RESOURCE_ID)
.tokenStore(tokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
// For some reason we cant just "permitAll" OPTIONS requests which are needed for CORS support. Spring Security
// will respond with an HTTP 401 nonetheless.
// So we just put all other requests types under OAuth control and exclude OPTIONS.
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")
.and()
// Add headers required for CORS requests.
.headers().addHeaderWriter((request, response) -> {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
}
});
}
}
WS Controller:
@RestController
@RequestMapping(value = "/todos")
public class TodoController {
@Autowired
private TodoRepository todoRepository;
@RequestMapping(method = RequestMethod.GET)
public List<Todo> todos() {
return todoRepository.findAll();
}
// other methods
}
How do I implement a Web Client project so that when user is logging in on the protected page or clicks on the Login button, be redirected to OAuth Provider url, login, and be authenticated on the Web Client with all user roles and also as well need to know which client was used
You want to use OAuth as SSO.
Option 1, use spring cloud https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v
Option 2, manually handle SSO process:
in your web client, configure the login page to OAuth server using authorization grant.
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // TODO. Enable this!!!
http.authorizeRequests()
.and()
.formLogin()
.loginPage("http://oauth.web.com/oauth/authorize?response_type=code&client_id=webclient&redirect_uri=http://web.com") // manually defining page to login
//.failureUrl("/login?error") // manually defining page for login error
.usernameParameter("email")
.permitAll()
.and()
.logout()
//.logoutUrl("/logout")
.logoutSuccessUrl("/")
.permitAll();
}
after authentication and authorization process finished, you will be redirected to web client with authorization code http://web.com/?code=jYWioI
. your web client should exchange this code with token access on your oauth server.
On your oauth server, create an endpoint for retrieving user information
@RestController
public class UserRestService {
@RequestMapping("/user")
public Principal user(Principal user) {
// you can also return User object with it's roles
// {"details":...,"principal":{"username":"user",...},"name":"user"}
return user;
}
}
Then, your web client can access the user details information by sending request with the token access to the above rest endpoint and authenticate the user based on the response.
Do I have to manage Access Token and always include it in the call to the Resource Server or it can be done somehow automatically?
Every request must include token access. If you want to do it automatically, spring has provide Oauth 2 client http://projects.spring.io/spring-security-oauth/docs/oauth2.html
What is the best way to implement security on the mobile apps that I'll be developing. Should I be using password grand for this authentication, since the apps will be build by me where I'll have a user name and password be in the native screen and then be send to the server as a basic authentication over SSL?
Since you are using native screen, password grant is enough, but you can store the refresh token, this will enable you to request token access without repeating the authentication process.
Are there any samples that I can take a look at that talk to Spring Security OAuth and return user details.
see sample code above or take a look at this https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v