In the tutorial, we will explain how to add social-based login to a Spring boot application using a Spring Security function called OAuth2. We use MySQL database and React for the frontend.
Creating the Project
You can create a project using the Spring Initializr web tool http://start.spring.io, follows the introduction to fill in the information:
- Artifact: ozenero-spring-social
- Dependencies: Spring Web, Spring Security, OAuth2 Client, Spring Data JPA, MySQL Driver, Validation
After that, you can click Generate and download your project
Creating OAuth2 apps for social login
You need to create an app in OAuth2 provider console to enable social login with an OAuth2 provider. After that, you can get AppID and AppSecret (also called ClientId and Client Secret). OAuth2 providers use the AppID and AppSecret to identify your app. Some other settings include:
- Authorized redirect URIs: A valid list of redirect URIs to redirect users after granting or rejecting permission to the app.
- Scope: used to ask users for permission to access data.
Creating Facebook, Github, and Google Apps
- Facebook App: Facebook app can be created at Facebook apps dashboard
- Github App: Github apps can be created at https://github.com/settings/apps.
- Google Project: Google Project and the credentials for OAuth2 can be created at Google Developer Console
Configuring the Spring Boot application
By default, Spring boot reads configurations from the file src/main/resource/application.properties
. It also supports .yaml
format. Rename application.properties
file to application.yaml
and add the following configurations.
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_social?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
username: root
password: callicoder
jpa:
show-sql: true
hibernate:
ddl-auto: update
naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
security:
oauth2:
client:
registration:
google:
clientId: your_id.apps.googleusercontent.com
clientSecret: your_secret
redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
scope:
- email
- profile
facebook:
clientId: your_id
clientSecret: your_secret
redirectUri: "{baseUrl}/oauth2/callback/{registrationId}" # Note that facebook now mandates the use of https redirect URIs, so make sure your app supports https in production
scope:
- email
- public_profile
github:
clientId: your_id
clientSecret: your_secret
redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
scope:
- user:email
- read:user
provider:
facebook:
authorizationUri: https://www.facebook.com/v3.0/dialog/oauth
tokenUri: https://graph.facebook.com/v3.0/oauth/access_token
userInfoUri: https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture.width(250).height(250)
app:
auth:
tokenSecret: your_token_secret
tokenExpirationMsec: 864000000
cors:
allowedOrigins: http://localhost:3000 # Comma separated list of allowed origins
oauth2:
# After successfully authenticating with the OAuth2 Provider,
# we'll be generating an auth token for the user and sending the token to the
# redirectUri mentioned by the client in the /oauth2/authorize request.
# We're not using cookies because they won't work well in mobile clients.
authorizedRedirectUris:
- http://localhost:3000/oauth2/redirect
- myandroidapp://oauth2/redirect
- myiosapp://oauth2/redirect
The data source includes MySQL database configurations. Database name is spring_social, please modify spring.datasource.username and spring.datasource.password.
In security:oauth2:client:registration:
, the configurations include all details of oauth2 providers. Inapp.auth
, its config the JWT authentication token when the user logged in successfully.
In redirectUriTemplate
property in all the registered oauth2 providers, when you create an app in the OAuth2 providers, you have to add an authorized redirect URI which matches the template. In this case, for a google app, we config the authorizedRedirectURI http://localhost:8080/oauth2/callback/google
.
Binding AppProperties
In the next step, we bind all configurations prefixed with app
to a POJO class using Spring Boot’s @ConfigurationProperties
feature
package com.example.springsocial.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private final Auth auth = new Auth();
private final OAuth2 oauth2 = new OAuth2();
public static class Auth {
private String tokenSecret;
private long tokenExpirationMsec;
public String getTokenSecret() {
return tokenSecret;
}
public void setTokenSecret(String tokenSecret) {
this.tokenSecret = tokenSecret;
}
public long getTokenExpirationMsec() {
return tokenExpirationMsec;
}
public void setTokenExpirationMsec(long tokenExpirationMsec) {
this.tokenExpirationMsec = tokenExpirationMsec;
}
}
public static final class OAuth2 {
private List<String> authorizedRedirectUris = new ArrayList<>();
public List<String> getAuthorizedRedirectUris() {
return authorizedRedirectUris;
}
public OAuth2 authorizedRedirectUris(List<String> authorizedRedirectUris) {
this.authorizedRedirectUris = authorizedRedirectUris;
return this;
}
}
public Auth getAuth() {
return auth;
}
public OAuth2 getOauth2() {
return oauth2;
}
}
Enabling AppProperties
In order to enable the properties, we add the@EnableConfigurationProperties
annotation. Let’s open the main application class SpringSocialApplication.java
and add the annotation like below.
package com.example.springsocial;
import com.example.springsocial.config.AppProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class SpringSocialApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSocialApplication.class, args);
}
}
Enabling CORS
In order to enable CORS to allow our frontend client can access the APIs from a different origin. We need to enable the origin http://localhost:3000
where the frontend application will be running.
package com.example.springsocial.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
@Value("${app.cors.allowedOrigins}")
private String[] allowedOrigins;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(MAX_AGE_SECS);
}
}
Creating the database entities
Let’s now create the Entity classes of our application. Following is the definition of the User
class –
In the next step, we create Entity classes for the application. You can define the User
class like below.
package com.example.springsocial.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "users", uniqueConstraints = {
@UniqueConstraint(columnNames = "email")
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Email
@Column(nullable = false)
private String email;
private String imageUrl;
@Column(nullable = false)
private Boolean emailVerified = false;
@JsonIgnore
private String password;
@NotNull
@Enumerated(EnumType.STRING)
private AuthProvider provider;
private String providerId;
// Getters and Setters (Omitted for brevity)
}
The User
class contains information about the authentication provider. Following is the definition of the AuthProvider
enum –
In the User
class, it provides information related to the authentication provider. Here is the definition of the AuthProvider enum.
package com.example.springsocial.model;
public enum AuthProvider {
local,
facebook,
google,
github
}
Creating the repositories for accessing data from the database
Let’s create the repository layer for accessing data from the database. The following UserRepository
interface provides database functionalities for the User
entity. Thanks to Spring-Data-JPA, we don’t need to write much code here
In order to create the repository layer for accessing data from the database, we need to config theUserRepository
interface to provide database functionalities for the User
entity. Refer to Spring-Data-JPA for the code
package com.example.springsocial.repository;
import com.example.springsocial.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Boolean existsByEmail(String email);
}
In this article, we introduce how to configure an application and defined the entity classes and repositories.