DEV Community

Pavan K Jadda
Pavan K Jadda

Posted on

OAuth 2.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"}
Enter fullscreen mode Exit fullscreen mode

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)  
);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);  
    }  

}
Enter fullscreen mode Exit fullscreen mode

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  
\*/  
...............................  
}
Enter fullscreen mode Exit fullscreen mode

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_);  
    }  
}
Enter fullscreen mode Exit fullscreen mode

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

8. Define OAuth2CorsFilter, PasswordEncoders 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 UsersAccessDecisionManager, CustomAuthenticationFailureHandler 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);
Enter fullscreen mode Exit fullscreen mode

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

$ npm install
Enter fullscreen mode Exit fullscreen mode

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

$ ng serve --watch
Enter fullscreen mode Exit fullscreen mode

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

Discussion (0)