A community based topic aggregation platform built on atproto
1package oauth
2
3import (
4 "crypto/rand"
5 "crypto/sha256"
6 "encoding/base64"
7 "fmt"
8)
9
10// PKCE (Proof Key for Code Exchange) - RFC 7636
11// Prevents authorization code interception attacks
12
13// PKCEChallenge contains the code verifier and challenge for PKCE
14type PKCEChallenge struct {
15 Verifier string // Random string (43-128 characters)
16 Challenge string // Base64URL(SHA256(verifier))
17 Method string // Always "S256" for atProto
18}
19
20// GeneratePKCEChallenge generates a new PKCE code verifier and challenge
21// Uses S256 method (SHA-256 hash) as required by atProto OAuth
22func GeneratePKCEChallenge() (*PKCEChallenge, error) {
23 // Generate 32 random bytes (will be 43 chars when base64url encoded)
24 verifierBytes := make([]byte, 32)
25 if _, err := rand.Read(verifierBytes); err != nil {
26 return nil, fmt.Errorf("failed to generate random bytes: %w", err)
27 }
28
29 // Base64URL encode (no padding)
30 verifier := base64.RawURLEncoding.EncodeToString(verifierBytes)
31
32 // Create SHA-256 hash of verifier
33 hash := sha256.Sum256([]byte(verifier))
34 challenge := base64.RawURLEncoding.EncodeToString(hash[:])
35
36 return &PKCEChallenge{
37 Verifier: verifier,
38 Challenge: challenge,
39 Method: "S256",
40 }, nil
41}
42
43// GenerateState generates a random state parameter for CSRF protection
44// State is used to prevent CSRF attacks in the OAuth flow
45func GenerateState() (string, error) {
46 // Generate 32 random bytes
47 stateBytes := make([]byte, 32)
48 if _, err := rand.Read(stateBytes); err != nil {
49 return "", fmt.Errorf("failed to generate random state: %w", err)
50 }
51
52 return base64.RawURLEncoding.EncodeToString(stateBytes), nil
53}