Summary
The OAuth provider throws on the first scope token it can't parse, rejecting the
whole PAR/authorize request with 400 invalid_scope. Login never reaches the
consent screen. The reference @atproto/oauth-provider instead drops unparseable
tokens and proceeds, so apps that work on bsky.social fail on a Cirrus PDS.
Repro
Log in to https://postgame.at from a Cirrus PDS. Its client metadata requests
repo:com.crashthearcade.{game,list,follow,settings}?action=read. The PAR fails:
POST /oauth/par
→ 400 {"error":"invalid_scope",
"error_description":"Malformed scope: repo:com.crashthearcade.game?action=read"}
The identical request returns 201 on bsky.social (and on a Cirrus PDS once the
?action=read tokens are removed).
Root cause
parseScope() calls each *Permission.fromString() and throws ScopeParseError
when one returns null. ?action=read is invalid (REPO_ACTIONS = [create, update, delete]), so one junk token kills the entire login — even though the app doesn't
need it.
Expected
Match the reference provider, which filters instead of throwing (RFC 6749 §3.3:
the AS MAY partially ignore the scope):
// @atproto/oauth-provider request-manager.js
const scope = Array.from(scopes).filter(isAtprotoOauthScope).join(' ') || undefined
In Cirrus's PAR/authorize path, drop unrecognized scope tokens rather than
throwing (keep atproto-required and other hard checks). This restores interop
with clients that request invalid or newer-than-supported scopes.
Versions
@getcirrus/pds 0.16.0 · @getcirrus/oauth-provider 0.5.0 · bundles
@atproto/oauth-scopes 0.3.2
Summary
The OAuth provider throws on the first scope token it can't parse, rejecting the
whole PAR/authorize request with
400 invalid_scope. Login never reaches theconsent screen. The reference
@atproto/oauth-providerinstead drops unparseabletokens and proceeds, so apps that work on bsky.social fail on a Cirrus PDS.
Repro
Log in to https://postgame.at from a Cirrus PDS. Its client metadata requests
repo:com.crashthearcade.{game,list,follow,settings}?action=read. The PAR fails:The identical request returns
201on bsky.social (and on a Cirrus PDS once the?action=readtokens are removed).Root cause
parseScope()calls each*Permission.fromString()and throwsScopeParseErrorwhen one returns null.
?action=readis invalid (REPO_ACTIONS = [create, update, delete]), so one junk token kills the entire login — even though the app doesn'tneed it.
Expected
Match the reference provider, which filters instead of throwing (RFC 6749 §3.3:
the AS MAY partially ignore the scope):
In Cirrus's PAR/authorize path, drop unrecognized scope tokens rather than
throwing (keep
atproto-required and other hard checks). This restores interopwith clients that request invalid or newer-than-supported scopes.
Versions
@getcirrus/pds0.16.0 ·@getcirrus/oauth-provider0.5.0 · bundles@atproto/oauth-scopes0.3.2