Fix three false positives surfaced by real-world OAuth clients
Running `test oauth client` against 16 deployed atproto OAuth clients
flagged 10 as non-conforming, all false positives traceable to three
gaps in this tester:
1. Scope grammar rejected `<resource>?<query>` (no positional). The
atproto permission grammar
(<https://atproto.com/specs/permission#scope-string-syntax>)
makes the colon+positional optional, so `repo?collection=…` is
valid. Rework `parse_permission_token` to split on `:` or `?`.
Affected real clients: blento.app, greengale.app, api.semble.so,
stream.place.
2. Scope grammar rejected any resource name outside a fixed allow-list
of six. The spec explicitly notes the resource space is expected
to expand and documents `transition:*` scopes
(<https://atproto.com/specs/oauth#authorization-scopes>) — these
are valid atproto-profile scopes during the App-Password-to-OAuth
migration. Drop the `known_resources` whitelist; a syntactic
check should not reject unknown-but-well-formed resource names.
`ScopeParseReason::UnknownResource` is removed. Affected real
clients: aetheros, cocoon, chive, leaflet, nooki.
3. `application_type` flagged as required; the atproto profile
marks it optional with a default of `web`
(<https://atproto.com/specs/oauth#clients>). Emit `Skipped` when
absent and default the client-kind derivation to `web`.
Affected real client: grain.social.
New regression coverage: three fixtures under
`tests/fixtures/oauth_client/metadata/` (`transition_scopes`,
`resource_query_no_positional`, `application_type_omitted`) plus
matching integration tests; five parser unit tests in `metadata.rs`.
The existing `scope_grammar_invalid` fixture is repointed to a
genuinely malformed scope so the negative path still fires under the
new semantics.
All 16 real-world clients now pass static checks; 321 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>