atproto tools in zig zat.dev
sdk atproto zig

test: add JWT signature verification tests

uses official test vectors from bluesky-social/indigo:
- ES256K (secp256k1) signature verification
- ES256 (P-256) signature verification
- reject wrong key test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+58
+58
src/internal/jwt.zig
··· 272 272 try std.testing.expectError(error.InvalidJwt, Jwt.parse(std.testing.allocator, "two.parts")); 273 273 try std.testing.expectError(error.InvalidJwt, Jwt.parse(std.testing.allocator, "too.many.parts.here")); 274 274 } 275 + 276 + test "verify ES256K signature - official fixture" { 277 + // test vector from bluesky-social/indigo atproto/auth/jwt_test.go 278 + // pubkey: did:key:zQ3shscXNYZQZSPwegiv7uQZZV5kzATLBRtgJhs7uRY7pfSk4 279 + // iss: did:example:iss, aud: did:example:aud, exp: 1713571012 280 + const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJleHAiOjE3MTM1NzEwMTJ9.J_In_PQCMjygeeoIKyjybORD89ZnEy1bZTd--sdq_78qv3KCO9181ZAh-2Pl0qlXZjfUlxgIa6wiak2NtsT98g"; 281 + 282 + // extract multibase key from did:key (strip "did:key:" prefix) 283 + const did_key = "did:key:zQ3shscXNYZQZSPwegiv7uQZZV5kzATLBRtgJhs7uRY7pfSk4"; 284 + const multibase_key = did_key["did:key:".len..]; 285 + 286 + var jwt = try Jwt.parse(std.testing.allocator, token); 287 + defer jwt.deinit(); 288 + 289 + // verify claims 290 + try std.testing.expectEqual(Algorithm.ES256K, jwt.header.alg); 291 + try std.testing.expectEqualStrings("did:example:iss", jwt.payload.iss); 292 + try std.testing.expectEqualStrings("did:example:aud", jwt.payload.aud); 293 + 294 + // verify signature 295 + try jwt.verify(multibase_key); 296 + } 297 + 298 + test "verify ES256 signature - official fixture" { 299 + // test vector from bluesky-social/indigo atproto/auth/jwt_test.go 300 + // pubkey: did:key:zDnaeXRDKRCEUoYxi8ZJS2pDsgfxUh3pZiu3SES9nbY4DoART 301 + // iss: did:example:iss, aud: did:example:aud, exp: 1713571554 302 + const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJleHAiOjE3MTM1NzE1NTR9.FFRLm7SGbDUp6cL0WoCs0L5oqNkjCXB963TqbgI-KxIjbiqMQATVCalcMJx17JGTjMmfVHJP6Op_V4Z0TTjqog"; 303 + 304 + // extract multibase key from did:key 305 + const did_key = "did:key:zDnaeXRDKRCEUoYxi8ZJS2pDsgfxUh3pZiu3SES9nbY4DoART"; 306 + const multibase_key = did_key["did:key:".len..]; 307 + 308 + var jwt = try Jwt.parse(std.testing.allocator, token); 309 + defer jwt.deinit(); 310 + 311 + // verify claims 312 + try std.testing.expectEqual(Algorithm.ES256, jwt.header.alg); 313 + try std.testing.expectEqualStrings("did:example:iss", jwt.payload.iss); 314 + try std.testing.expectEqualStrings("did:example:aud", jwt.payload.aud); 315 + 316 + // verify signature 317 + try jwt.verify(multibase_key); 318 + } 319 + 320 + test "reject signature with wrong key" { 321 + // ES256K token 322 + const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6ZXhhbXBsZTppc3MiLCJhdWQiOiJkaWQ6ZXhhbXBsZTphdWQiLCJleHAiOjE3MTM1NzEwMTJ9.J_In_PQCMjygeeoIKyjybORD89ZnEy1bZTd--sdq_78qv3KCO9181ZAh-2Pl0qlXZjfUlxgIa6wiak2NtsT98g"; 323 + 324 + // different ES256K key (second fixture from indigo) 325 + const wrong_key = "zQ3shqKrpHzQ5HDfhgcYMWaFcpBK3SS39wZLdTjA5GeakX8G5"; 326 + 327 + var jwt = try Jwt.parse(std.testing.allocator, token); 328 + defer jwt.deinit(); 329 + 330 + // should fail verification with wrong key 331 + try std.testing.expectError(error.SignatureVerificationFailed, jwt.verify(wrong_key)); 332 + }