fork of indigo with slightly nicer lexgen
at main 4.1 kB view raw
1package auth 2 3import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/bluesky-social/indigo/atproto/crypto" 10 "github.com/bluesky-social/indigo/atproto/identity" 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 13 "github.com/golang-jwt/jwt/v5" 14 "github.com/stretchr/testify/assert" 15) 16 17// Returns an early-2024 timestamp as a point in time for validating known JWTs (which contain expires-at) 18func testTime() time.Time { 19 return time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) 20} 21 22func validateMinimal(token string, iss, aud string, pub crypto.PublicKey) error { 23 24 p := jwt.NewParser( 25 jwt.WithValidMethods(supportedAlgs), 26 jwt.WithTimeFunc(testTime), 27 jwt.WithIssuer(iss), 28 jwt.WithAudience(aud), 29 ) 30 _, err := p.Parse(token, func(tok *jwt.Token) (any, error) { 31 return pub, nil 32 }) 33 if err != nil { 34 return fmt.Errorf("failed to parse auth header JWT: %w", err) 35 } 36 return nil 37} 38 39func TestSignatureMethods(t *testing.T) { 40 assert := assert.New(t) 41 42 jwtTestFixtures := []struct { 43 name string 44 pubkey string 45 iss string 46 aud string 47 jwt string 48 }{ 49 { 50 name: "secp256k1 (K-256)", 51 pubkey: "did:key:zQ3shscXNYZQZSPwegiv7uQZZV5kzATLBRtgJhs7uRY7pfSk4", 52 iss: "did:example:iss", 53 aud: "did:example:aud", 54 jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJleHAiOjE3MTM1NzEwMTJ9.J_In_PQCMjygeeoIKyjybORD89ZnEy1bZTd--sdq_78qv3KCO9181ZAh-2Pl0qlXZjfUlxgIa6wiak2NtsT98g", 55 }, 56 { 57 name: "secp256k1 (K-256)", 58 pubkey: "did:key:zQ3shqKrpHzQ5HDfhgcYMWaFcpBK3SS39wZLdTjA5GeakX8G5", 59 iss: "did:example:iss", 60 aud: "did:example:aud", 61 jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJleHAiOjE3MTM1NzExMzJ9.itNeYcF5oFMZIGxtnbJhE4McSniv_aR-Yk1Wj8uWk1K8YjlS2fzuJMo0-fILV3payETxn6r45f0FfpTaqY0EZQ", 62 }, 63 { 64 name: "P-256", 65 pubkey: "did:key:zDnaeXRDKRCEUoYxi8ZJS2pDsgfxUh3pZiu3SES9nbY4DoART", 66 iss: "did:example:iss", 67 aud: "did:example:aud", 68 jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJleHAiOjE3MTM1NzE1NTR9.FFRLm7SGbDUp6cL0WoCs0L5oqNkjCXB963TqbgI-KxIjbiqMQATVCalcMJx17JGTjMmfVHJP6Op_V4Z0TTjqog", 69 }, 70 } 71 72 for _, fix := range jwtTestFixtures { 73 74 pubk, err := crypto.ParsePublicDIDKey(fix.pubkey) 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 assert.NoError(validateMinimal(fix.jwt, fix.iss, fix.aud, pubk)) 80 } 81} 82 83func testSigningValidation(t *testing.T, priv crypto.PrivateKey) { 84 assert := assert.New(t) 85 ctx := context.Background() 86 87 iss := syntax.DID("did:example:iss") 88 aud := "did:example:aud#svc" 89 lxm := syntax.NSID("com.example.api") 90 91 priv, err := crypto.GeneratePrivateKeyP256() 92 if err != nil { 93 t.Fatal(err) 94 } 95 pub, err := priv.PublicKey() 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 dir := identity.NewMockDirectory() 101 dir.Insert(identity.Identity{ 102 DID: iss, 103 Keys: map[string]identity.VerificationMethod{ 104 "atproto": { 105 Type: "Multikey", 106 PublicKeyMultibase: pub.Multibase(), 107 }, 108 }, 109 }) 110 111 v := ServiceAuthValidator{ 112 Audience: aud, 113 Dir: &dir, 114 } 115 116 t1, err := SignServiceAuth(iss, aud, time.Minute, nil, priv) 117 if err != nil { 118 t.Fatal(err) 119 } 120 d1, err := v.Validate(ctx, t1, nil) 121 assert.NoError(err) 122 assert.Equal(d1, iss) 123 _, err = v.Validate(ctx, t1, &lxm) 124 assert.Error(err) 125 126 t2, err := SignServiceAuth(iss, aud, time.Minute, &lxm, priv) 127 if err != nil { 128 t.Fatal(err) 129 } 130 d2, err := v.Validate(ctx, t2, nil) 131 assert.NoError(err) 132 assert.Equal(d2, iss) 133 _, err = v.Validate(ctx, t2, &lxm) 134 assert.NoError(err) 135 136 _, err = v.Validate(ctx, t2, nil) 137 assert.NoError(err) 138 _, err = v.Validate(ctx, t2, &lxm) 139 assert.NoError(err) 140} 141 142func TestP256SigningValidation(t *testing.T) { 143 priv, err := crypto.GeneratePrivateKeyP256() 144 if err != nil { 145 t.Fatal(err) 146 } 147 testSigningValidation(t, priv) 148} 149 150func TestK256SigningValidation(t *testing.T) { 151 priv, err := crypto.GeneratePrivateKeyK256() 152 if err != nil { 153 t.Fatal(err) 154 } 155 testSigningValidation(t, priv) 156}