Skip to content

TOTP auth#11288

Merged
mekarpeles merged 4 commits intomasterfrom
totp-auth
Mar 25, 2026
Merged

TOTP auth#11288
mekarpeles merged 4 commits intomasterfrom
totp-auth

Conversation

@mekarpeles
Copy link
Copy Markdown
Member

Closes ArchiveLabs/lenny#95

Prototype to implement Time bound One Time Passwords for Lenny services

Introduces an email-based One Time Password (OTP) flow with built-in safeguards.

Endpoints

Issue endpoint (/account/otp/issue)

  • Generates OTPs tied to (service_ip, email, client_ip, timestamp)
  • Enforces rate limiting across email, client IP, and service IP to prevent abuse
  • Supports optional sendmail=true flag to email the OTP directly to the user

Redeem endpoint (/account/otp/redeem)

  • Verifies the submitted OTP against stored/derived values
  • Returns success or failure in JSON

Security & Abuse Prevention

  • Rate limiting

    • OTPs are scoped per minute (10-second rolling loop across ~10 minutes of validity)
    • Attempts are tracked to mitigate brute-force attacks
  • Challenge check

    • Proof of Work & Identity? Service must pass a challenge_url test (verifying it is a running Lenny server with https that returns json)
    • Blocks unauthorized services from abusing the API

Testing

From a Lenny service (must be running https)

curl -X POST "https://staging.openlibrary.org/account/otp/issue" -d "email=client@example.org&ip=127.0.0.1&challenge_url=https://lennyforlibraries.org/v1/api/items&sendmail=true"
curl -X POST "https://staging.openlibrary.org/account/otp/redeem" -d "email=client@example.org&ip=127.0.0.1&otp=XXX"

Screenshot

Stakeholders

Copilot AI review requested due to automatic review settings September 18, 2025 22:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a Time-based One Time Password (TOTP) authentication system for Lenny services, providing email-based OTP flow with built-in security safeguards and rate limiting.

  • Adds new /account/otp/issue and /account/otp/redeem endpoints for OTP generation and verification
  • Implements rate limiting and service verification to prevent abuse
  • Introduces HMAC-based OTP generation with configurable seed and time-based validation

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
openlibrary/plugins/upstream/account.py Adds two new endpoint classes for OTP issue and redeem functionality
openlibrary/core/lending.py Adds OTP seed configuration setup
openlibrary/core/auth.py Implements the core TimedOneTimePassword class with generation, validation, and rate limiting

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@mekarpeles
Copy link
Copy Markdown
Member Author

mekarpeles commented Sep 18, 2025

The OTP challenge needs work: tested with

import requests
r = requests.post("https://staging.openlibrary.org/account/otp/issue", data={"email": "mek@archive.org", "challenge_url": "https://lennyforlibraries.org/api/ia/honore-de-balzac_father-goriot_ellen-marriage/manifest.json", "ip": "127.0.0.1", "sendmail": "true"})

Got {"error": "challenge_failed"}

@mekarpeles mekarpeles marked this pull request as draft September 18, 2025 22:41
@mekarpeles mekarpeles changed the title Totp auth TOTP auth Oct 19, 2025
@mekarpeles mekarpeles marked this pull request as ready for review October 22, 2025 21:24
@mekarpeles mekarpeles requested a review from Copilot October 22, 2025 21:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

payload = f"{service_ip}:{client_email}:{client_ip}:{ts}".encode()
digest = hmac.new(seed.encode('utf-8'), payload, hashlib.sha256).digest()
return cls.shorten(digest)

Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function can raise socket.gaierror if hostname resolution fails, and AttributeError if parsed.hostname is None. Add error handling to prevent crashes.

Suggested change
if not parsed.hostname:
return False
try:
resolved_ip = socket.gethostbyname(parsed.hostname)
except socket.gaierror:
return False

Copilot uses AI. Check for mistakes.
Comment on lines +476 to +477
if i.challenge_url and not OTP.verify_service(i.service_ip, i.challenge_url):
return delegate.RawText(json.dumps({"error": "challenge_failed"}))
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the challenge mechanism doesn't work due to infrastructure limitations, consider removing this code or clearly documenting plans to fix it. Dead code paths reduce maintainability.

Copilot uses AI. Check for mistakes.
@mekarpeles mekarpeles marked this pull request as draft October 27, 2025 20:45
@mekarpeles mekarpeles mentioned this pull request Nov 26, 2025
3 tasks
@mekarpeles mekarpeles added this to the Sprint 2026-03 milestone Mar 24, 2026
@mekarpeles mekarpeles marked this pull request as ready for review March 24, 2026 22:34
- Move OTP/TOTP docs into docs/ai/subsystems/otp_service.md
- Remove network-dependent doctest from otp_service_issue.POST
  (test env blocks network requests; doctest had no assertions anyway)
@mekarpeles mekarpeles merged commit dd3ddb2 into master Mar 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OTP sendmail

2 participants