···262262263263Modern third-party ATProto OAuth clients should now be able to discover and authenticate directly against your PDS. The built-in provider enforces both the transition scopes (`transition:generic`, `transition:email`, `transition:chat.bsky`), the granular ATProto permission families (`account:`, `identity:`, `repo:`, `blob:`, and `rpc:`), and `include:<nsid>` permission-set scopes. Permission-set scopes are resolved through lexicon records and compiled down to concrete repo/RPC permissions before tokens are issued, so apps requesting spec-compliant permission bundles still get least-privilege tokens. For example, a client like Tangled will start by fetching `/.well-known/oauth-protected-resource`, follow the advertised authorization-server metadata, submit a pushed authorization request, and then send the browser through `/oauth/authorize`.
264264265265+The local OAuth metadata only advertises the pieces perlsky actually implements today: authorization-code flow with PAR, PKCE `S256`, DPoP, `private_key_jwt` client auth, `response_mode=query`, and interactive `prompt=login` / `prompt=consent`.
266266+265267## First Account
266268267269You can create the first account directly with XRPC:
···286288- `refreshJwt`
287289288290Passwords must be at least 8 characters long.
291291+292292+If you are running without outbound email during smoke/dev work, the safer testing knobs are:
293293+294294+- `testing_auto_confirm_email`: mark new-account emails as confirmed immediately.
295295+- `testing_allow_unauthenticated_email_confirm`: allow `com.atproto.server.confirmEmail` without a bearer token for local testing only.
296296+297297+Both are intended for testing environments. Leave them off in normal deployments.
289298290299If you want to disable open signup, enable `invite_code_required` and mint invite codes locally on the server:
291300
+2-2
docs/TEST_AUDIT.md
···4545- Firehose tests must not assume the smallest possible CAR diff. The reference runtime guarantees normalized behavior, not a minimal encoding.
4646- Label replay and cursor handling need exclusive replay semantics, proper future-cursor rejection, and forward progress across unhandled backlog events.
4747- `com.atproto.repo.listMissingBlobs` needed a real implementation rather than an always-empty placeholder.
4848-- ATProto OAuth `include:<nsid>` permission-set scopes need to be compiled into concrete repo/RPC permissions instead of being echoed back as inert strings.
4848+- ATProto OAuth `include:<nsid>` permission-set scopes are now compiled into concrete repo/RPC permissions before token issuance; local regression coverage pins that least-privilege behavior for supported and unsupported permissions.
4949- Deactivated accounts should still be able to establish and refresh sessions, but those responses must stay marked `active=false` with `status=deactivated`.
5050- Local `app.bsky.*` emulation must be conservative: only synthesize owner-local feed/thread data when the PDS can answer authoritatively, and proxy upstream instead of inventing partial global state.
5151- Account email handling needs consistent normalization on write, lookup, session creation, and confirmation checks; treating email case inconsistently leaves both tests and user-facing auth behavior brittle.
···57575858These are not currently treated as audit failures:
59596060-- Email confirmation remains testing-friendly by explicit user request because email sending is not configured in the current environment.
6060+- Email confirmation remains testing-friendly only behind the explicit `testing_allow_unauthenticated_email_confirm` / `testing_auto_confirm_email` toggles because email sending is not configured in the current environment.
6161- Admin auth still accepts a local bearer-token shortcut, while the official reference PDS expects Basic auth with `admin` credentials.
6262- Self-service invite creation exists only behind `self_service_invite_codes`; default behavior is admin-only invite minting.
6363- Label RPC parity is covered locally, but there is no like-for-like official local-labeler surface to diff against in the same way as core PDS endpoints.
+1-1
lib/ATProto/PDS/API/Server.pm
···441441 });
442442443443 $registry->register('com.atproto.server.confirmEmail', sub ($c, $endpoint) {
444444- if (($c->req->headers->authorization // q()) =~ /\A(?:Bearer|DPoP)\s+/i) {
444444+ if (!$c->config_value('testing_allow_unauthenticated_email_confirm', 0)) {
445445 require_auth(
446446 $c,
447447 audience => TOKEN_AUD_ACCESS,