A simple and lightweight API to authenticate PESU credentials using PESU Academy.
The API is secure and protects user privacy by not storing any user credentials. It only validates credentials and returns the user's profile information. No personal data is stored.
PESUAuth checks the username and password with PESU Academy, through the same API
that the PESU Academy mobile app uses. If PESU Academy accepts them, the request succeeds; if it rejects them,
PESUAuth answers 401. If you ask for the profile, PESUAuth fetches it too and returns it as a
ProfileObject.
Credentials are only ever sent to PESU Academy, over HTTPS, and are never stored or logged. Nothing from one user's sign-in is shared with another's.
Note
PESU Academy's mobile API is not publicly documented and can change without notice. If it does, /authenticate
may answer 422 or 502, or return null for the profile fields that changed, until PESUAuth is updated to match.
- You can access the PESUAuth API endpoints here.
- You can view the health status of the API on the health check pages for production and staging.
- You can view detailed metrics and KPIs for both environments on the
PESUAuth metrics dashboard,
built from the counters that
/metricsexposes.
Note
All timestamps are in UTC.
Warning
Both environments are hosted on free tier servers located in Singapore. As a result, you might experience higher latencies and slower response times, compared to running the API locally or on a server closer to your location.
Running the PESUAuth API locally is simple. Clone the repository and follow the steps below to get started.
Tip
We recommend running the API locally using Docker for ease of use, the best performance, and lowest latency.
This is the easiest and recommended way to run the API locally. Ensure you have Docker installed on your system. Run the following commands to start the API.
-
Build the Docker image either from the source code or pull the pre-built image from Docker Hub.
-
You can build the Docker image from the source code by running the following command in the root directory of the repository.
docker build . --tag pesu-auth -
You can also pull the pre-built Docker image from Docker Hub by running the following command:
docker pull pesudev/pesu-auth:latest
-
-
Run the Docker container
docker run --name pesu-auth -d -p 5000:5000 pesu-auth # If you pulled the pre-built image, use the following command instead: docker run --name pesu-auth -d -p 5000:5000 pesudev/pesu-auth:latest -
Access the API at
http://localhost:5000/
If you don't have Docker installed, you can run the API natively. Ensure you have Python 3.14 or higher
installed on your system. We recommend using a package manager like uv to manage
dependencies.
-
Install the dependencies.
uv sync
-
Run the API using the following command.
uv run python -m app.app
You can set the port with
--portorPORT. The priority order is--port, thenPORT, then the default of5000.PORT=8080 uv run python -m app.app
-
Access the API as previously mentioned on
http://localhost:5000/
The API provides multiple endpoints for authentication, documentation, and monitoring.
| Endpoint | Method | Description |
|---|---|---|
/ |
GET, HEAD |
Serves the interactive API Explorer. |
/docs |
GET, HEAD |
Serves the standard Swagger UI documentation. |
/redoc |
GET, HEAD |
Serves the ReDoc API documentation. |
/openapi.json |
GET, HEAD |
Serves the OpenAPI specification used by both UIs. |
/authenticate |
POST |
Authenticates a user using their PESU credentials. |
/health |
GET, HEAD |
A health check endpoint to monitor the API's status. |
/metrics |
GET, HEAD |
Exposes traffic and error counters. See fmt below. |
/readme |
GET, HEAD |
Redirects to the project's official GitHub repository. |
The Explorer builds request controls and response examples from /openapi.json. Enter the
required credentials, choose whether to fetch a profile, and optionally select profile fields.
Unselected fields are omitted, so requesting a profile with no fields selected returns the full
profile. Copy request copies a curl command for the current inputs; Send request calls the
API. The response's Copy button copies only the body, without its status or timing. Next to the
name, the Explorer shows the version from /openapi.json and the environment from /health
(development, staging or production).
Every response includes the following baseline security headers:
X-Content-Type-Options: nosniffReferrer-Policy: no-referrerX-Frame-Options: DENY
Responses to /authenticate and /health also include:
Cache-Control: no-store
You can send a request to the /authenticate endpoint with the user's credentials and the API will return a JSON
object, with the user's profile information if requested.
| Parameter | Optional | Type | Default | Description |
|---|---|---|---|---|
username |
No | str |
The user's SRN, PRN, email address, or phone number. Maximum length is 96 characters. | |
password |
No | str |
The user's password, up to 128 characters, counted exactly as typed. It is sent only to PESU Academy, and never stored or logged. | |
profile |
Yes | boolean |
False |
Whether to fetch profile information. This makes a second call to PESU Academy, so it takes longer |
fields |
Yes | list[str] |
None |
Which ProfileObject fields to return. A list can only be given when profile is true; otherwise the request is a 400. If not provided or null, all fields are returned. Repeated names are ignored. Fields come back in the table's order, whatever order they are asked for in |
The request body is validated strictly. A missing or empty username or password, one that is not valid text, a username with a
line break or another invisible character, a value of the wrong type (such as
the string "true" for profile), an unknown key, an empty fields list, fields without profile: true, or an unknown field name is rejected with a
400. So is a body that cannot be read at all, such as JSON that is not valid UTF-8, with the fixed message
Could not parse request body.
| Code | When |
|---|---|
200 |
The credentials are valid. profile is included if it was requested |
400 |
The request body failed validation, as described above |
401 |
PESU Academy rejected the credentials: a wrong password, or a user that does not exist |
422 |
PESU Academy's profile response could not be parsed or held no profile, which means their API changed. Only when the profile was requested |
500 |
An unexpected failure, rendered by the catch-all handler |
502 |
PESU Academy could not be reached, timed out, or answered the login or profile request unexpectedly |
Every error this API renders carries the same {status, message, timestamp} body, with status set to false. That
includes a 404 for an unknown path and a 405 for an unsupported method, whose Allow header lists the methods
the path is served for.
On authentication, it returns the following parameters in a JSON object. If the authentication was successful and
profile data was requested, the response's profile key will store a dictionary with a user's profile information.
On an unsuccessful sign-in, this field will not exist.
| Field | Type | Description |
|---|---|---|
status |
boolean |
A flag indicating whether the overall request was successful |
profile |
ProfileObject |
A nested map storing the profile information, returned only if requested |
message |
str |
A message that provides information corresponding to the status |
timestamp |
datetime |
The time of the request, as an ISO 8601 timestamp in IST (+05:30) |
This object contains the user's profile information, which is returned only if the profile parameter is set to True.
If the authentication fails, this field will not be present in the response.
Only the fields in the table below can be requested; any other name in fields is a 400. A requested field is never left out for having no value, and any field can be null when PESU Academy has no value for it, does not send it, or sends it in an
unexpected shape. PESU Academy marks a missing value with null or an empty string, and both become null; any other text, "NA" included, is returned as
PESU Academy stores it, with surrounding whitespace trimmed. Fields come back in the order of the table below.
| Field | Type | Description |
|---|---|---|
name |
str |
Full name of the user |
prn |
str |
PRN of the user: PES, the campus digit, the year of joining and a 5-digit number, such as PES1202000001 |
srn |
str |
SRN of the user: PES, the campus digit, the program (UG, PG, ...), the last two digits of the year of joining, the branch and a 3-digit number, such as PES1UG20CS001. For students who joined before SRNs were introduced, it is the same as their PRN |
program |
str |
Academic program as PESU Academy writes it, such as B.Tech.; PESU Academy sends no full name. |
branch |
str |
Full name of the branch, such as Computer Science and Engineering |
semester |
str |
Current semester, as PESU Academy writes it, such as Sem-4 |
section |
str |
Current section, such as Section C |
email |
str |
Email address registered with PESU |
mobile |
str |
Mobile number registered with PESU |
campusCode |
int |
1 for PES University (Ring Road), 2 for PES University (Electronic City); null for any other campus name |
campus |
str |
Name of the campus's institute as PESU Academy writes it, such as PES University (Ring Road) |
firstName |
str |
First name of the user |
middleName |
str |
Middle name of the user, or null if they have none |
lastName |
str |
Last name of the user |
branchShortCode |
str |
Abbreviation of the branch, such as CSE |
gender |
str |
Gender of the user, as recorded by PESU |
dateOfBirth |
str |
Date of birth, as YYYY-MM-DD |
Everything else in PESU Academy's profile information, such as the photo, blood group, addresses, parents' details and marks, is
discarded and never returned, and cannot be requested in fields.
Answers 200 whenever the process is serving; a 500 would come from the catch-all handler, as on any other endpoint.
This endpoint can be used to check the health of the API. It's useful for monitoring and uptime checks. This endpoint
does not take any request parameters. Like every GET endpoint, it also answers HEAD, with the same status and
headers and no body, for monitors that check that way.
| Field | Type | Description |
|---|---|---|
status |
boolean |
true if healthy, false if there was an error |
message |
str |
"ok" if healthy, error message otherwise |
timestamp |
datetime |
A timezone offset timestamp indicating the time of the health check |
version |
str |
The version of pesu-auth that is running |
environment |
str |
The deployment this instance runs as: development, staging or production |
environment comes from the PESU_AUTH_ENVIRONMENT environment variable, read once at startup. It is development
when the variable is unset, and any value other than development, staging or production stops the app from
starting. See Configuration.
This endpoint exposes counters describing the traffic this process has served and the work it did to serve it. The counters are kept in memory and reset to zero when the process restarts. It takes no request parameters other than the format selector below.
| Field | Type | Description |
|---|---|---|
fmt |
str |
prometheus (default) for the text exposition format, or json for the same counters |
The default is prometheus because that is what a scraper pointed at this path expects. An unrecognised value is a
400, like any other validation failure.
curl http://localhost:5000/metrics # Prometheus text, for a scraper
curl http://localhost:5000/metrics?fmt=json | jq # the same numbers, for a humanThe endpoint is open unless the METRICS_TOKEN environment variable is set. When it is, every request must carry that
token as a bearer token:
METRICS_TOKEN=<token> uv run python -m app.app
curl -H "Authorization: Bearer <token>" http://localhost:5000/metrics| Code | When |
|---|---|
200 |
The counters, in the format named by fmt |
400 |
fmt was something other than prometheus or json |
401 |
A token is configured and the request did not carry it. Carries WWW-Authenticate: Bearer |
500 |
An unexpected failure, rendered by the catch-all handler like on any other endpoint |
See docs/metrics.md for how the counters are collected, what each metric means, full example responses, and how to protect and scrape the endpoint.
This endpoint redirects to the project's official GitHub repository with a 308, and takes no request parameters. A 500 would come from the catch-all handler, as on any other endpoint.
Here is a request for a user's full profile, and the response.
curl -X POST http://localhost:5000/authenticate \
-H "Content-Type: application/json" \
-d '{
"username": "your SRN, PRN, email or phone number here",
"password": "your password here",
"profile": true
}'{
"status": true,
"profile": {
"name": "John Doe",
"prn": "PES1202000001",
"srn": "PES1UG20CS001",
"program": "B.Tech.",
"branch": "Computer Science and Engineering",
"semester": "Sem-4",
"section": "Section C",
"email": "johndoe@gmail.com",
"mobile": "1234567890",
"campusCode": 1,
"campus": "PES University (Ring Road)",
"firstName": "John",
"middleName": null,
"lastName": "Doe",
"branchShortCode": "CSE",
"gender": "Male",
"dateOfBirth": "2002-01-31"
},
"message": "Login successful.",
"timestamp": "2024-07-28T22:30:10.103368+05:30"
}Made with ❤️ by
Powered by contrib.rocks
If you'd like to contribute, please follow our contribution guidelines. How the service works,
and how to develop, test and deploy it, is documented in docs/.