Spring Security – Implement oauth2 SSO
I want to implement a central authentication system with Spring Security and OAuth2 SSO. In other words, I have a Spring Boot application responsible for authorization and a simple client. My customer has a rest API. First, I get the token from the authorization server and then send a request to the client API where the authorization header contains the bearer token from the above request. But this request always logs me in to the page.
Here is the server-side and client-side implementation:
Server
AuthorizationServerConfig.java
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret("{noop}secret")
.authorizedGrantTypes("password")
.scopes("user_info")
.autoApprove(true);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
App configuration:
@SpringBootApplication
@EnableResourceServer
public class ApplicationConfig extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
Security configuration:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this is just example
auth.inMemoryAuthentication().withUser("user").password("{noop}1234").roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/token")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
application.yml:
server:
port: 8900
servlet:
context-path: /auth
Client:
App configuration:
@SpringBootApplication
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
Security configuration:
@Configuration
@EnableOAuth2Sso
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
Test Controller:
@RestController
public class HomeController {
@GetMapping("/")
public String index() {
return "home";
}
@RequestMapping("/admin")
public String admin() {
return "admin";
}
}
application.yml:
server:
port: 9000
servlet:
context-path: /client1
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8900/auth/oauth/token
userAuthorizationUri: http://localhost:8900/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8900/auth/user/me
First, I sent the client_id and password along with the username, password, and grant_type to localhost:8900/auth/oauth/token
and got the following result:
{
"access_token": "603b505f-e701-43d0-b8b8-976a2178f7ea",
"token_type": "bearer",
"expires_in": 43199,
"scope": "user_info"
}
Now, I get the token above and send a request to localhost:9000/client1/admin
header contains the above tags. But the client application seems to ignore the header and display the server login page as a result. How do I fix this?
Solution
@EnableOAuth2Sso
is a comment that uses OAuth 2.0 as an end-user authentication mechanism (for example, a “A Login with Google” button). This annotation connects your app to a login page that redirects to the authorization server, where you will sign in and then redirect back to your app.
If this is your intention, then you need to update your Authorization Server to Support the authorization_code
grant flow instead of the password
authorization process.
However, if your client is strictly a REST API, you are more likely to need to connect to the client using @EnableResourceServer
rather than @EnableOAuth2Sso
. A Resource Server authorizes tokens through the Authorization HTTP header.