OAuth2.0 authentication with Spring Security, Angular and MySql

OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices. This blog post explains the implementation of OAuth2 authentication with Spring Boot, Spring Security and Angular.

OAuth Roles

OAuth defines four roles:

  • Resource Owner
  • Client
  • Resource Server
  • Authorization Server

Resource Owner: User

The resource owner is the user who authorizes an application to access their account. The application’s access to the user’s account is limited to the “scope” of the authorization granted (e.g. read or write access).

Resource / Authorization Server: API

The resource server hosts the protected user accounts, and the authorization server verifies the identity of the user then issues access tokens to the application.

Client: Application

The client is the application that wants to access the user’s account. Before it may do so, it must be authorized by the user, and the authorization must be validated by the API.

Workflow

Following diagram explains the process

OAuth2 Workflow
  1. User goes to http://localhost:4200 to access data and Angular redirects him to log in page
  2. The user enters credentials and clicks on login
  3. Angular sends those credentials to https://localhost:8090/oauth/token and Spring Security using OAuth2 tries to authenticate him
  4. If the credentials are valid, it returns the following response. And invalid credentials keeps him in the same page with the error message
{"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3","token_type":"bearer","expires_in":3600,"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk","scope":"create"}

5. Then Angular makes another request using access_token to fetch user information (This can be done in many ways)

6. Once Angular receives the response, it redirects him to the home page. He can access categories, orders, etc. based on the roles.

Technologies

  1. SpringBoot 2.x
  2. Spring Security 2.x
  3. OAuth2
  4. MySql database
  5. Angular 8

Setup Instructions

  1. Create the following tables on the MySQL database to store OAuth tokens
drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(255)
);

drop table if exists oauth_client_token;
create table oauth_client_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255)
);

drop table if exists oauth_access_token;
create table oauth_access_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication LONG VARBINARY,
refresh_token VARCHAR(255)
);

drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication LONG VARBINARY
);

drop table if exists oauth_code;
create table oauth_code (
code VARCHAR(255), authentication LONG VARBINARY
);

SET SQL_MODE='ALLOW_INVALID_DATES';

drop table if exists oauth_approvals;
create table oauth_approvals (
userId VARCHAR(255),
clientId VARCHAR(255),
scope VARCHAR(255),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);

drop table if exists ClientDetails;
create table ClientDetails (
appId VARCHAR(255) PRIMARY KEY,
resourceIds VARCHAR(255),
appSecret VARCHAR(255),
scope VARCHAR(255),
grantTypes VARCHAR(255),
redirectUrl VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(255)
);

2. Let’s call the resource server resource-server-rest-api and define two clients. Client secret encrypted with BCrypt with 4 rounds.

  • spring-security-oauth2-read-client (authorized grant types: read)
  • spring-security-oauth2-read-write-client (authorized grant types: read, write)

Go to the website and enter spring-security-oauth2-read-write-client-password1234 as password and rounds as 4 and to get the encrypted password. Make sure replace Encrypted password field in the following SQL query with your BCrypt password.

INSERT INTO OAUTH_CLIENT_DETAILS(CLIENT_ID, RESOURCE_IDS, CLIENT_SECRET, SCOPE, AUTHORIZED_GRANT_TYPES, AUTHORITIES, ACCESS_TOKEN_VALIDITY, REFRESH_TOKEN_VALIDITY) VALUES ('spring-security-oauth2-read-client', 'resource-server-rest-api','Encrypted Password','read', 'password,authorization_code,refresh_token,implicit', 'USER', 10800, 2592000);INSERT INTO OAUTH_CLIENT_DETAILS(CLIENT_ID, RESOURCE_IDS, CLIENT_SECRET, SCOPE, AUTHORIZED_GRANT_TYPES, AUTHORITIES, ACCESS_TOKEN_VALIDITY, REFRESH_TOKEN_VALIDITY) VALUES ('spring-security-oauth2-read-write-client', 'resource-server-rest-api','Encrypted Password','read,write', 'password,authorization_code,refresh_token,implicit', 'USER', 10800, 2592000);

3. Create OAuth2 AuthorizationServer that authorizes user requests. ClassAuthorizationServerConfig should extend AuthorizationServerConfigurerAdapter and override configure() methods.

  • tokenStore() bean sets Mysql JDBC store as TokenStore
  • configure(AuthorizationServerEndpointsConfigurer endpoints) method ties endpoints with TokenStore, AuthenticationManager, and UserDetailsService
  • configure(AuthorizationServerSecurityConfigurer oauthServer) method sets token to access and client password encryption algorithm.
  • configure(ClientDetailsServiceConfigurer clients) method sets the client’s data source

OAuth2AuthorizationServerConfig.java

@Configuration
@EnableAuthorizationServer
@Import(SecurityConfig.class)
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
{
private final AuthenticationManager authenticationManager;

@Qualifier("dataSource")
private final DataSource dataSource;

private final MyUserDetailsService myUserDetailsService;

private final PasswordEncoder oauthClientPasswordEncoder;

@Autowired
public OAuth2AuthorizationServerConfig(AuthenticationManager authenticationManager, DataSource dataSource, MyUserDetailsService myUserDetailsService, @Qualifier("oauthClientPasswordEncoder") PasswordEncoder oauthClientPasswordEncoder)
{
this.authenticationManager = authenticationManager;
this.dataSource = dataSource;
this.myUserDetailsService = myUserDetailsService;
this.oauthClientPasswordEncoder = oauthClientPasswordEncoder;
}

@Bean
public OAuth2AccessDeniedHandler oauthAccessDeniedHandler()
{
return new OAuth2AccessDeniedHandler();
}

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
{
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
clients.jdbc(dataSource);
}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
{
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).userDetailsService(myUserDetailsService);
}


@Bean
public TokenStore tokenStore()
{
return new JdbcTokenStore(dataSource);
}

}

4. SecurityConfig class secures the application with SpringSecurity which allows configuring Roles and Privileges

5. Define OAuth2CorsFilter bean in SecurityConfig and add it in configure() method as shown below. This forces Spring Security to use OAuth2 authentication before basic authentication

@Override
public void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(oAuth2CorsFilter, BasicAuthenticationFilter.class);
/*Standard Spring Security config
*/
...............................
}

6. The resource server takes the request sent by client and requests Authorization Server to validate the token. Define OAuth2ResourceServerConfig class which acts as Resource Server with annotation EnableResourceServer .

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter
{
@Override
public void configure(ResourceServerSecurityConfigurer resources)
{
resources.resourceId(ResourceConstants.RESOURCE_SERVER_REST_API);
}
}

7. Define MethodSecurityConfig class that extends GlobalMethodSecurityConfiguration class, which helps to use Spring Security annotations on top of the methods in controllers

8. Define OAuth2CorsFilterPasswordEncoders classes as shown in the code base

9. Define MyUserDetails class that extends Spring Security core UserDetails class

10. Define service MyUserDetailsService that implements Spring Security interface UserDetailsService

11. Define CustomDaoAuthenticationProvider class that authenticates provided credentials against database username and password.

12. Define CustomAuthenticationSuccessHandler class and override onAuthenticationSuccess() method, which specifies actions to be taken after authentication success and in a similar way implement other classes such as UsersAccessDecisionManagerCustomAuthenticationFailureHandler and CustomLogoutSuccessHandler

13. Clone the GitHub repository and run the application. This will create necessary tables like user, role, privilege, etc.

14. Insert the following user data into user, role, and privilege tables. Go to the website and enter admin as password and rounds as 12 and copy the encrypted password. Make sure replace the Encrypted Password field in the following SQL query with your BCrypt password.

/* Insert Data into User Table  */
INSERT INTO springsessiondemo.user VALUES(1,1,1,1,1,'Encrypted Password','admin');
INSERT INTO springsessiondemo.user VALUES(2,1,1,1,1,'Encrypted Password','user');


/* Insert Data into Role Table */
insert into springsessiondemo.role values(1,'ROLE_USER');
insert into springsessiondemo.role values(2,'ROLE_ADMIN');
insert into springsessiondemo.role values(3,'ROLE_APIUSER');
insert into springsessiondemo.role values(4,'ROLE_DBA');
insert into springsessiondemo.role values(5,'ROLE_SELLER');
insert into springsessiondemo.role values(6,'ROLE_BUYER');


/* Insert Data into Privilege Table */
insert into springsessiondemo.privilege values(1,'READ_PRIVILEGE');
insert into springsessiondemo.privilege values(2,'WRITE_PRIVILEGE');
insert into springsessiondemo.privilege values(3,'DELETE_PRIVILEGE');

/* Insert Data into UserRole Table */
INSERT INTO `springsessiondemo`.`user_role`(`id`,`user_id`,`role_id`) VALUES (1,2,1);
INSERT INTO `springsessiondemo`.`user_role`(`id`,`user_id`,`role_id`) VALUES (2,1,2);INSERT INTO `springsessiondemo`.`user_role`(`id`,`user_id`,`role_id`) VALUES (3,1,1);
/* Insert Data into RolePrivilege Table */
insert into springsessiondemo.role_privilege values(2,1);
insert into springsessiondemo.role_privilege values(2,2);
insert into springsessiondemo.role_privilege values(2,3);
insert into springsessiondemo.role_privilege values(1,1);

15. Go to src/webapp and run the following command to install dependencies required for Angular UI.

$ npm install

16. Bring up Angular UI by running the following command and go to http://localhost:4200 to see the login screen

$ ng serve --watch

17. Enter the username and password you created in Step 14. It should take you to the home page

18. Click on Categories to see list of categories


Download the repository from Github

Pavan Kumar Jadda
Pavan Kumar Jadda
Articles: 36

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.