Skip to main content

Auth.xjtu

auth-xjtu automates authentication for services protected by Xi'an Jiaotong University's unified identity gateway.

The package deliberately separates two features:

  1. Entry discovery finds the authentication entry for pages that expose a login link without redirecting automatically.
  2. Login starts from a redirecting business URL or a known authentication entry and completes the OAuth/CAS chain.

This separation keeps the authentication flow predictable while allowing site-specific discovery rules to evolve independently.

Requirements

  • Python 3.9+
  • pip

Installation

Install the published package:

python -m pip install auth-xjtu

Install a development checkout with test dependencies:

python -m pip install -e ".[dev]"

Login from a business URL

Authenticator.login() follows the application's redirects to the XJTU gateway, performs the current MFA preflight, submits the password form, and follows the authenticated redirect back to the application:

from auth_xjtu import Authenticator, LoginStatus

auth = Authenticator(
    username="your_netid",
    password="your_password",
)

status, message = auth.login("https://gmis.xjtu.edu.cn/pyxx/")
if status is LoginStatus.SUCCESS:
    session = auth.get_session()
    print("Login successful")
else:
    print(f"Login failed: {message}")

The argument may also be a known authentication entry URL. login() handles HTTP redirects but does not scan arbitrary application HTML for login links.

Discover an entry, then log in

When an application page does not redirect and only exposes a login link, use an EntryFinder. Sharing the same session preserves cookies collected during discovery:

from auth_xjtu import Authenticator, EntryFinder, LoginStatus

app_url = "https://portal.example.edu/application"
auth = Authenticator("your_netid", "your_password")

finder = EntryFinder(session=auth.get_session())
entry_url = finder.find(app_url)
status, message = auth.login(entry_url)

if status is LoginStatus.SUCCESS:
    response = auth.get_session().get(app_url, timeout=10)
    response.raise_for_status()

For discovery without a shared session, the convenience function find_entry(app_url) is also available.

Entry discovery strategies

EntryFinder resolves an entry in this order:

  1. a hard-coded URL in a matching SiteRule;
  2. a redirect to a different host or a login-like URL;
  3. CSS selectors configured for the matching site;
  4. generic password-form and scored login-link detection.

Per-site configuration

A site can use a hard-coded path:

from auth_xjtu import EntryFinder, SiteRule

rules = (
    SiteRule(
        hostname="portal.example.edu",
        fixed_entry_url="/sso/start",
    ),
)

entry_url = EntryFinder(rules=rules).find(
    "https://portal.example.edu/application"
)

Or a page-specific CSS selector:

rules = (
    SiteRule(
        hostname="library.example.edu",
        selectors=(
            "a.campus-sso",
            "form#unified-login",
        ),
        keywords=("campus access",),
    ),
)

Rules passed to EntryFinder replace the built-in rule set. Built-in XJTU rules live in auth_xjtu/entry_discovery/rules.py; this is the intended place for small, reviewed site-specific hard-coded entries.

If no generic strategy or rule succeeds, discovery raises EntryDiscoveryError instead of silently treating the application URL as a login entry.

Login status codes

Authenticator.login() returns (LoginStatus, message). LoginStatus is an IntEnum, so it remains compatible with existing integer comparisons.

Value Enum Meaning
0 SUCCESS The session is authenticated.
1 ENTRY_REDIRECT_FAILED The entry URL could not reach the login page.
2 PAYLOAD_CONSTRUCTION_FAILED Tokens, public key, or encrypted payload could not be created.
3 POST_LOGIN_REDIRECT_FAILED The redirect after credential submission failed.
4 LOGIN_SUBMISSION_FAILED Submission or credential preflight was rejected.
5 MFA_REQUIRED The account requires an interactive second factor.

Project layout

src/auth_xjtu/
├── entry_discovery/
│   ├── finder.py       # generic discovery strategies
│   └── rules.py        # per-site configuration and hard-coded entries
├── login/
│   ├── authenticator.py
│   ├── payload.py
│   └── redirects.py
├── authenticator.py    # backward-compatible import
├── exceptions.py
└── __init__.py

tests/
├── unit/               # isolated tests without network or credentials
└── integration/        # live XJTU tests

examples/
└── course_selection/   # read-only GMIS course-list query

Examples

The GMIS course-list example logs in from the GMIS application URL, queries every result page with a current cache-busting timestamp, and can filter results by course name. It is read-only and does not submit course selections.

Migrating from 0.2.x

Version 0.3.0 contains intentional breaking changes:

  • Authenticator.login() now takes start_url; replace keyword calls such as login(dest_app_url=...) with login(start_url=...).
  • Imports from the undocumented auth_xjtu.core modules are no longer supported. Use Authenticator, EntryFinder, SiteRule, and find_entry from the top-level package.
  • The first value returned by login() is now LoginStatus. It is an IntEnum, so existing comparisons such as status == 0 continue to work.
  • Entry discovery and login are separate operations. Use EntryFinder only when a page exposes a login link without redirecting automatically.

Testing

Run the complete local suite:

python -m pytest

Live integration cases skip automatically unless both credentials are available. To run them in PowerShell:

$env:TEST_USERNAME = "your_netid"
$env:TEST_PASSWORD = "your_password"
python -m pytest -m integration

Run only isolated tests:

python -m pytest -m "not integration"

License and disclaimer

This project is licensed under the MIT License.

It is an independent project and is not affiliated with, authorized by, or endorsed by Xi'an Jiaotong University. Use it responsibly and do not commit credentials, cookies, or authentication logs to source control.

Metadata

Release files for auth-xjtu 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for auth-xjtu 0.3.0
File Size Uploaded
auth_xjtu-0.3.0.tar.gz 14.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for auth-xjtu 0.3.0
File Interpreter ABI Platform
auth_xjtu-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.4 kB

Release files / auth_xjtu-0.3.0.tar.gz

Download URL auth_xjtu-0.3.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7648b531c9b0f9b883f8b12d62097e40a1b4dbd891ce15cd618359c6ff02af60
BLAKE2b-256 checksum
How to use checksums
7d9dc7cf0a0e13045752108c78c5420efb505648f3a76d6b46398fe1f9cedc53
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.

Transparency log

Release files / auth_xjtu-0.3.0-py3-none-any.whl

Download URL auth_xjtu-0.3.0-py3-none-any.whl
Size 14.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f62af8d88d9fe76beed4f533314af140abe143388d884642a26173485f89e79b
BLAKE2b-256 checksum
How to use checksums
abdb1f731d63133da37194beae54f253fe2ed1a58695cad607291845bb8ffb0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page