Handling OAuth2 Flow and JWT Authentication in a React + Flask Application
Introduction
In this blog post, I'll share my experience implementing Google OAuth2 authentication in a React frontend with a Flask backend. I encountered several challenges along the way, particularly with maintaining user sessions and handling redirects properly.
Initial Problem: Missing Refresh Token
The Issue
My first error occurred when trying to validate user tokens:
1 |
|
This happened in the following code:
1 |
|
The Solution
The problem was that web applications need to explicitly request offline access to get a refresh token. I modified the authorization flow:
1 |
|
Second Problem: OAuth Redirect Issues
The Issue
After successful OAuth authentication, instead of seeing my schedule page, I got a JSON response displayed on screen:
1 |
|
The Solution
I modified the OAuth callback to properly redirect to the frontend:
1 |
|
Third Problem: Lost Authentication State
The Issue
After implementing the redirect, a new problem emerged: users were being shown the login page again after the OAuth redirect. This happened because the React state was being reset on page reload.
The Initial Frontend Code
1 |
|
The Complete Solution
I implemented a JWT-based solution to maintain authentication state:
Backend Changes:
1
2
3
4
5
6
7
8
9@app.route('/oauth2callback')
def oauth2callback():
# ... OAuth flow code ...
# Create JWT token
access_token = create_access_token(identity=email)
# Redirect with token
return redirect(f"http://localhost:3001?token={access_token}")Frontend Changes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24export default function LoginPage() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
// Check for token in URL
const queryParams = new URLSearchParams(window.location.search);
const tokenFromUrl = queryParams.get('token');
if (tokenFromUrl) {
localStorage.setItem('token', tokenFromUrl);
setIsLoggedIn(true);
// Clean URL
window.history.replaceState({}, document.title, window.location.pathname);
} else {
// Check existing token
const existingToken = localStorage.getItem('token');
if (existingToken) {
setIsLoggedIn(true);
}
}
}, []);
// ... rest of component
}
Key Learnings
- Web applications need explicit offline access for refresh tokens
- OAuth redirects need careful handling in SPAs
- Use JWT tokens and localStorage to maintain authentication state
- Always clean up URL parameters after processing
- Consider token verification on page load
Conclusion
This implementation now successfully: - Handles Google OAuth2 authentication - Maintains user sessions across page reloads - Properly manages the authentication flow between frontend and backend - Provides a smooth user experience without unnecessary logins
The final solution combines OAuth2 for secure Google API access with JWT for maintaining local authentication state, creating a robust authentication system for web applications.
Remember to always implement proper security measures and token validation in production environments!