Skip to content

Harden OAuth redirect, secret, and token expiry checks - #58

Closed
grantf04 wants to merge 1 commit into
nick-transition:mainfrom
grantf04:fitapp-oauth-security-hardening
Closed

Harden OAuth redirect, secret, and token expiry checks#58
grantf04 wants to merge 1 commit into
nick-transition:mainfrom
grantf04:fitapp-oauth-security-hardening

Conversation

@grantf04

@grantf04 grantf04 commented May 9, 2026

Copy link
Copy Markdown

Summary

Fixes #6
Fixes #7
Fixes #8

Hardens the OAuth flow by validating registered redirect URIs, comparing client secrets with crypto.timingSafeEqual, and adding expiration to OAuth access tokens. I saw these were proposed as S-size security bounty candidates, but I understand they are not currently formalized live bounties.

Changes

  • Validate redirect_uri against the OAuth client's redirectUris during /authorize, /login, and /callback
  • Replace direct client-secret string comparison with length-guarded crypto.timingSafeEqual
  • Store expiresAt for newly issued OAuth tokens and return expires_in from /token
  • Reject expired OAuth tokens during request authentication, including older tokens that only have createdAt

Type

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactor

Testing

  • Unit tests pass
  • Integration tests pass (if applicable)
  • Manual testing completed

Manual verification:

  • cd functions && npm run build

AI Usage

  • No AI tools used
  • AI-assisted (describe below)

AI tools used: OpenAI Codex
What AI generated: OAuth/security patch implementation and PR text
What was human-reviewed/modified: Security behavior, redirect validation coverage, token expiry behavior, and TypeScript build output

Screenshots

No UI changes.

@nick-transition

Copy link
Copy Markdown
Owner

Thank you @grantf04 for taking this on — and apologies that it sat in review as long as it did. Your fix was correct: I reviewed it against #6/#7/#8 and all three holes were properly closed, with sensible coverage of the /authorize, /login, and /callback flows and a clean length-guarded timingSafeEqual.

This ended up as a head-to-head with #59, which fixed the same three issues, and I could only merge one. I went with #59 on two narrow technical points: it rejects legacy tokens that have neither expiresAt nor createdAt (fail-closed, where this PR would let them live forever), and it hashes both secrets to fixed-length digests before comparing, which avoids even the length signal from an early length-check return. Nothing about that diminishes the work here — two independent, correct implementations of the same fix is a good problem for a project to have, and yours was the first one in.

I'd genuinely welcome more contributions from you. The exercise-library issues (#62, #63) are open, and the roadmap in ROADMAP.md has plenty more — and if you comment on an issue first per BOUNTIES.md/CONTRIBUTING.md, we can avoid the duplicate-work situation that happened here. Thanks again.

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.

Security: Client secret comparison not timing-safe Security: Access tokens never expire Security: OAuth redirect_uri not validated

2 participants