Skip to content

ashrawan/Spring-Security-OAuth-Example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Security with OAuth2, and Custom JWT

  • OAuth2 with (Google, Facebook, Github), (additional example.yml included)
  • Additional Custom JWT (register and Login)
  • Email based - register/login, freemarker-templates, registration-verification and password-reset

Link:

Flow Overview:

[ OAUTH2 CONFIGURATION PROPERTIES @see: resources -> application.yml ]

# By default, popular provider details isn't required @See org.springframework.security.config.oauth2.client.CommonOAuth2Provider
# To achieve same through java configuration, @See org.springframework.security.oauth2.client.registration.ClientRegistrationRepository that register ClientRegistration Object

  security:
    oauth2:
      client:
        registration:
          google:
            clientId: ${GOOGLE_CLIENT_ID}
            clientSecret: ${GOOGLE_CLIENT_SECRET}
            redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
            scope: email, profile

          facebook:
            clientId: ${FACEBOOK_CLIENT_ID}
            clientSecret: ${FACEBOOK_CLIENT_SECRET}
            redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
            scope: email, public_profile

[ MAIN WEB SECURITY CONFIGURATION @see: config -> WebSecurityConfig.java ]

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // withDefaults() uses a Bean by the name of CorsConfigurationSource
                .cors(withDefaults())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .exceptionHandling(e -> e
                        .authenticationEntryPoint(customAuthenticationEntryPoint))
                .authorizeRequests(a -> a
                        .antMatchers("/auth/**", "/oauth2/**").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login(o -> o
                        .authorizationEndpoint().baseUri("/oauth2/authorize")
                        .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
                        .and()
                        .redirectionEndpoint().baseUri("/oauth2/callback/*")
                        .and()
                        .userInfoEndpoint().userService(customOAuth2UserService)
                        .and()
                        .successHandler(oAuth2AuthenticationSuccessHandler)
                        .failureHandler(oAuth2AuthenticationFailureHandler));

        // Add our custom Token based authentication filter
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

#. Run using maven wrapper/executable or via your preferred IDE

  • Note:
    • To run: Change datasource properties @See application.yml -> spring: datasource: (create database "test")
    • To view flow: Change logging level as needed @See application.yml -> logging:
    ./mvnw spring-boot:run     // (using maven-wrapper)
                    
                     OR     
                    
    mvn spring-boot:run        // ( using env mvn executable)

1. CUSTOM REGISTRATION AND OAUTH2 REGISTRATION

  • CUSTOM: Registration is done via custom PUBLIC endpoint -> "/auth/register" { email/username, password, address etc. }
  • OAUTH2: Registration requires setting up _CustomOAuth2UserService.java_ which extends DefaultOAuth2UserService
    • we carry authentication with OAuth2 providers. After authentication, we will receive email or some form of identification,
    • And then we check whether the specified email belonging to that provider already exists in our system/database or not
    • If its new user, we registered that user directly,
    • OPTIONAL: to promote users for new Sign up confirmation: we can structure flow e.g. setting up flag in db "userApprovedRegistration: false" or emailVerified, with verification code - if user already exists, flow continue to LOG IN / SIGN IN OAUTH2 process (see below)

2. CUSTOM / OAUTH2 - LOGIN OR SIGN IN

  • CUSTOM: JWT token are created via separate custom PUBLIC endpoint -> "/auth/login" { email/username, password }
  • OAUTH2: In case of OAuth2, authentication via OAuth2 providers is done with CustomOAuth2UserService.java
    • on SUCCESSFUL authentication custom class _OAuth2AuthenticationSuccessHandler.java_ extends SimpleUrlAuthenticationSuccessHandler triggers
    • This class handles custom jwt token creation and respond back to redirect_uri
String redirect_uri="http://my-ui-app.com/login"
String targetUrl = UriComponentsBuilder.fromUriString(redirect_uri)
                .queryParam("token", createdCustomJwtToken)
                .build().toUriString();

// e.g. targetUrl - http://my-ui-app.com/login?token=created-custom-jwt-token
redirectStrategy.sendRedirect(request, response, targetUrl);
# 1. Requesting OAuth2 Sign Up / Sign In
e.g. http://localhost:8080/oauth2/authorize/google?redirect_uri=http://localhost:8080/oauth2/redirect  
http://localhost:8080/oauth2/authorize/google?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fredirect

# 2. After clicking on consent screen
For all OAuth2 providers, On Successful OAuth2User Authentication Request process OAuth2UserRequest (CustomOAuth2UserService) 
  - Determine is this [ New Sign Up ] or [ Existing Sign In ]
  -  Create Principle Object that will set Authentication object
  - Continue flow to either OAuth2AuthenticationSuccessHandler OR OAuth2AuthenticationFailureHandler

# 3. Flow, After Success/Failure Authentication  
- OAuth2AuthenticationSuccessHandler.java
    - We create ( Custom JWT Token OR use OAuthProvider JWT token ) and respond back to redirect_uri , e.g. http://my-ui-app.com/oauth2/redirectPage?token=jwtToken )
    - http://localhost:8080/oauth2/redirect?token=jwtToken )

- OAuth2AuthenticationFailureHandler.java
    - We send authentication error response to the redirect_uri , e.g. http://my-ui-app.com/oauth2/redirectPage?error=Sorry-Couldnt-retrieve-your-email-from-Provider )
    - http://localhost:8080/oauth2/redirect?error=authenticationException-OR-localizedMessage-OR-custom-message )

3. ACCESSING RESOURCES ( ACCESS CONTROL MECHANISM )

  • If user requests PUBLIC endpoints

    • PUBLIC endpoints are freely available and skips Security filter chain
  • If user requests into SECURED endpoints e.g. "/api/userProfile"

    • Requesting WITHOUT token i.e. "Authorization" header

      • SecurityContext is empty, (path request matcher or anyRequest() ) -> .authenticated() will catch, or will fail from globalMethodSecurity expression if defined
      • And forward it to CustomAuthenticationEntryPoint.java for SC_UNAUTHORIZED response
    • Requesting WITH token i.e. "Authorization" header has jwt token

      • JWTAuthenticationFilter.java Validates Token and set SecurityContext -> Authentication Object
      • Since, Authentication Object is available, spring security filter continues the flow
      • Further permission checks are done in spring security Expression-Based Access Control or any configured Access Control mechanisms

jwtAuthenticationFilter: A custom filter to validate and parse jwtToken and Set Authentication Object

  • Checks "Authorization" header is present
    • Not Present: Continues the filterChain
    • If present: Validates Token and set SecurityContext -> Authentication Object

Structure

  • Properties defined in application.yml are initialized into AppProperties.java, default values are initialized in same class
  • For Multiple properties usage from AppProperties appProperties, values are initialized in @PostConstruct to avoid code clutter
  • Uses Lombok @Getter @Setter for getter/setter generation
  • By Default, config classes uses Field Injection @Autowired, other class uses constructor injection
  • Exception thrown are handled from @ControllerAdvice

About

Spring Security with OAuth2, and Custom JWT

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages