FIX: Made Oauth more secure
Changed styling
This commit is contained in:
@@ -109,12 +109,12 @@ body[data-theme="dark"] {
|
|||||||
--user-pfp-border: #ffffff;
|
--user-pfp-border: #ffffff;
|
||||||
--user-pfp-border-shadow: -10px 15px 25px rgba(0, 0, 0, 0.754);
|
--user-pfp-border-shadow: -10px 15px 25px rgba(0, 0, 0, 0.754);
|
||||||
|
|
||||||
--user-borderBg: rgb(123, 0, 0);
|
--user-borderBg: rgb(57, 0, 123);
|
||||||
--user-box: rgb(75, 0, 150);
|
--user-box: rgb(75, 0, 150);
|
||||||
--user-box-strip: rgb(165, 0, 0);
|
--user-box-strip: rgb(165, 0, 0);
|
||||||
--user-box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.754);
|
--user-box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.754);
|
||||||
--user-sideBox: rgba(25, 25, 25, 0.938);
|
--user-sideBox: rgba(25, 25, 25, 0.938);
|
||||||
--user-contentBox: rgba(108, 0, 0, 0.764);
|
--user-contentBox: rgba(29, 0, 66, 0.75);
|
||||||
--user-follow-bg: #9a0000;
|
--user-follow-bg: #9a0000;
|
||||||
|
|
||||||
--user-bg: rgb(16, 16, 16);
|
--user-bg: rgb(16, 16, 16);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ const LoginForm: React.FC<SubmitProps> = ({ onSubmit, onForgotPassword }) => {
|
|||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.username ? "border-red-500" : ""
|
errors.username ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -143,7 +143,7 @@ const LoginForm: React.FC<SubmitProps> = ({ onSubmit, onForgotPassword }) => {
|
|||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.password ? "border-red-500" : ""
|
errors.password ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
></Input>
|
></Input>
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
|
|||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.username ? "border-red-500" : ""
|
errors.username ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -148,7 +148,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
|
|||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.email ? "border-red-500" : ""
|
errors.email ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -164,7 +164,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
|
|||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.password ? "border-red-500" : ""
|
errors.password ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -180,7 +180,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
|
|||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
value={formData.confirmPassword}
|
value={formData.confirmPassword}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
extraClasses={`w-full focus:w-[120%] p-3 ${
|
extraClasses={`w-full p-3 ${
|
||||||
errors.confirmPassword ? "border-red-500" : ""
|
errors.confirmPassword ? "border-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type={type}
|
type={type}
|
||||||
className={`${extraClasses} p-2 text-[clamp(1rem, 1.5vw, 1.25rem)] text-white hover:text-purple-600 bg-black/30 hover:bg-black/80 rounded-md border border-gray-300 hover:border-purple-500 hover:border-b-4 hover:border-l-4 active:border-b-2 active:border-l-2 transition-all`}
|
className={`${extraClasses} p-2 text-[clamp(1rem, 1.5vw, 1.25rem)] text-white hover:text-purple-600 bg-black/30 hover:bg-black/80 rounded-md border border-gray-300 hover:border-purple-500 hover:border-b-4 hover:border-l-4 active:border-b-2 active:border-l-2 transition-all box-border`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -39,7 +39,7 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
|
|||||||
" cursor-pointer hover:text-purple-600 hover:bg-black/80 hover:border-purple-500 hover:border-b-4 hover:border-l-4");
|
" cursor-pointer hover:text-purple-600 hover:bg-black/80 hover:border-purple-500 hover:border-b-4 hover:border-l-4");
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`${extraClasses} p-2 text-[1.5rem] text-white bg-black/30 rounded-[1rem] border border-gray-300 transition-all`}
|
className={`${extraClasses} p-2 text-[1.5rem] text-white bg-black/30 rounded-[1rem] border border-gray-300 transition-all box-border`}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -47,4 +47,4 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
||||||
@@ -27,7 +27,7 @@ const Input: React.FC<InputProps> = ({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
{...props}
|
{...props}
|
||||||
className={`${extraClasses} relative p-2 rounded-[1rem] w-[20vw] focus:w-[22vw] bg-black/40 border border-gray-300 focus:border-purple-500 focus:outline-purple-500 text-center text-white text-xl transition-all`}
|
className={`${extraClasses} relative p-2 rounded-[1rem] w-[20vw] focus:w-[110%] bg-black/40 border border-gray-300 focus:border-purple-500 focus:outline-purple-500 text-center text-white text-xl transition-all`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ def init_oauth(app):
|
|||||||
client_secret=app.config['GOOGLE_CLIENT_SECRET'],
|
client_secret=app.config['GOOGLE_CLIENT_SECRET'],
|
||||||
authorize_url='https://accounts.google.com/o/oauth2/auth',
|
authorize_url='https://accounts.google.com/o/oauth2/auth',
|
||||||
access_token_url='https://oauth2.googleapis.com/token',
|
access_token_url='https://oauth2.googleapis.com/token',
|
||||||
client_kwargs={'scope': 'openid profile email'},
|
client_kwargs={
|
||||||
|
'scope': 'openid profile email',
|
||||||
|
'prompt': 'select_account' # Forces account selection even if already logged in
|
||||||
|
},
|
||||||
api_base_url='https://www.googleapis.com/oauth2/v1/',
|
api_base_url='https://www.googleapis.com/oauth2/v1/',
|
||||||
userinfo_endpoint='https://openidconnect.googleapis.com/v1/userinfo',
|
userinfo_endpoint='https://openidconnect.googleapis.com/v1/userinfo',
|
||||||
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
|
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
|
||||||
@@ -40,13 +43,18 @@ def login_google():
|
|||||||
"""
|
"""
|
||||||
Redirects to Google's OAuth authorization page
|
Redirects to Google's OAuth authorization page
|
||||||
"""
|
"""
|
||||||
# Creates nonce to be sent
|
# Create both nonce and state
|
||||||
session["nonce"] = token_urlsafe(16)
|
session["nonce"] = token_urlsafe(16)
|
||||||
|
session["state"] = token_urlsafe(32)
|
||||||
session["origin"] = request.args.get("next")
|
session["origin"] = request.args.get("next")
|
||||||
|
|
||||||
|
# Make sure session is saved before redirect
|
||||||
|
session.modified = True
|
||||||
|
|
||||||
return google.authorize_redirect(
|
return google.authorize_redirect(
|
||||||
f'{url}/api/google_auth',
|
redirect_uri=f'{url}/api/google_auth',
|
||||||
nonce=session['nonce']
|
nonce=session['nonce'],
|
||||||
|
state=session['state']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,10 +64,22 @@ def google_auth():
|
|||||||
Receives token from Google OAuth and authenticates it to validate login
|
Receives token from Google OAuth and authenticates it to validate login
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Check state parameter before authorizing
|
||||||
|
returned_state = request.args.get('state')
|
||||||
|
stored_state = session.get('state')
|
||||||
|
|
||||||
|
if not stored_state or stored_state != returned_state:
|
||||||
|
print(f"State mismatch: stored={stored_state}, returned={returned_state}", flush=True)
|
||||||
|
return jsonify({
|
||||||
|
'error': f"mismatching_state: CSRF Warning! State not equal in request and response.",
|
||||||
|
'message': 'Authentication failed'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
# State matched, proceed with token authorization
|
||||||
token = google.authorize_access_token()
|
token = google.authorize_access_token()
|
||||||
|
|
||||||
# Verifies token as well as nonce
|
# Verify nonce
|
||||||
nonce = session.pop('nonce', None)
|
nonce = session.get('nonce')
|
||||||
if not nonce:
|
if not nonce:
|
||||||
return jsonify({'error': 'Missing nonce in session'}), 400
|
return jsonify({'error': 'Missing nonce in session'}), 400
|
||||||
|
|
||||||
@@ -97,22 +117,33 @@ def google_auth():
|
|||||||
)
|
)
|
||||||
user_data = get_session_info_email(user_email)
|
user_data = get_session_info_email(user_email)
|
||||||
|
|
||||||
origin = session.pop("origin", f"{url.replace('/api', '')}")
|
# Store origin, username and user_id before clearing session
|
||||||
|
origin = session.get("origin", f"{url.replace('/api', '')}")
|
||||||
|
username = user_data["username"]
|
||||||
|
user_id = user_data["user_id"]
|
||||||
|
|
||||||
|
# Clear session and set new data
|
||||||
session.clear()
|
session.clear()
|
||||||
session["username"] = user_data["username"]
|
session["username"] = username
|
||||||
session["user_id"] = user_data["user_id"]
|
session["user_id"] = user_id
|
||||||
|
|
||||||
|
# Ensure session is saved
|
||||||
|
session.modified = True
|
||||||
|
|
||||||
print(f"session: {session.get('username')}. user_id: {session.get('user_id')}", flush=True)
|
print(f"session: {session.get('username')}. user_id: {session.get('user_id')}", flush=True)
|
||||||
|
|
||||||
return redirect(origin)
|
return redirect(origin)
|
||||||
|
|
||||||
except OAuthError as e:
|
except OAuthError as e:
|
||||||
|
print(f"OAuth Error: {str(e)}", flush=True)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': 'Authentication failed',
|
'message': 'Authentication failed',
|
||||||
'error': str(e)
|
'error': str(e)
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"Unexpected Error: {str(e)}", flush=True)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': 'An unexpected error occurred',
|
'message': 'An unexpected error occurred',
|
||||||
'error': str(e)
|
'error': str(e)
|
||||||
}), 500
|
}), 500
|
||||||
Reference in New Issue
Block a user