I have this demo springboot app and my goal is to understand more about oauth2, I was able to make it work using github auth I configured like this
spring:
security:
oauth2:
client:
registration:
github:
clientId: ***
clientSecret: ***
This is my gradle dependencies
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-web'
this is my security filter chain
@Slf4j
@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
@Autowired
private CustomOAuth2AuthenticationFailureHandler failureHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
CookieCsrfTokenRepository cRepo = CookieCsrfTokenRepository.withHttpOnlyFalse();
http
.addFilterBefore(new PrintCsrfTokenFilter(cRepo), CsrfFilter.class)
.csrf(csrf -> csrf.csrfTokenRepository(cRepo))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/error", "/webjars/**","/index.html").permitAll()
.anyRequest().authenticated()
)
.logout((logout) -> logout.logoutSuccessUrl("/").permitAll())
.oauth2Login(t -> t.failureHandler((request, response, exception) -> {
log.error(exception.getMessage());
request.getSession().setAttribute("error.message", exception.getMessage());
failureHandler.onAuthenticationFailure(request, response, exception);
}));
return http.build();
}
}
This is my debug filter class
@Slf4j
public final class PrintCsrfTokenFilter extends OncePerRequestFilter {
private CsrfTokenRepository tokenRepository;
public PrintCsrfTokenFilter(CsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.tokenRepository = csrfTokenRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
DeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request, response);
CsrfToken csrfToken = deferredCsrfToken.get();
String actualToken = this.resolveCsrfTokenValue(request, csrfToken);
log.info("csrfToken: {} , actualToken: {}", csrfToken.getToken(), actualToken);
String xcsrfToken = request.getHeader("X-XSRF-TOKEN");
log.info("xcsrfToken Token: " + xcsrfToken);
filterChain.doFilter(request, response);
}
private String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
Assert.notNull(request, "request cannot be null");
Assert.notNull(csrfToken, "csrfToken cannot be null");
String actualToken = request.getHeader(csrfToken.getHeaderName());
log.info("header {}: {} ", csrfToken.getHeaderName(), actualToken);
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
log.info("param {}: {} ", csrfToken.getParameterName(), actualToken);
}
return actualToken;
}
}
heres my front end side
<body>
<script type="text/javascript">
// Declare logout function in the global scope
window.logout = function() {
$.post("/logout", function() {
$("#user").html('');
$(".unauthenticated").show();
$(".authenticated").hide();
})
return true;
}
$.ajaxSetup({
beforeSend : function(xhr, settings) {
if (settings.type == 'POST' || settings.type == 'PUT'
|| settings.type == 'DELETE') {
if (!(/^http:.*/.test(settings.url) || /^https:.*/
.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-XSRF-TOKEN",
Cookies.get('XSRF-TOKEN'));
}
}
}
});
$(document).ready(function() {
$.get("/user", function(data, status, xhr) {
if (xhr.status == 200 && data.name != null && data.name != "") {
$("#user").html(data.name);
$(".unauthenticated").hide();
$(".authenticated").show();
} else {
$(".authenticated").hide();
$(".unauthenticated").show();
}
}).fail(function() {
$(".authenticated").hide();
$(".unauthenticated").show();
});
});
</script>
<div class="container">
<h1>Demo</h1>
<div class="container unauthenticated">
With GitHub: <a href="/oauth2/authorization/github">click here</a>
</div>
<div class="container authenticated">
Logged in as: <span id="user"></span>
<div>
<button onClick="logout()" class="btn btn-primary">Logout</button>
</div>
</div>
</div>
</body>
I can see that the actualToken
and the csrfToken.getToken()
is the same when I try to debug with breakpoint but I still get this failures when I try to logout
2023-07-31T18:17:31.904+08:00 INFO 10277 --- [nio-8080-exec-9] c.mark.oauth2.Demo.PrintCsrfTokenFilter : csrfToken: afc6a080-363c-4d9f-87e2-187314baf11a , actualToken: afc6a080-363c-4d9f-87e2-187314baf11a
2023-07-31T18:17:31.905+08:00 DEBUG 10277 --- [nio-8080-exec-9] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:8080/logout
This is how it looks like from the browser,
[(https://i.stack.imgur.com/HEygr.png)
I wanted to know if I am missing how I should implement OAuth2. Any Answers will be appreciated, I will keep this post updated for everyones learning and reference.
I tried to logout from the browser but its not working because even though the csrf token is the same it keeps on saying Invalid CSRF token found
, I am expecting it to work as I have understood it but clearly somethings missing.