WARNING: This project's code is 100% generated by AI, use at your own risk.
This document is also available in 中文
A FastAPI third-party HTTP layer over Yanshan University's unified
authentication (CAS) and academic systems — the network wrapper around
ysu-sdk.
- Exposes the SDK's full CAS login flow, including CAPTCHA and SMS / cpdaily MFA.
- Exposes the SDK's full academic-system surface: grades, statistics / distribution / ranking, GPA, schedule, exams, student profile, training plan, academic completion / warnings, and course evaluation.
- Stateless. The server never holds user credentials. Every request
carries a Base64-encoded CAS cookie JSON (the output of
CASCredential.to_json()) in theX-CAS-Credentialheader, and an optionalX-JWXT-Sessionheader for jwxt-domain cookies. - Session rotation. After each request the server may return updated
X-CAS-Credential/X-JWXT-Sessionresponse headers (viaSessionRotationMiddleware). Clients should read and persist them to minimize CAS round-trips. - Error pass-through.
CASError/JWXTErrorraised by the SDK are mapped into HTTP responses with acodefield (see Error model), so clients can branch on a business code rather than parse messages. - Only evaluation writes. Like the SDK, the only endpoint that mutates
server state is
POST /jwxt/evaluation/submit.
Requires Python ≥ 3.12 and uv.
ysu-sdk is declared as a path dependency in pyproject.toml and resolved
automatically by uv:
uv sync
# or
uv pip install -e .uv run python -m ysu_api.mainThe service binds 0.0.0.0:11920 by default. Once running, open
http://localhost:11920/docs for Swagger UI or
http://localhost:11920/redoc for ReDoc.
For production, drive uvicorn directly:
uv run uvicorn ysu_api.main:app --host 0.0.0.0 --port 11920| Variable | Default | Notes |
|---|---|---|
YSU_API_HOST |
0.0.0.0 |
Only honored when launched via python -m ysu_api.main. |
YSU_API_PORT |
11920 |
Same as above. |
YSU_API_RELOAD |
0 |
Set to 1 to enable uvicorn autoreload (development only). |
YSU_API_LOG_LEVEL |
INFO |
Standard logging level name. |
YSU_API_CORS_ORIGINS |
unset | Comma-separated allow-list. Leaving it unset disables CORSMiddleware entirely. Setting it to * turns off allow_credentials to comply with browser policy. |
The API exposes both of the SDK's login paths so callers can pick whichever fits — one-shot for CLIs, step-by-step for browser front-ends.
Submit everything (password, CAPTCHA, MFA code) in a single request. Convenient for CLIs and scripts.
curl -sX POST http://localhost:11920/auth/login \
-H 'Content-Type: application/json' \
-d '{
"username": "<student id>",
"password": "<password>",
"captcha": "",
"mfa_code": "<sms code>",
"mfa_method": "cpdaily"
}'
# → {"credential": "<base64-json>"}If the server requires CAPTCHA and captcha is empty → 403 NEED_CAPTCHA.
Call POST /auth/captcha to fetch a Base64 PNG, then retry.
If the server requires MFA and mfa_code is empty → 403 MFA_REQUIRED.
Use this when you cannot complete "ask for password → prompt for MFA → resubmit" inside a single request.
1. POST /auth/captcha → fetch a CAPTCHA image if needed
2. POST /auth/login/step1 → submit username / password (/ captcha)
- authenticated=true → full credential returned, done
- needs_mfa=true → temporary credential returned, go to 3
3. POST /auth/login/mfa/request (header: X-CAS-Credential) → ask server to send SMS
4. POST /auth/login/mfa/submit (header: X-CAS-Credential) → submit code, receive final credential
The returned credential string is the auth token for every academic
endpoint, carried via X-CAS-Credential. You may also supply an
X-JWXT-Session header (see Session rotation) to skip
the CAS authorize cold path:
curl -s http://localhost:11920/jwxt/student/info \
-H "X-CAS-Credential: $CREDENTIAL" \
-H "X-JWXT-Session: $JWXT_SESSION"Clients should persist both headers (e.g. browser localStorage) and
update them from every response (see Session rotation
below). When the credential expires, academic endpoints return
401 not authenticated — run the login flow again.
The full schema is served at /docs. The tables below list endpoints by
category.
| Endpoint | Purpose |
|---|---|
POST /auth/captcha |
Fetch a CAPTCHA image when the server requires one. |
POST /auth/login |
One-shot login. |
POST /auth/login/step1 |
Stepped login — submit credentials. |
POST /auth/login/mfa/request |
Stepped login — request MFA code. |
POST /auth/login/mfa/submit |
Stepped login — submit MFA code. |
GET /auth/status |
Check whether a credential is still valid. |
Every /jwxt/* endpoint requires the X-CAS-Credential header and
optionally accepts X-JWXT-Session (see Session rotation).
| Endpoint | Purpose |
|---|---|
GET /jwxt/student/info |
Student profile. |
GET /jwxt/grades |
Grades query (paginated, filterable by term / course name). |
GET /jwxt/gpa |
Credit and GPA summary. |
GET /jwxt/grades/statistics |
Grade statistics (by teaching class or whole course). |
GET /jwxt/grades/distribution |
Grade distribution bucketed by letter grade. |
GET /jwxt/grades/ranking |
Student's rank within a class or course. |
GET /jwxt/schedule |
Lecture schedule. |
GET /jwxt/schedule/experimental |
Lab / experiment schedule. |
GET /jwxt/schedule/unscheduled |
Courses not yet placed on the timetable. |
GET /jwxt/class-periods |
Class period (bell) configuration. |
GET /jwxt/term-calendar |
Term calendar. |
GET /jwxt/current-week |
Current (or given-date) teaching week. |
GET /jwxt/exams |
Exam arrangements. |
GET /jwxt/training-plan |
Training plan course list. |
GET /jwxt/academic-completion |
Academic completion progress. |
GET /jwxt/academic-warnings |
Academic warnings. |
GET /jwxt/evaluation/types |
Evaluation categories and pending counts. |
GET /jwxt/evaluation/pending |
Pending evaluation tasks within a category. |
GET /jwxt/evaluation/detail |
Evaluation questionnaire detail. |
POST /jwxt/evaluation/calculate-score |
Pre-check answers without writing. |
POST /jwxt/evaluation/submit |
Submit the questionnaire (only write endpoint, irreversible). |
grades/statistics, grades/distribution, and grades/ranking share one
dispatch rule: exactly one of class_id or course_code must be
provided. Passing both or neither follows the SDK and raises
JWXTBusinessError / ValueError (the former maps to HTTP 422).
During a request the SDK may refresh CAS-domain cookies (e.g. happyVoyage)
or jwxt-domain cookies (JSESSIONID, _WEU). To keep these updates on the
client side, SessionRotationMiddleware writes the latest snapshots back
into every response:
| Response header | Content |
|---|---|
X-CAS-Credential |
Base64-encoded CASCredential.to_json() |
X-JWXT-Session |
Base64-encoded JWXTSession.to_json() (only when non-empty) |
Clients must read these headers after each API call and overwrite their
local copy. Supplying a stale X-JWXT-Session is safe — the server falls
back to the cold path (one extra CAS authorize call), but persisting the
fresh values eliminates most round-trips to cer.ysu.edu.cn.
When YSU_API_CORS_ORIGINS is configured, both headers are already listed
in expose_headers so browser JS can read them.
SDK exceptions are normalized into the response shape below (see
ysu_api/exc_handlers.py):
| HTTP | code |
Trigger |
|---|---|---|
| 401 | (none) | NotAuthenticatedError / LoginFailedError / NotLoggedInError: missing or expired credential. |
| 403 | NEED_CAPTCHA |
One-shot login without a CAPTCHA when one is required. |
| 403 | MFA_REQUIRED |
One-shot login without an MFA code when one is required. |
| 403 | MFA_FAILED |
MFA code is wrong or expired. |
| 422 | <EMAP code> |
JWXTBusinessError: business-rule rejection (e.g. "evaluation window not open"). |
| 429 | IP_BLOCKED |
The auth gateway has temporarily banned this IP. |
| 500 | CAS_ERROR |
Other uncategorized CAS exceptions. |
| 502 | CAS_PROTOCOL_ERROR |
Malformed CAS response. |
| 502 | JWXT_PROTOCOL_ERROR |
Malformed academic-system response (non-JSON, broken envelope, etc.). |
Body shape:
{"detail": "human-readable message", "code": "NEED_CAPTCHA"}code is only present for rows in the table above. Clients should branch
on code and surface detail for display.
- Treat both headers as session cookies. Anyone holding
X-CAS-CredentialorX-JWXT-Sessioncan impersonate the student. Beyond TLS in transit, clients should guard them as carefully as access tokens. - CORS is off by default. Without
YSU_API_CORS_ORIGINS,CORSMiddlewareis not mounted. Configure the allow-list explicitly when deploying behind a browser front-end. - The credential header is read only when necessary.
/auth/loginand/auth/login/step1ignoreX-CAS-Credential, so the credential never traverses pre-login paths.