Skip to content

OAuth: a single unrecognized scope token rejects the entire authorization request, breaking login #198

@simnaut

Description

@simnaut

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions