atproto pds in zig pds.zat.dev
pds atproto
26

Configure Feed

Select the types of activity you want to include in your feed.

Use typed JSON for passkey responses

zzstoatzz (May 27, 2026, 2:16 PM -0500) bc411ac9 0bfb5c01

+26 -25
+26 -25
src/internal/passkeys.zig
··· 293 293 } 294 294 295 295 fn credentialsJson(allocator: std.mem.Allocator, passkeys: []const store.Passkey) ![]const u8 { 296 - var out: std.ArrayList(u8) = .empty; 297 - try out.append(allocator, '['); 298 - for (passkeys, 0..) |passkey, i| { 299 - if (i != 0) try out.append(allocator, ','); 296 + const Descriptor = struct { 297 + type: []const u8 = "public-key", 298 + id: []const u8, 299 + }; 300 + 301 + var descriptors: std.ArrayList(Descriptor) = .empty; 302 + for (passkeys) |passkey| { 300 303 const id = try webauthn.base64url.encodeAlloc(allocator, passkey.credential_id); 301 - const item = try std.fmt.allocPrint(allocator, "{{\"type\":\"public-key\",\"id\":{f}}}", .{std.json.fmt(id, .{})}); 302 - try out.appendSlice(allocator, item); 304 + try descriptors.append(allocator, .{ .id = id }); 303 305 } 304 - try out.append(allocator, ']'); 305 - return out.toOwnedSlice(allocator); 306 + return std.json.Stringify.valueAlloc(allocator, descriptors.items, .{}); 306 307 } 307 308 308 309 fn passkeysMetadataJson(allocator: std.mem.Allocator, passkeys: []const store.Passkey) ![]const u8 { 309 - var out: std.ArrayList(u8) = .empty; 310 - try out.append(allocator, '['); 311 - for (passkeys, 0..) |passkey, i| { 312 - if (i != 0) try out.append(allocator, ','); 310 + const Metadata = struct { 311 + id: []const u8, 312 + credentialId: []const u8, 313 + friendlyName: []const u8, 314 + createdAt: i64, 315 + lastUsed: ?i64, 316 + }; 317 + 318 + var items: std.ArrayList(Metadata) = .empty; 319 + for (passkeys) |passkey| { 313 320 const credential_id = try webauthn.base64url.encodeAlloc(allocator, passkey.credential_id); 314 321 const friendly_name = passkey.friendly_name orelse ""; 315 - const last_used = if (passkey.last_used) |value| 316 - try std.fmt.allocPrint(allocator, "{d}", .{value}) 317 - else 318 - "null"; 319 - const item = try std.fmt.allocPrint(allocator, "{{\"id\":{f},\"credentialId\":{f},\"friendlyName\":{f},\"createdAt\":{d},\"lastUsed\":{s}}}", .{ 320 - std.json.fmt(passkey.id, .{}), 321 - std.json.fmt(credential_id, .{}), 322 - std.json.fmt(friendly_name, .{}), 323 - passkey.created_at, 324 - last_used, 322 + try items.append(allocator, .{ 323 + .id = passkey.id, 324 + .credentialId = credential_id, 325 + .friendlyName = friendly_name, 326 + .createdAt = passkey.created_at, 327 + .lastUsed = passkey.last_used, 325 328 }); 326 - try out.appendSlice(allocator, item); 327 329 } 328 - try out.append(allocator, ']'); 329 - return out.toOwnedSlice(allocator); 330 + return std.json.Stringify.valueAlloc(allocator, items.items, .{}); 330 331 } 331 332 332 333 fn newChallenge(allocator: std.mem.Allocator) ![]u8 {