@@ -543,6 +543,72 @@ async function run() {
543543 }
544544 )
545545
546+ await check (
547+ 'An address change ends sessions and OAuth grants issued under the old one' ,
548+ async ( ) => {
549+ const carolEmail = `carol@${ primary . domain } `
550+ const created = await scim ( token , '/Users' , {
551+ method : 'POST' ,
552+ expected : 201 ,
553+ body : {
554+ schemas : [ USER_SCHEMA ] ,
555+ userName : carolEmail ,
556+ active : true ,
557+ name : { givenName : 'Carol' , familyName : 'Example' } ,
558+ emails : [ { value : carolEmail , type : 'work' , primary : true } ] ,
559+ } ,
560+ } )
561+ const carolId = string ( created . body . id )
562+ const userId = string (
563+ ( await sql `select user_id from scim_user where id = ${ carolId } ` ) [ 0 ] ?. user_id
564+ )
565+ const sessionId = generateId ( )
566+ const clientId = generateId ( )
567+ await sql `insert into session (id, token, user_id, expires_at, created_at, updated_at)
568+ values (${ sessionId } , ${ generateShortId ( ) } , ${ userId } , now() + interval '1 day', now(), now())`
569+ await sql `insert into oauth_client (id, client_id, name, public, token_endpoint_auth_method, grant_types, redirect_uris)
570+ values (${ clientId } , ${ clientId } , 'SCIM E2E OAuth client', true, 'none',
571+ ${ [ 'authorization_code' , 'refresh_token' ] } , ${ [ 'http://127.0.0.1/callback' ] } )`
572+ try {
573+ await sql `insert into oauth_consent (id, client_id, user_id, scopes, created_at, updated_at)
574+ values (${ generateId ( ) } , ${ clientId } , ${ userId } , ${ [ 'api:read' ] } , now(), now())`
575+ await sql `insert into oauth_token_family (id, client_id, user_id, session_id, created_at, expires_at)
576+ values (${ generateId ( ) } , ${ clientId } , ${ userId } , ${ sessionId } , now(), now() + interval '1 day')`
577+ await sql `insert into oauth_access_token (id, token, client_id, user_id, scopes, created_at, expires_at)
578+ values (${ generateId ( ) } , ${ generateShortId ( ) } , ${ clientId } , ${ userId } , ${ [ 'api:read' ] } ,
579+ now(), now() + interval '1 hour')`
580+
581+ await patch ( token , `/Users/${ carolId } ` , [
582+ { op : 'replace' , path : 'name.givenName' , value : 'Caroline' } ,
583+ ] )
584+ assert . equal (
585+ ( await sql `select id from oauth_access_token where user_id = ${ userId } ` ) . length ,
586+ 1
587+ )
588+
589+ await patch ( token , `/Users/${ carolId } ` , [
590+ {
591+ op : 'replace' ,
592+ path : 'emails[type eq "work"].value' ,
593+ value : `carol-renamed@${ primary . domain } ` ,
594+ } ,
595+ ] )
596+
597+ assert . equal ( ( await sql `select id from session where user_id = ${ userId } ` ) . length , 0 )
598+ assert . equal (
599+ ( await sql `select id from oauth_token_family where user_id = ${ userId } ` ) . length ,
600+ 0
601+ )
602+ assert . equal (
603+ ( await sql `select id from oauth_access_token where user_id = ${ userId } ` ) . length ,
604+ 0
605+ )
606+ } finally {
607+ await sql `delete from oauth_client where client_id = ${ clientId } `
608+ }
609+ }
610+ )
611+
546612 await check (
547613 'Group CRUD supports Entra member PATCH, duplicates, foreign IDs, and membership-only PUT versions' ,
548614 async ( ) => {
0 commit comments