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
⌛ This post has been reserved for your question.
Hey @Danix! Please useTIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here./closeor theClose Postbutton 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.
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();
}
💤 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.