Skip to content

wj OAuth and account-password login#11

Merged
PACHAKUTlQ merged 41 commits intodevfrom
fix/auth
Sep 26, 2025
Merged

wj OAuth and account-password login#11
PACHAKUTlQ merged 41 commits intodevfrom
fix/auth

Conversation

@A-lexisL
Copy link
Contributor

@A-lexisL A-lexisL commented Sep 5, 2025

No description provided.

MatchaDinosaur and others added 3 commits September 5, 2025 22:42
…, backend see apps/verifier/urls.py, apps/verifier/views.py, currently use ngrok for getting webhook from sjtu wj platform
@A-lexisL
Copy link
Contributor Author

A-lexisL commented Sep 6, 2025

log:

  1. rm old confirmation, signup but keep auth_login (I think its convenient for api debugging). update endpoint in web/view.py

@A-lexisL A-lexisL changed the title [DO NOT MERGED]This PR is for checking the changes with latest dev OAuth backend api Sep 10, 2025
@A-lexisL A-lexisL requested a review from Copilot September 10, 2025 12:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces OAuth authentication functionality with SJTU university credentials, replacing the previous email-based authentication system. It implements a secure multi-step authentication flow using Turnstile verification, questionnaire-based identity validation, and session management with Redis caching.

  • Adds comprehensive OAuth backend API with secure token management and user verification
  • Integrates frontend Auth component with Turnstile security verification and authentication flow
  • Replaces email-based authentication with university credential system

Reviewed Changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
website/urls.py Adds OAuth API endpoints and reorganizes authentication routes
website/settings.py Configures Redis caching, OAuth environment variables, and session management
pyproject.toml Adds httpx and django-redis dependencies for OAuth functionality
frontend/src/main.js Adds new Auth route for OAuth flow
frontend/src/components/Auth.vue Complete OAuth frontend component with Turnstile integration
apps/web/views.py Updates authentication decorators and removes deprecated auth views
apps/web/models/review.py Handles edge case of multiple reviews per user-course pair
apps/auth/views.py Comprehensive OAuth backend implementation with secure token management
apps/auth/* New OAuth app structure with proper Django configuration
Comments suppressed due to low confidence (1)

website/settings.py:1

  • Extra closing parenthesis in comment. Should be 'password resets(deprecated)' without the double closing parenthesis.
import os

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@A-lexisL A-lexisL self-assigned this Sep 10, 2025
@4rthurCai 4rthurCai requested a review from Copilot September 17, 2025 14:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 30 out of 32 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +30 to +31
for key, value in config.items():
globals()[key] = value
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using globals()[key] = value to dynamically set configuration variables can be dangerous as it allows arbitrary code execution if the YAML file is compromised. Consider using a whitelist of allowed configuration keys or a more secure configuration loading mechanism.

Suggested change
for key, value in config.items():
globals()[key] = value
# Only allow specific keys from the YAML config to be set as globals
ALLOWED_CONFIG_KEYS = {
"EXAMPLE_SETTING", # Replace/add with actual allowed keys
"ANOTHER_SETTING", # Replace/add with actual allowed keys
# Add all expected config keys here
}
for key, value in config.items():
if key in ALLOWED_CONFIG_KEYS:
globals()[key] = value
else:
# Optionally, log or warn about unexpected keys
pass

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending discussion

Comment on lines 234 to 236
latest_answer, error_response = asyncio.run(
utils.get_latest_answer(action=mapped_action, account=account),
)
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using asyncio.run() in a synchronous Django view creates overhead by starting a new event loop for each request. Consider using async views or making the HTTP calls synchronous to improve performance.

Suggested change
latest_answer, error_response = asyncio.run(
utils.get_latest_answer(action=mapped_action, account=account),
)
latest_answer, error_response = utils.get_latest_answer_sync(action=mapped_action, account=account)

Copilot uses AI. Check for mistakes.
@4rthurCai 4rthurCai self-assigned this Sep 17, 2025
@zzjc1234
Copy link
Contributor

The component has some redundancy in the watch logic. The two watchers for reviews and totalPages both try to adjust the currentPage when it exceeds totalPages, which could be simplified into a single watcher.

watch(
() => props.reviews,
() => {
if (currentPage.value > totalPages.value) {
currentPage.value = Math.max(1, totalPages.value);
}
},
);
// Adjust current page if it exceeds total pages when reviews are filtered
watch(totalPages, (newTotalPages) => {
if (currentPage.value > newTotalPages) {
currentPage.value = Math.max(1, newTotalPages);
}
});

@zzjc1234
Copy link
Contributor

Signup.vue and ResetPassword.vue are largely duplicated. Refactor to extract shared template and logic into a reusable component.

Copy link
Contributor

@zzjc1234 zzjc1234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@PACHAKUTlQ PACHAKUTlQ marked this pull request as ready for review September 25, 2025 08:16
@zzjc1234 zzjc1234 requested a review from PACHAKUTlQ September 25, 2025 08:18
status=500,
)

# Build the 'params' and 'sort' dictionaries
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many comments

full_url_path = f"{QUEST_BASE_URL}/{quest_api}/json"

try:
async with httpx.AsyncClient() as client:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Currently using async function in sync django so the waiting is still blocking. Requires migrating whole project to async.

latest_answer = full_data["data"]["rows"][0] # Get the first (latest) row

# Find the verification code by matching the question ID
verification_code = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's called OTP

}

# Single regex pattern to check all character requirements at once
pattern = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently a very strong password like KalwJ&!)@& cannot pass this test, and any passphrase (e.g. subscript-mardi-distract) cannot. Passphrases are common, easy to remember and very secure.
I think the rule should be like the sum of scores of length, character of different cases, number, symbols (i.e. `~!@#$%^&*()-=_+,./;'[]<>?:"{}|) must pass a threshold.

Update:
See frontend password check logic (add requirement of lengh >= 10 && length < 32). Make frontend and backend consistent, except that backend also need to use django password validator to ensure not common passwords (as you have already done)

from apps.web.models import Student


class CsrfExemptSessionAuthentication(SessionAuthentication):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to disable anti CSRF check. This is generally dangerous but is safe here due to httponly temp_token check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe unauthorized user doesn't have csrf token? (I know nothing

};

// Get cookie value by name
function getCookie(name) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember there are multiple getCookie() in frontend, is there? Confirm not.

return cookieValue;
}

// Reset authentication state
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many comments

@@ -1,92 +1,137 @@
<template>
<!--
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF

Comment on lines +30 to +31
for key, value in config.items():
globals()[key] = value
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending discussion

countdown.value = 1;

countdownInterval = setInterval(() => {
countdown.value--;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You setInterval 1 second, decrease by 1 every second which will run only once, after one second you clear the interval and jump. WTF???

}
}, 1000);
} catch (err) {
console.error("Failed to copy to clipboard:", err);
Copy link
Collaborator

@PACHAKUTlQ PACHAKUTlQ Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated code. The try-catch error handling is for copy failure. So display copy failure message on error, and wait and redirect go anyhow.

const hasOtpHint = urlParams.has("otp_hint");
const referrer = document.referrer;
const isFromQuestionnaire =
referrer.includes("questionnaire") ||
Copy link
Collaborator

@PACHAKUTlQ PACHAKUTlQ Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF is "questionnaire"?
Obviously Copilot hallucination.


// Prevent sending development/mock tokens to backend which will fail verification.
// Mock tokens are generated by Turnstile.vue when VITE_TURNSTILE_MOCK=true or on localhost.
const mockPrefix = "dev-turnstile-token-";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF

return `${minutes}:${seconds.toString().padStart(2, "0")}`;
};

// getCookie imported from utils/cookies.js
Copy link
Collaborator

@PACHAKUTlQ PACHAKUTlQ Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stop fucking adding so many useless comments

if (redirectTime) {
const timeSinceRedirect = now - parseInt(redirectTime);
// If user returned within 60 seconds, likely verification failed
if (timeSinceRedirect < 60 * 1000 && timeSinceRedirect > 5 * 1000) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this. If failed then use has to wait for the two minutes pass.
What if there is a network change? What if user session is somehow suspended? What if user closes his laptop and open again? This code will clear the OTP and break valid auth flow on these interruptions.

}
}

// If OTP exists for more than 30 seconds when user returns, likely verification failed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???


// If user is on signup, reset_password, or login page and has stored auth data, restart verification flow
const isOnSignupWithStoredAuth =
props.action === "signup" && localStorage.getItem("auth_otp");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no point switching cases of action

}

// Add visibility change listener to detect when user returns from questionnaire
const handleVisibilityChange = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain this. What is this for?

let turnstileWidget = null;

// Generate unique container ID for this instance
const containerId = `turnstile-widget-${Math.random()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be collisions. Is it possible to detect if there exist a widget with id_1, if there is than create one with id_2?

@PACHAKUTlQ PACHAKUTlQ mentioned this pull request Sep 25, 2025
@PACHAKUTlQ PACHAKUTlQ merged commit 5b08b40 into dev Sep 26, 2025
@PACHAKUTlQ PACHAKUTlQ deleted the fix/auth branch September 26, 2025 16:56
PACHAKUTlQ added a commit that referenced this pull request Oct 13, 2025
wj OAuth and account-password login
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants