Sessions

VTOP keeps the session on the server, keyed by a JSESSIONID cookie. A client holding that cookie and the post-login CSRF token can make data requests without authenticating at all.

That makes a session portable, which is what lets a caller avoid holding a live client in memory.

Exporting and restoring

# once, wherever you can prompt for an OTP
async with VtopClient("REGNO", "password") as client:
    await client.login()
    session = client.session          # RestorableSession

# later, in another process, with no credentials
client = VtopClient.restore(
    session.registration_number,
    session.cookie,
    session.csrf_token,
    user_agent=session.user_agent,
)
attendance = await client.get_attendance(sem_sub_id)

A restored client holds no password. If the session has expired it cannot log back in, and raises VtopSessionError saying so, rather than attempting a login it cannot complete.

Carrying an OTP challenge

An authenticated session is only half the problem. If VTOP interrupts the login with an OTP, the challenge is raised while you are answering one request and the OTP arrives on the next — by which point the client that raised it is gone.

A pending challenge carries the same way:

try:
    await client.login()
except VtopLoginOtpRequiredError:
    challenge = client.otp_challenge     # hand this back to the caller

# ... a later request, another process ...
client = VtopClient.restore_otp_challenge(
    challenge.registration_number,
    challenge.cookie,
    challenge.csrf_token,
    user_agent=challenge.user_agent,
)
await client.verify_login_otp(otp)       # or client.resend_login_otp()
session = client.session                 # now a full session

A restored challenge is deliberately not authenticated. Asking it for data raises VtopSessionError telling you to answer the OTP first, so a half-finished login cannot be mistaken for a usable one.

Warning

An OtpChallenge is a credential too. Credentials and captcha have already been accepted, so anyone holding one can finish the login as soon as they have the OTP.

Warning

Treat the exported values as credentials. Anyone holding them can read the student’s records for as long as VTOP keeps the session alive.

When a session dies

An expired CSRF token does not redirect you to the login page. Spring’s CSRF filter refuses the request before it is routed, so Tomcat answers with its own 404. The client recognises this and raises VtopSessionError with a 401, because the caller’s problem is authentication rather than a missing page.

Two CSRF tokens

There is a pre-login token used to authenticate, and a different post-login token that every data request needs. They are not interchangeable. The second is on LoggedInStudent.post_login_csrf_token and is what session exports.

The User-Agent

Each client pins one User-Agent for the life of its session, so the session presents a consistent identity. As of 2026-09-17 VTOP was not observed to require that a reused session match the agent that created it — an exported session was reused successfully from a completely different agent. Passing the original value is advisory, not required.