Github Authorization via pkce

I am making a github authorization via react and spring boot but i am getting 401 error while getting the access token .. React frontend :
import React, { useEffect } from "react";
import axios from "axios";

const GitHubCallback = () => {
useEffect(() => {
const handleGitHubCallback = async () => {
const code = new URLSearchParams(window.location.search).get("code");
const codeVerifier = sessionStorage.getItem("code_verifier"); // Use sessionStorage as per your code

if (code && codeVerifier) {
try {
const params = new URLSearchParams();
params.append("code", code);
params.append("codeVerifier", codeVerifier);

const response = await axios.post("http://localhost:8080/api/auth/github", params, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});

// Handle the received JWT or token
console.log("Access Token:", response.data.access_token);
} catch (error) {
console.error("GitHub OAuth Error:", error);
}
}
};

handleGitHubCallback();
}, []);

return <p>Processing GitHub login...</p>;
};

export default GitHubCallback;

import React, { useEffect } from "react";
import axios from "axios";

const GitHubCallback = () => {
useEffect(() => {
const handleGitHubCallback = async () => {
const code = new URLSearchParams(window.location.search).get("code");
const codeVerifier = sessionStorage.getItem("code_verifier"); // Use sessionStorage as per your code

if (code && codeVerifier) {
try {
const params = new URLSearchParams();
params.append("code", code);
params.append("codeVerifier", codeVerifier);

const response = await axios.post("http://localhost:8080/api/auth/github", params, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});

// Handle the received JWT or token
console.log("Access Token:", response.data.access_token);
} catch (error) {
console.error("GitHub OAuth Error:", error);
}
}
};

handleGitHubCallback();
}, []);

return <p>Processing GitHub login...</p>;
};

export default GitHubCallback;

3 Replies
JavaBot
JavaBot3w ago
This post has been reserved for your question.
Hey @Danix! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
Danix
DanixOP3w ago
import { generateCodeChallenge, generateCodeVerifier , generateRandomString} from "../../utils/pkce";

export const LoginWithGithub = async () => {
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

// Store the code verifier in session storage (temporary)
sessionStorage.setItem('code_verifier', codeVerifier);

const params = new URLSearchParams({
client_id: 'Ov23li7XMZBxnH3K31ME', // Replace with your GitHub client ID
redirect_uri: 'http://localhost:5173/oauth2/callback/github', // Must match GitHub OAuth app settings
scope: 'user:email', // Requested scopes
response_type: 'code',
state: generateRandomString(16), // Optional but recommended for security
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});

// Redirect the user to GitHub's authorization endpoint
window.location.href = `https://github.com/login/oauth/authorize?${params.toString()}`;
};
import { generateCodeChallenge, generateCodeVerifier , generateRandomString} from "../../utils/pkce";

export const LoginWithGithub = async () => {
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

// Store the code verifier in session storage (temporary)
sessionStorage.setItem('code_verifier', codeVerifier);

const params = new URLSearchParams({
client_id: 'Ov23li7XMZBxnH3K31ME', // Replace with your GitHub client ID
redirect_uri: 'http://localhost:5173/oauth2/callback/github', // Must match GitHub OAuth app settings
scope: 'user:email', // Requested scopes
response_type: 'code',
state: generateRandomString(16), // Optional but recommended for security
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});

// Redirect the user to GitHub's authorization endpoint
window.location.href = `https://github.com/login/oauth/authorize?${params.toString()}`;
};
export const generateRandomString = (length) => {
const array = new Uint32Array(length);
window.crypto.getRandomValues(array);
return Array.from(array, (dec) => ('0' + dec.toString(16)).slice(-2)).join('');
};

const sha256 = async (plain) => {
const encoder = new TextEncoder();
const data = encoder.encode(plain);
const hash = await window.crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};

export const generateCodeVerifier = () => generateRandomString(64);
export const generateCodeChallenge = async (verifier) => sha256(verifier);
export const generateRandomString = (length) => {
const array = new Uint32Array(length);
window.crypto.getRandomValues(array);
return Array.from(array, (dec) => ('0' + dec.toString(16)).slice(-2)).join('');
};

const sha256 = async (plain) => {
const encoder = new TextEncoder();
const data = encoder.encode(plain);
const hash = await window.crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};

export const generateCodeVerifier = () => generateRandomString(64);
export const generateCodeChallenge = async (verifier) => sha256(verifier);
Spring boot
Spring boot
package com.scriptenhancer.controllers;

import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/api/auth")
public class OAuthController {

@Value("${spring.security.oauth2.client.registration.github.client-id}")
private String clientId;

@Value("${spring.security.oauth2.client.registration.github.client-secret}")
private String clientSecret;

@Value("${spring.security.oauth2.client.registration.github.redirect-uri}")
private String redirectUri;

private final RestTemplate restTemplate = new RestTemplate();

@PostMapping("/api/auth/github")
public ResponseEntity<?> handleGitHubCallback(@RequestBody Map<String, String> params) {
String code = params.get("code");
String codeVerifier = params.get("codeVerifier");

// Handle the code and codeVerifier here
System.out.println("The code verifier is : " +codeVerifier);
System.out.println("The code is : " +code);

return ResponseEntity.ok().body("GitHub OAuth Success");
}
}
package com.scriptenhancer.controllers;

import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/api/auth")
public class OAuthController {

@Value("${spring.security.oauth2.client.registration.github.client-id}")
private String clientId;

@Value("${spring.security.oauth2.client.registration.github.client-secret}")
private String clientSecret;

@Value("${spring.security.oauth2.client.registration.github.redirect-uri}")
private String redirectUri;

private final RestTemplate restTemplate = new RestTemplate();

@PostMapping("/api/auth/github")
public ResponseEntity<?> handleGitHubCallback(@RequestBody Map<String, String> params) {
String code = params.get("code");
String codeVerifier = params.get("codeVerifier");

// Handle the code and codeVerifier here
System.out.println("The code verifier is : " +codeVerifier);
System.out.println("The code is : " +code);

return ResponseEntity.ok().body("GitHub OAuth Success");
}
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors(cors -> corsFilter())
.csrf(csrf -> csrf.disable()) // Disable CSRF for stateless APIs
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasAuthority("ADMIN")
.requestMatchers("/api/user/**").hasAnyAuthority("USER" , "ADMIN")
.requestMatchers("/api/auth/**" , "/api/auth/github").permitAll()
.anyRequest().authenticated() // Protect all other endpoints
)
.sessionManagement(sess -> sess
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No sessions
).exceptionHandling(e -> {
e.authenticationEntryPoint((req, res, ex) -> {
res.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
});
})
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); // Add JWT filter

return http.build();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors(cors -> corsFilter())
.csrf(csrf -> csrf.disable()) // Disable CSRF for stateless APIs
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasAuthority("ADMIN")
.requestMatchers("/api/user/**").hasAnyAuthority("USER" , "ADMIN")
.requestMatchers("/api/auth/**" , "/api/auth/github").permitAll()
.anyRequest().authenticated() // Protect all other endpoints
)
.sessionManagement(sess -> sess
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No sessions
).exceptionHandling(e -> {
e.authenticationEntryPoint((req, res, ex) -> {
res.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
});
})
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); // Add JWT filter

return http.build();
}
JavaBot
JavaBot3w ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.

Did you find this page helpful?