Owner Abhishek P

V1 API Auth

Source: OpenAPI 3.1.0 spec + src/api/v1/user/routes.py, src/api/v1/user/schemas.py Tag: Auth - Login, OTP, token refresh, logout.


POST /auth/send-email-otp

Summary: Request OTP

Send a one-time password (OTP) to the provided email for authentication.

Auth: None required (public endpoint)

Parameters

NameInRequiredDescription
platformqueryrequiredLogin platform: web, ios, ios_t, an, an_t
applicationqueryrequiredApplication identifier: wri, prx, arivu
device_idqueryoptionalDevice ID for mobile platforms
x-dev-timeheaderoptionalEpoch time in ms (13 digits) from client device

Request Body

{
  "email": "user@example.com",
  "encrypted_data": null
}

Conditions:

  • At least one of email or encrypted_data must be provided; error code 1003 (INVALID_PARAMETERS) if neither
  • When encrypted_data is provided, it is decrypted using CryptoV2.2 (secret=device_id, salt=x_dev_time) to extract the email
  • After decryption, email must be non-empty; error code 1003 if empty

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": {
    "message": "OTP sent successfully to user@example.com",
    "expiry_time": 1714400300000,
    "oti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "error": null,
  "app_actions": null
}
FieldTypeDescription
messagestringConfirmation message
expiry_timeintegerEpoch ms when OTP expires
otistringOne-time identifier for this OTP request (pass to verify)

Error Responses

{
  "status": "failure",
  "is_data_encrypted": 0,
  "data": null,
  "error": {
    "code": 1003,
    "message": "Either email or encrypted_data is required"
  },
  "app_actions": null
}

POST /auth/verify-email-otp

Summary: Verify OTP

Verify the OTP sent to the email and generate authentication tokens.

Auth: None required (public endpoint)

Parameters

NameInRequiredDescription
platformqueryrequiredLogin platform
applicationqueryrequiredApplication identifier
device_idqueryoptionalDevice ID for mobile platforms
course_idqueryoptionalCourse ID - when provided, includes subscription details and creates default bookmark collection
x-dev-timeheaderoptionalEpoch ms (13 digits)

Request Body

{
  "email": "user@example.com",
  "otp": "123456",
  "oti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "encrypted_data": null
}

Conditions:

  • Either encrypted_data OR all three of (email, otp, oti) must be provided; error 1003 if neither
  • When encrypted_data is set, decrypts to extract email/otp/oti using CryptoV2.2
  • otp length must match server-configured OTP length

Business Logic

  1. Verifies OTP against cache and revokes the OTI (one-time use)
  2. Creates user if not exists via get_or_create_user_by_email()
  3. If course_id provided: creates default “All Bookmarks” collection if missing
  4. Generates JWT access token + refresh token
  5. If course_id provided: generates CloudFront signed cookies (TTL = access_token_expiry + 60 minutes)

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "bearer",
    "user_details": {
      "id": "507f1f77bcf86cd799439011",
      "email": "user@example.com",
      "username": null,
      "phone_number": null,
      "is_phone_validated": false,
      "name": {
        "first_name": "John",
        "last_name": "Doe"
      },
      "date_of_birth": null,
      "gender": 3,
      "bio": null,
      "background_info": null,
      "attempt_number": null,
      "attempt_year": null,
      "profile_picture": null,
      "address": null,
      "social_links": [],
      "mcq_stats": null,
      "subscription": null
    },
    "signed_cookies": {
      "cookies": {
        "CloudFront-Policy": "eyJTdGF0ZW1lbnQiOlt7...",
        "CloudFront-Signature": "abc123...",
        "CloudFront-Key-Pair-Id": "KXYZ123..."
      },
      "domain": "cdn.example.com"
    }
  },
  "error": null,
  "app_actions": null
}

Notes:

  • signed_cookies is null when course_id is not provided
  • mcq_stats and subscription inside user_details are null when course_id is not provided
  • Auth responses are always plaintext (never encrypted) regardless of feature flag

Error Responses

Error CodeCondition
1003Missing email/otp/oti or encrypted_data
2002OTP is incorrect
2003OTP has expired
2008OTI has expired

POST /auth/login/apple/mobile

Summary: Apple Mobile Login

Handle Apple Sign In for native iOS/Android clients using identity token.

Auth: None required (public endpoint)

Parameters

NameInRequiredDescription
course_idqueryoptionalCourse ID - when provided, includes subscription and creates default bookmark collection
x-dev-timeheaderoptionalEpoch ms (13 digits)

Request Body

{
  "token": "eyJraWQiOiJXNldjT0...",
  "device_id": "device-uuid-12345",
  "platform": "ios",
  "application": "arivu",
  "first_name": "John",
  "last_name": "Doe"
}
FieldRequiredDescription
tokenrequiredApple identity token (ASAuthorizationAppleIDCredential.identityToken)
device_idrequiredUnique device ID
platformrequiredios, ios_t, an, an_t
applicationrequiredwri, prx, arivu
first_nameoptionalOnly provided on initial Apple Sign In
last_nameoptionalOnly provided on initial Apple Sign In

Business Logic

  1. Validates Apple identity token via get_apple_user_mobile() to extract apple_sso_id and email
  2. Creates or retrieves user by Apple SSO ID
  3. If course_id provided: creates default bookmark collection + generates CloudFront cookies
  4. Returns JWT tokens

Success Response

Same shape as POST /auth/verify-email-otp response (TokenResponse with user_details and optional signed_cookies).

Error Responses

Error CodeCondition
2005Apple token verification failed (SSO_ERROR)

POST /auth/login/google/mobile

Summary: Google Login Mobile

Handle Google authentication for mobile devices using ID token.

Auth: None required (public endpoint)

Parameters

NameInRequiredDescription
course_idqueryoptionalCourse ID
x-dev-timeheaderoptionalEpoch ms (13 digits)

Request Body

{
  "token": "eyJhbGciOiJSUzI1NiIs...",
  "device_id": "device-uuid-12345",
  "platform": "an",
  "application": "arivu"
}

All fields required.

Business Logic

  1. Validates Google ID token via get_google_user_mobile() to extract google_sso_id, email, picture, first_name, last_name
  2. Creates or retrieves user by Google SSO ID
  3. If course_id provided: default bookmark collection + CloudFront cookies
  4. Returns JWT tokens

Success Response

Same shape as POST /auth/verify-email-otp response.

Error Responses

Error CodeCondition
2005Google token verification failed (SSO_ERROR)

POST /auth/refresh

Summary: Refresh Token

Generate a new access token using a valid refresh token.

Auth: None required (uses refresh token in body)

Parameters

NameInRequiredDescription
course_idqueryoptionalCourse ID - when provided, generates CloudFront cookies
x-dev-timeheaderoptionalEpoch ms (13 digits)

Request Body

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "grant_type": "refresh_token"
}
FieldRequiredDescription
refresh_tokenrequiredValid refresh token JWT
grant_typerequiredMust be exactly "refresh_token"

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "bearer",
    "user_details": { ... },
    "signed_cookies": { ... }
  },
  "error": null,
  "app_actions": null
}

Error Responses

Error CodeCondition
3000Token invalid or revoked
3003Refresh token expired

POST /auth/logout

Summary: Logout

Invalidate the current session’s refresh token.

Auth: Bearer Token required

Parameters

NameInRequiredDescription
x-dev-timeheaderoptionalEpoch ms (13 digits)

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": {
    "message": "Successfully logged out"
  },
  "error": null,
  "app_actions": null
}

Note: Response data is encrypted when encryption feature flag is enabled.


GET /auth/me

Summary: Get Current User

Retrieve the details of the currently authenticated user.

Auth: Bearer Token required

Parameters

NameInRequiredDescription
course_idqueryoptionalWhen provided: includes mcq_stats and subscription in response
x-dev-timeheaderoptionalEpoch ms (13 digits)

Business Logic

  1. Fetches user details from the authenticated JWT claims
  2. If course_id provided:
    • Fetches MCQ stats via CustomTestService.get_mcq_stats(user_id, course_id)
    • Fetches active subscription via SubscriptionService.get_active_for_user_and_course(user_id, course_id)

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "email": "user@example.com",
    "username": "johndoe",
    "phone_number": {
      "code": "+91",
      "value": "9876543210"
    },
    "is_phone_validated": true,
    "name": {
      "first_name": "John",
      "last_name": "Doe"
    },
    "date_of_birth": 946684800000,
    "gender": 1,
    "bio": "Preparing for UPSC 2026",
    "background_info": "Engineering graduate",
    "attempt_number": 2,
    "attempt_year": 2026,
    "profile_picture": {
      "filename": "profile_507f1f77.jpg",
      "width": 400,
      "height": 400,
      "aspect_ratio": "1:1"
    },
    "address": {
      "street": "123 Main St",
      "city": "Bangalore",
      "state": "Karnataka",
      "zip_code": "560001",
      "country": "India"
    },
    "social_links": [
      {
        "platform": "linkedin",
        "url": "https://linkedin.com/in/johndoe"
      }
    ],
    "mcq_stats": {
      "total_count": 150,
      "pyq_count": 100,
      "dq_count": 30,
      "eq_count": 20
    },
    "subscription": {
      "id": "60d5ec49f1b2c72e4c8b4567",
      "plan_id": "60d5ec49f1b2c72e4c8b1234",
      "order_id": "60d5ec49f1b2c72e4c8b7890",
      "duration_in_days": 365,
      "starts_at": 1714400000000,
      "expires_at": 1745936000000,
      "status": 1,
      "subscription_components": [
        {
          "content_type": "taxonomy",
          "content_id": "all"
        }
      ],
      "plan_components": null
    }
  },
  "error": null,
  "app_actions": null
}

Notes:

  • mcq_stats is null when course_id is not provided
  • subscription is null when course_id is not provided or user has no active subscription
  • subscription.status: 1=ACTIVE, 2=EXPIRED, 3=CANCELLED
  • date_of_birth is stored as midnight UTC epoch ms
  • gender: NON_BINARY=0, MALE=1, FEMALE=2, OTHER=3
  • Response data is encrypted when feature flag is enabled