63 lines
3.0 KiB
Markdown
63 lines
3.0 KiB
Markdown
# TASK-002A: Implement the OAuth PKCE protocol core
|
||
|
||
Status: complete
|
||
Owner: Codex
|
||
Reviewer: Claude
|
||
|
||
## Outcome
|
||
|
||
Provide a deterministic, Android-independent OAuth authorization contract that generates fresh PKCE/state values, requests only approved read scopes, and strictly validates callbacks before any authorization code can be exchanged.
|
||
|
||
## Writable paths
|
||
|
||
- `app/src/main/java/com/ubisam/ubgitea/core/auth/protocol/`
|
||
- `app/src/test/java/com/ubisam/ubgitea/core/auth/protocol/`
|
||
- `docs/decisions/002-oauth-security.md`
|
||
- `tasks/002-oauth-spike.md`
|
||
- `tasks/002a-oauth-protocol-core.md`
|
||
- `tasks/002b-oauth-android-integration.md`
|
||
|
||
## Acceptance criteria
|
||
|
||
- [x] A fresh cryptographically random verifier and state are generated for every authorization attempt.
|
||
- [x] PKCE uses SHA-256 and unpadded Base64 URL encoding with method `S256`.
|
||
- [x] Authorization requests use HTTPS, the registered client ID and redirect URI, and only approved read scopes.
|
||
- [x] Callback validation requires an exact redirect scheme, host, effective port, and path.
|
||
- [x] Callback validation rejects missing, duplicated, mismatched, or expired state before accepting code or error parameters.
|
||
- [x] Missing, blank, or duplicated codes and malformed query encoding are rejected.
|
||
- [x] OAuth denial is represented separately from malformed callbacks.
|
||
- [x] No client secret, token, Android type, or logging appears in the protocol package.
|
||
- [x] Focused tests cover URL construction, freshness, PKCE vectors, scope, redirect validation, state, expiry, denial, code, and malformed input.
|
||
|
||
## Security and privacy
|
||
|
||
This package must remain pure and must never persist or log authorization codes, tokens, verifier values, or state values.
|
||
|
||
## Required checks
|
||
|
||
```text
|
||
./gradlew assembleDebug
|
||
./gradlew testDebugUnitTest
|
||
./gradlew lintDebug
|
||
```
|
||
|
||
## Evidence
|
||
|
||
Recorded on 2026-08-25 using JDK 17 and Android SDK 36:
|
||
|
||
- Focused OAuth protocol tests — 21 tests, 0 failures, 0 errors.
|
||
- `./gradlew assembleDebug` — passed.
|
||
- `./gradlew testDebugUnitTest` — passed; 43 tests across 9 suites, 0 failures, 0 errors.
|
||
- `./gradlew lintDebug` — passed.
|
||
- Sensitive-pattern scan excluding generated and Git metadata — no assigned client secret, token, password, or private-key value found.
|
||
- `git diff --check` — passed.
|
||
- Automatic string representations for transaction, request, authorization code, and denial results redact sensitive values.
|
||
- Actual browser, App Link, token exchange, Keystore, and identity verification remain in TASK-002B.
|
||
|
||
## Review findings
|
||
|
||
- P2 verifier grammar — fixed by enforcing the RFC 7636 unreserved ASCII character set and 43–128 character length; adversarial tests added.
|
||
- P2 callback pollution/encoding — fixed by rejecting conflicting response keys and duplicate recognized parameters, and using strict UTF-8 percent decoding; adversarial tests added.
|
||
- P3 time arithmetic overflow — fixed with saturating comparison boundaries and `Long.MIN_VALUE`/`Long.MAX_VALUE` tests.
|
||
- Independent re-review confirmed all findings closed with no remaining P0-P3 findings.
|