A community based topic aggregation platform built on atproto
1# atProto OAuth Authentication
2
3This package implements third-party OAuth authentication for Coves, validating JWT Bearer tokens from mobile apps and other atProto clients.
4
5## Architecture
6
7This is **third-party authentication** (validating incoming requests), not first-party authentication (logging users into Coves web frontend).
8
9### Components
10
111. **JWT Parser** (`jwt.go`) - Parses and validates JWT tokens
122. **JWKS Fetcher** (`jwks_fetcher.go`) - Fetches and caches public keys from PDS authorization servers
133. **Auth Middleware** (`internal/api/middleware/auth.go`) - HTTP middleware that protects endpoints
14
15### Flow
16
17```
18Client Request
19 ↓
20Authorization: Bearer <jwt>
21 ↓
22Auth Middleware
23 ↓
24Extract JWT → Parse Claims → Verify Signature (via JWKS)
25 ↓
26Inject DID into Context → Call Handler
27```
28
29## Usage
30
31### Phase 1: Parse-Only Mode (Testing)
32
33Set `AUTH_SKIP_VERIFY=true` to only parse JWTs without signature verification:
34
35```bash
36export AUTH_SKIP_VERIFY=true
37```
38
39This is useful for:
40- Initial integration testing
41- Testing with mock tokens
42- Debugging JWT structure
43
44### Phase 2: Full Verification (Production)
45
46Set `AUTH_SKIP_VERIFY=false` (or unset) to enable full JWT signature verification:
47
48```bash
49export AUTH_SKIP_VERIFY=false
50# or just unset it
51```
52
53This is **required for production** and validates:
54- JWT signature using PDS public key
55- Token expiration
56- Required claims (sub, iss)
57- DID format
58
59## Protected Endpoints
60
61The following endpoints require authentication:
62
63- `POST /xrpc/social.coves.community.create`
64- `POST /xrpc/social.coves.community.update`
65- `POST /xrpc/social.coves.community.subscribe`
66- `POST /xrpc/social.coves.community.unsubscribe`
67
68### Making Authenticated Requests
69
70Include the JWT in the `Authorization` header:
71
72```bash
73curl -X POST https://coves.social/xrpc/social.coves.community.create \
74 -H "Authorization: Bearer eyJhbGc..." \
75 -H "Content-Type: application/json" \
76 -d '{"name":"Gaming","hostedByDid":"did:plc:..."}'
77```
78
79### Getting User DID in Handlers
80
81The middleware injects the authenticated user's DID into the request context:
82
83```go
84import "Coves/internal/api/middleware"
85
86func (h *Handler) HandleCreate(w http.ResponseWriter, r *http.Request) {
87 // Extract authenticated user DID
88 userDID := middleware.GetUserDID(r)
89 if userDID == "" {
90 // Not authenticated (should never happen with RequireAuth middleware)
91 http.Error(w, "Unauthorized", http.StatusUnauthorized)
92 return
93 }
94
95 // Use userDID for authorization checks
96 // ...
97}
98```
99
100## Key Caching
101
102Public keys are fetched from PDS authorization servers and cached for 1 hour. The cache is automatically cleaned up hourly to remove expired entries.
103
104### JWKS Discovery Flow
105
1061. Extract `iss` claim from JWT (e.g., `https://pds.example.com`)
1072. Fetch `https://pds.example.com/.well-known/oauth-authorization-server`
1083. Extract `jwks_uri` from metadata
1094. Fetch JWKS from `jwks_uri`
1105. Find matching key by `kid` from JWT header
1116. Cache the JWKS for 1 hour
112
113## Security Considerations
114
115### ✅ Implemented
116
117- JWT signature verification with PDS public keys
118- Token expiration validation
119- DID format validation
120- Required claims validation (sub, iss)
121- Key caching with TTL
122- Secure error messages (no internal details leaked)
123
124### ⚠️ Not Yet Implemented
125
126- DPoP validation (for replay attack prevention)
127- Scope validation (checking `scope` claim)
128- Audience validation (checking `aud` claim)
129- Rate limiting per DID
130- Token revocation checking
131
132## Testing
133
134Run the test suite:
135
136```bash
137go test ./internal/atproto/auth/... -v
138```
139
140### Manual Testing
141
1421. **Phase 1 (Parse Only)**:
143 ```bash
144 # Create a test JWT (use jwt.io or a tool)
145 export AUTH_SKIP_VERIFY=true
146 curl -X POST http://localhost:8081/xrpc/social.coves.community.create \
147 -H "Authorization: Bearer <test-jwt>" \
148 -d '{"name":"Test","hostedByDid":"did:plc:test"}'
149 ```
150
1512. **Phase 2 (Full Verification)**:
152 ```bash
153 # Use a real JWT from a PDS
154 export AUTH_SKIP_VERIFY=false
155 curl -X POST http://localhost:8081/xrpc/social.coves.community.create \
156 -H "Authorization: Bearer <real-jwt>" \
157 -d '{"name":"Test","hostedByDid":"did:plc:test"}'
158 ```
159
160## Error Responses
161
162### 401 Unauthorized
163
164Missing or invalid token:
165
166```json
167{
168 "error": "AuthenticationRequired",
169 "message": "Missing Authorization header"
170}
171```
172
173```json
174{
175 "error": "AuthenticationRequired",
176 "message": "Invalid or expired token"
177}
178```
179
180### Common Issues
181
1821. **Missing Authorization header** → Add `Authorization: Bearer <token>`
1832. **Token expired** → Get a new token from PDS
1843. **Invalid signature** → Ensure token is from a valid PDS
1854. **JWKS fetch fails** → Check PDS availability and network connectivity
186
187## Future Enhancements
188
189- [ ] DPoP proof validation
190- [ ] Scope-based authorization
191- [ ] Audience claim validation
192- [ ] Token revocation support
193- [ ] Rate limiting per DID
194- [ ] Metrics and monitoring