Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# AI Agent Guidelines for Auth0.Android SDK
# AI Agent Guidelines for Auth0.Android

See [CLAUDE.md](CLAUDE.md) for all coding guidelines, commands, project structure, code style, testing conventions, and boundaries.

This file exists so that non-Claude AI agents (Codex CLI, Gemini CLI, etc.) read the same instructions. All guidelines are maintained in a single place (`CLAUDE.md`) to avoid duplication and drift.
@./CLAUDE.md for all coding guidelines, commands, project structure, code style, testing conventions, and boundaries.
783 changes: 79 additions & 704 deletions CLAUDE.md

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions references/code-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Code Style Reference — Auth0.Android

## CI-enforced rules (hard failures)

- **Explicit API mode** (`-Xexplicit-api=strict`): every public declaration needs an explicit visibility modifier AND explicit return type. Compiler rejects implicit visibility.
- **Java 17** source/target (`sourceCompatibility`/`targetCompatibility = VERSION_17`).
- **LF line endings** — `.editorconfig` (`end_of_line = lf`).
- **Android Lint** — `abortOnError = true`; lint errors block the build.

## Naming conventions

| Element | Pattern | Example |
|---------|---------|---------|
| Class / Object | PascalCase | `WebAuthProvider`, `Auth0UserAgent` |
| Exception | `{Domain}Exception` | `AuthenticationException`, `CredentialsManagerException` |
| Function / Method | camelCase | `login()`, `awaitCredentials()` |
| Constant | `UPPER_SNAKE_CASE` | `HEADER_NAME`, `KEY_TOKENS` |
| Enum member | PascalCase | `Factor.OTP` |

## Dual async API (required for Java consumers)

```kotlin
// ✅ Both forms required for every async public method
fun getCredentials(callback: Callback<Credentials, CredentialsManagerException>)
suspend fun awaitCredentials(): Credentials
```

## Explicit visibility (required)

```kotlin
// ✅ Correct
public class Auth0UserAgent public constructor(name: String) {
public val value: String
}
Comment thread
sanchitmehtagit marked this conversation as resolved.

// ❌ Rejected by -Xexplicit-api=strict
class Auth0UserAgent(name: String) {
val value: String
}
```

## Typed exceptions — not string matching

```kotlin
// ✅ Correct
catch (e: CredentialsManagerException) {
when { e.isNoCredentials -> ... }
}

// ❌ Brittle
catch (e: Exception) {
if (e.message?.contains("no_credentials") == true) { ... }
}
```

## Error hierarchy

`Auth0Exception` → `AuthenticationException` / `CredentialsManagerException` / `DPoPException` / `MyAccountException`

Always throw and catch from this hierarchy; never throw raw `Exception` or `RuntimeException` from public API.
41 changes: 41 additions & 0 deletions references/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Commands Reference — Auth0.Android

Verified against `.github/workflows/test.yml` and `auth0/build.gradle`.

## CI pipeline (exact command from `test.yml`)

```bash
./gradlew testReleaseUnitTest jacocoTestReleaseUnitTestReport lintRelease --continue --console=plain
```

## Individual tasks

```bash
# Unit tests (release variant)
./gradlew testReleaseUnitTest

# Unit tests + JaCoCo coverage report
./gradlew testReleaseUnitTest jacocoTestReleaseUnitTestReport

# Lint (release variant, matches CI)
./gradlew lintRelease

# Build SDK (debug + release AARs)
./gradlew auth0:assemble

# Build release AAR only
./gradlew auth0:assembleRelease

# Build sample app
./gradlew sample:assembleDebug

# Check explicit-API compliance (fails on implicit visibility)
./gradlew auth0:compileReleaseKotlin

# Clean
./gradlew clean
```

## Coverage

JaCoCo reports land in `auth0/build/reports/jacoco/` after `jacocoTestReleaseUnitTestReport`. Codecov uploads automatically in CI. Thresholds: 80% patch target, 1% project degradation max (`codecov.yml`). `CryptoUtil.java` is excluded.
25 changes: 25 additions & 0 deletions references/docs-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Docs Update Rules — Auth0.Android

## Tracked docs

| Doc | What it covers |
|-----|---------------|
| `README.md` | Installation (Gradle coords), requirements (minSdk 26, Java 17), Auth0 dashboard config, `AndroidManifest.xml` setup, quick-start login/logout, ProGuard rules |
| `EXAMPLES.md` | Full Kotlin + Java usage for all features: WebAuthProvider, CredentialsManager, SecureCredentialsManager, AuthenticationAPIClient, MFA, DPoP, My Account API, passkeys, bot protection, PAR, SSO |

Migration guides (`V4_MIGRATION_GUIDE.md`) are not tracked as fixed docs — filename is version-specific, inferred from the target branch at breaking-change time.

## Code-to-docs mapping (library shape)

| When this changes | Update |
|-------------------|--------|
| Public API entry point (`Auth0`, `WebAuthProvider`, `AuthenticationAPIClient`, `CredentialsManager`, `SecureCredentialsManager`, `MyAccountAPIClient`) | `README.md` quick-start, `EXAMPLES.md` affected samples |
| Constructor params or config options on public classes | `README.md` configuration section |
| `AndroidManifest.xml` changes (new activity, intent filter, permission) | `README.md` setup section |
| New authentication flow or major feature | `EXAMPLES.md` new section with Kotlin + Java sample |
| Public method or class added | `EXAMPLES.md` usage sample |
| Public method, property, or class removed or renamed | `README.md` + `EXAMPLES.md` remove/update references |
| SDK installation coordinates or minSdk/Java requirements changed | `README.md` installation + requirements |
| ProGuard/R8 rules changed | `README.md` ProGuard section |

> Never defer docs to a follow-up PR. A PR that ships a new public method without an `EXAMPLES.md` entry is incomplete.
35 changes: 35 additions & 0 deletions references/git-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Git Workflow Reference — Auth0.Android

## Branch naming

| Type | Pattern | Example |
|------|---------|---------|
| Feature | `feat/{name}` | `feat/passkey-enrollment` |
| Fix | `fix/{issue-id}-{name}` | `fix/992-dpop-nonce-retry` |
| Chore | `chore/{name}` | `chore/bump-okhttp` |
| Release | `release/{version}` | `release/4.1.0` |
| Docs | `docs/{name}` | `docs/update-dpop-examples` |

## Commit messages

Format: `{type}({scope}): {description}` — under 70 chars, imperative mood.

Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `perf`, `ci`

Scope (optional): `dpop`, `storage`, `mfa`, `provider`, `credentials`, `myaccount`

## Pull requests

Template: `.github/PULL_REQUEST_TEMPLATE.md` — describe what changed and why, list methods/classes added/removed, add testing instructions, check the three-item checklist.

Required before merge: `test.yml` + `codeql.yml` + `sca_scan.yml` pass, at least one reviewer approval.

Merge strategy: squash or rebase for linear history.

## Pre-commit checklist

```bash
./gradlew testReleaseUnitTest jacocoTestReleaseUnitTestReport lintRelease --continue --console=plain
```

Then: confirm coverage >= 80% for the patch, update `README.md`/`EXAMPLES.md` if public API changed, scan staged diff for secrets before pushing.
25 changes: 25 additions & 0 deletions references/pitfalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Common Pitfalls — Auth0.Android

## 1. Async test race — callback fires after assertion

Always use `Awaitility.await()` for callback tests and `runTest {}` for coroutine tests. A bare `assertThat` after starting a callback operation is a race — the callback may not have fired yet.

## 2. DPoP nonce expiry — missing retry on 401

Server DPoP nonce expiry returns 401 + `DPoP-Nonce` header. Ensure the retry path in `RetryInterceptor` is covered by a test mocking 401-then-200 with a new nonce. Missing it means all DPoP requests fail after nonce refresh.

## 3. Keystore init failure — silent fallback to plaintext

`SecureCredentialsManager` must throw a `CredentialsManagerException` on Keystore init failure — never silently downgrade to unencrypted storage. If you touch `CryptoUtil` or `SecureCredentialsManager`, verify the failure path is explicit.

## 4. Forgetting Java interoperability

Adding a `suspend`-only public method breaks Java consumers. Every new async public method needs both callback and `suspend` forms. Verify by checking or writing a Java test.

## 5. Explicit API mode fails in CI but not always in IDE

IntelliJ may not flag missing visibility modifiers, but `./gradlew auth0:compileReleaseKotlin` will reject them. Run it locally after adding any new public class, function, or property.

## 6. New request path missing Auth0-Client header

Creating an `OkHttpClient` or `NetworkingClient` outside `RequestFactory` bypasses the `Auth0-Client` header. Always route new outbound requests through `RequestFactory`.
57 changes: 57 additions & 0 deletions references/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Testing Reference — Auth0.Android

## Framework versions

| Tool | Version |
|------|---------|
| JUnit | 4.13.2 |
| Robolectric | 4.15.1 |
| Mockito Core | 5.14.0 |
| Mockito-Kotlin | 5.4.0 (`org.mockito.kotlin`) |
| MockWebServer | 4.12.0 |
| okhttp-tls | 4.12.0 |
| Awaitility | 1.7.0 |
| kotlinx-coroutines-test | 1.10.2 |
| Espresso Intents | 3.6.1 |

## Test locations

```text
auth0/src/test/java/com/auth0/android/
├── authentication/ # AuthenticationAPIClient + request tests
├── authentication/storage/ # CredentialsManager + SecureCredentialsManager tests
├── dpop/ # DPoP tests
├── myaccount/ # MyAccountAPIClient tests
├── provider/ # WebAuthProvider + browser flow tests
├── request/ # Request interface + internal tests
└── result/ # Response parsing tests
```

## Coverage

- Tool: JaCoCo (`gradle/jacoco.gradle`)
- Excluded: `CryptoUtil.java` (hardware Keystore-dependent)
- Patch target: 80% (Codecov `codecov.yml`)

## Run command

```bash
# Safe unit-only — no credentials required
./gradlew testReleaseUnitTest jacocoTestReleaseUnitTestReport lintRelease --continue --console=plain
```

## Conventions

**Runner:** `@RunWith(RobolectricTestRunner::class)` for any test needing Android framework APIs.

**HTTP mocking:** `MockWebServer` with `okhttp-tls`; use `AuthenticationAPIMockServer` fixtures in `src/test/.../util/`.

**Async — callbacks:** Use `Awaitility.await().atMost(...)` — never `Thread.sleep()`.

**Async — coroutines:** Use `runTest { }` from `kotlinx-coroutines-test`.

**Mocking:** Mockito-Kotlin (`mock<T>()`, `whenever`, `verify`, `argumentCaptor`). Avoid PowerMock in new tests — the project is removing it.

**Coverage:** Every new public method needs at least one success test and one failure test. For callbacks verify `onSuccess` and `onFailure` separately.

**Biometric/Keystore:** Mock `CryptoUtil` via constructor injection — Robolectric does not support hardware-backed keys.
Loading