@@ -10,6 +10,7 @@ import {
1010 createTestApp ,
1111 createTestRequest ,
1212 generateTestToken ,
13+ TEST_RS256_PUBLIC_KEY_PEM ,
1314} from "./test-helpers.js" ;
1415
1516const OIDC_KEY_PAIR = crypto . generateKeyPairSync ( "rsa" , { modulusLength : 2048 } ) ;
@@ -182,6 +183,47 @@ test("OIDC-bound org accepts verified sponsor proof and records binding evidence
182183 const stored = await assertJsonResponse < CreatedIdentity > ( storedResponse , 200 ) ;
183184 assert . deepEqual ( stored . sponsorBinding , identity . sponsorBinding ) ;
184185
186+ const ledger = await app . storage . DB . prepare ( `
187+ SELECT entry_type, agent_id, sponsor_id, jti, payload_json, jws
188+ FROM attestation_ledger
189+ WHERE org_id = ? AND agent_id = ?
190+ ` ) . bind ( org , identity . id ) . first < {
191+ entry_type : string ;
192+ agent_id : string ;
193+ sponsor_id : string ;
194+ jti : string | null ;
195+ payload_json : string ;
196+ jws : string ;
197+ } > ( ) ;
198+ assert . ok ( ledger ) ;
199+ assert . equal ( ledger . entry_type , "identity.created" ) ;
200+ assert . equal ( ledger . agent_id , identity . id ) ;
201+ assert . equal ( ledger . sponsor_id , "user_alice" ) ;
202+ assert . equal ( ledger . jti , "idp-session-1" ) ;
203+ const [ encodedHeader , encodedPayload , encodedSignature ] = ledger . jws . split ( "." ) ;
204+ assert . ok ( encodedHeader && encodedPayload && encodedSignature ) ;
205+ const signedPayloadJson = Buffer . from ( encodedPayload , "base64url" ) . toString ( "utf8" ) ;
206+ assert . equal ( signedPayloadJson , ledger . payload_json ) ;
207+ assert . deepEqual ( JSON . parse ( ledger . payload_json ) , {
208+ agentId : identity . id ,
209+ sponsorId : "user_alice" ,
210+ issuer,
211+ subject : "alice" ,
212+ iat : now ,
213+ jti : "idp-session-1" ,
214+ sponsorBinding : identity . sponsorBinding ,
215+ ts : identity . createdAt ,
216+ } ) ;
217+ assert . equal (
218+ crypto . verify (
219+ "RSA-SHA256" ,
220+ Buffer . from ( `${ encodedHeader } .${ encodedPayload } ` ) ,
221+ TEST_RS256_PUBLIC_KEY_PEM ,
222+ Buffer . from ( encodedSignature , "base64url" ) ,
223+ ) ,
224+ true ,
225+ ) ;
226+
185227 const patchResponse = await app . request (
186228 createTestRequest (
187229 "PATCH" ,
@@ -338,3 +380,69 @@ test("malformed sponsor federation configuration fails closed", async () => {
338380 const body = await assertJsonResponse < { code : string } > ( response , 503 ) ;
339381 assert . equal ( body . code , "sponsor_binding_misconfigured" ) ;
340382} ) ;
383+
384+ test ( "OIDC-bound identity creation rolls back when the signed ledger append fails" , async ( t ) => {
385+ const { issuer } = await startOidcFixture ( t ) ;
386+ const org = "org_oidc_atomic_ledger" ;
387+ const app = createTestApp ( {
388+ RELAYAUTH_SPONSOR_FEDERATIONS : JSON . stringify ( {
389+ [ org ] : { sponsorBinding : "oidc" , issuer, clientId : "chief-fixture" } ,
390+ } ) ,
391+ } ) ;
392+ const apiKey = await createWorkspaceApiKey ( app , org ) ;
393+ const now = Math . floor ( Date . now ( ) / 1000 ) ;
394+ const proofResponse = await app . request (
395+ createTestRequest (
396+ "POST" ,
397+ "/v1/sponsors/proof" ,
398+ {
399+ idToken : signIdToken ( {
400+ iss : issuer ,
401+ sub : "alice" ,
402+ aud : "chief-fixture" ,
403+ iat : now ,
404+ exp : now + 300 ,
405+ } ) ,
406+ } ,
407+ { "x-api-key" : apiKey } ,
408+ ) ,
409+ undefined ,
410+ app . bindings ,
411+ ) ;
412+ const proof = await assertJsonResponse < SponsorProof > ( proofResponse , 201 ) ;
413+
414+ await app . storage . DB . prepare ( `
415+ CREATE TRIGGER reject_identity_created_ledger
416+ BEFORE INSERT ON attestation_ledger
417+ WHEN NEW.entry_type = 'identity.created'
418+ BEGIN
419+ SELECT RAISE(ABORT, 'fixture ledger failure');
420+ END
421+ ` ) . run ( ) ;
422+
423+ const response = await app . request (
424+ createTestRequest (
425+ "POST" ,
426+ "/v1/identities" ,
427+ {
428+ name : "must-roll-back" ,
429+ sponsorId : proof . sponsorId ,
430+ sponsorProof : proof . sponsorProof ,
431+ } ,
432+ { "x-api-key" : apiKey } ,
433+ ) ,
434+ undefined ,
435+ app . bindings ,
436+ ) ;
437+ const body = await assertJsonResponse < { code : string } > ( response , 500 ) ;
438+ assert . equal ( body . code , "identity_create_failed" ) ;
439+
440+ const identityRow = await app . storage . DB . prepare (
441+ "SELECT id FROM identities WHERE org_id = ? AND name = ?" ,
442+ ) . bind ( org , "must-roll-back" ) . first < { id : string } > ( ) ;
443+ const ledgerRow = await app . storage . DB . prepare (
444+ "SELECT seq FROM attestation_ledger WHERE org_id = ?" ,
445+ ) . bind ( org ) . first < { seq : number } > ( ) ;
446+ assert . equal ( identityRow ?? null , null ) ;
447+ assert . equal ( ledgerRow ?? null , null ) ;
448+ } ) ;
0 commit comments