this repo has no description
1# Scope Validation Comparison: pds.js vs atproto PDS
2
3Comparison of OAuth scope validation between this implementation and the official AT Protocol PDS.
4
5---
6
7## Scope Types Supported
8
9| Scope Type | Format | pds.js | atproto PDS |
10|------------|--------|--------|-------------|
11| `atproto` | Static | Full access | Required for all OAuth |
12| `transition:generic` | Static | Full access | Full repo/blob bypass |
13| `transition:email` | Static | N/A | Read account email |
14| `transition:chat.bsky` | Static | N/A | Chat RPC access |
15| `repo:<collection>?action=<action>` | Granular | Full parsing + enforcement | Full parsing + enforcement |
16| `blob:<mime>` | Granular | Full parsing + enforcement | Full parsing + enforcement |
17| `rpc:<aud>:<lxm>` | Granular | Not implemented | Full parsing + enforcement |
18
19---
20
21## Scope Enforcement by Endpoint
22
23### com.atproto.repo.createRecord
24
25| Aspect | pds.js | atproto PDS |
26|--------|--------|-------------|
27| Scope check | `ScopePermissions.allowsRepo(collection, 'create')` | `permissions.assertRepo({ action: 'create', collection })` |
28| Required scope | `repo:<collection>?action=create` or `transition:generic` or `atproto` | `repo:<collection>?action=create` or `transition:generic` or `atproto` |
29| OAuth-only check | Yes (legacy tokens without scope bypass) | Yes (legacy Bearer bypasses) |
30| Error response | 403 "Missing required scope \"repo:...?action=...\"" | 403 "Missing required scope \"repo:...?action=...\"" |
31
32### com.atproto.repo.putRecord
33
34| Aspect | pds.js | atproto PDS |
35|--------|--------|-------------|
36| Scope check | `allowsRepo(collection, 'create')` AND `allowsRepo(collection, 'update')` | `assertRepo({ action: 'create' })` AND `assertRepo({ action: 'update' })` |
37| Required scope | `repo:<collection>?action=create&action=update` | `repo:<collection>?action=create&action=update` |
38| Notes | Requires both since putRecord can create or update | Requires both since putRecord can create or update |
39
40### com.atproto.repo.deleteRecord
41
42| Aspect | pds.js | atproto PDS |
43|--------|--------|-------------|
44| Scope check | `ScopePermissions.allowsRepo(collection, 'delete')` | `permissions.assertRepo({ action: 'delete', collection })` |
45| Required scope | `repo:<collection>?action=delete` | `repo:<collection>?action=delete` |
46
47### com.atproto.repo.applyWrites
48
49| Aspect | pds.js | atproto PDS |
50|--------|--------|-------------|
51| Scope check | Iterates all writes, checks each unique action/collection pair | Iterates all writes, asserts each unique action/collection pair |
52| Required scope | All `repo:<collection>?action=<action>` for each write | All `repo:<collection>?action=<action>` for each write |
53| Per-write validation | Yes | Yes |
54
55### com.atproto.repo.uploadBlob
56
57| Aspect | pds.js | atproto PDS |
58|--------|--------|-------------|
59| Scope check | `ScopePermissions.allowsBlob(contentType)` | `permissions.assertBlob({ mime: encoding })` |
60| Required scope | `blob:<mime-type>` (e.g., `blob:image/*`) | `blob:<mime-type>` (e.g., `blob:image/*`) |
61| MIME type awareness | Yes (validates against Content-Type) | Yes (validates against Content-Type) |
62
63### app.bsky.actor.getPreferences
64
65| Aspect | pds.js | atproto PDS |
66|--------|--------|-------------|
67| Scope check | Requires auth only | `permissions.assertRpc({ aud, lxm })` |
68| Required scope | Any valid auth | `rpc:app.bsky.actor.getPreferences` |
69
70### app.bsky.actor.putPreferences
71
72| Aspect | pds.js | atproto PDS |
73|--------|--------|-------------|
74| Scope check | Requires auth only | `permissions.assertRpc({ aud, lxm })` |
75| Required scope | Any valid auth | `rpc:app.bsky.actor.putPreferences` |
76
77---
78
79## Scope Parsing
80
81| Feature | pds.js | atproto PDS |
82|---------|--------|-------------|
83| Scope string splitting | `scope.split(' ')` | `ScopesSet` class |
84| Repo scope parsing | `parseRepoScope()` | `RepoPermission.fromString()` |
85| Repo scope format | `repo:collection?action=create&action=update` | `repo:collection?action=create&action=update` |
86| Blob scope parsing | `parseBlobScope()` | `BlobPermission.fromString()` |
87| RPC scope parsing | None | `RpcPermission.fromString()` |
88| Scope validation | Returns null for invalid | Validates syntax, ignores invalid |
89| Action deduplication | Yes (via Set) | Yes |
90| Default actions | All (create, update, delete) when no `?action=` | All (create, update, delete) when no `?action=` |
91
92---
93
94## Permission Checking
95
96| Feature | pds.js | atproto PDS |
97|---------|--------|-------------|
98| Permission class | `ScopePermissions` | `ScopePermissions` / `ScopePermissionsTransition` |
99| `allowsRepo(collection, action)` | Yes | Yes |
100| `allowsBlob(mime)` | Yes (with MIME wildcard matching) | Yes (with MIME wildcard matching) |
101| `allowsRpc(aud, lxm)` | N/A | Yes |
102| Transition scope handling | `transition:generic` bypasses repo/blob checks | `transition:generic` bypasses repo/blob checks |
103| Error messages | Specific missing scope in error | Specific missing scope in error |
104
105---
106
107## OAuth Flow
108
109| Feature | pds.js | atproto PDS |
110|---------|--------|-------------|
111| `scopes_supported` in metadata | `['atproto']` | `['atproto']` (but accepts granular) |
112| Scope validation at PAR | None | Validates syntax |
113| Scope stored in token | Yes | Yes |
114| Scope returned in token response | Yes | Yes |
115| `atproto` scope required | Checked at endpoints | Required at token verification |
116
117---
118
119## Transition Scope Behavior
120
121| Scope | pds.js | atproto PDS |
122|-------|--------|-------------|
123| `transition:generic` | Bypasses all repo/blob permission checks | Bypasses ALL repo/blob permission checks |
124| `transition:chat.bsky` | Not implemented | Allows `chat.bsky.*` RPC methods |
125| `transition:email` | Not implemented | Allows `account:email:read` |
126
127---
128
129## Summary
130
131| Category | pds.js | atproto PDS |
132|----------|--------|-------------|
133| Scope parsing | Full parser for repo/blob | Full parser per scope type |
134| Enforcement granularity | Per-collection, per-action | Per-collection, per-action |
135| Transition scope support | `transition:generic` only | Full |
136| MIME-aware blob scopes | Yes | Yes |
137| RPC scopes | No | Yes |
138| Error specificity | Names missing scope | Names missing scope |
139
140---
141
142## Remaining Gaps
143
1441. **RPC scopes** — `rpc:<aud>:<lxm>` parsing and enforcement not implemented
1452. **Additional transition scopes** — `transition:chat.bsky` and `transition:email` not implemented
1463. **Scope validation at PAR** — Could validate scope syntax during authorization request