@@ -1574,6 +1574,130 @@ describe('Session', () => {
15741574 } ) ;
15751575 } ) ;
15761576
1577+ describe ( 'sends previous token in /tokens request body' , ( ) => {
1578+ let dispatchSpy : ReturnType < typeof vi . spyOn > ;
1579+ let fetchSpy : ReturnType < typeof vi . spyOn > ;
1580+
1581+ beforeEach ( ( ) => {
1582+ dispatchSpy = vi . spyOn ( eventBus , 'emit' ) ;
1583+ fetchSpy = vi . spyOn ( BaseResource , '_fetch' as any ) ;
1584+ BaseResource . clerk = clerkMock ( {
1585+ __internal_environment : {
1586+ authConfig : { sessionMinter : true } ,
1587+ } ,
1588+ } ) as any ;
1589+ } ) ;
1590+
1591+ afterEach ( ( ) => {
1592+ dispatchSpy ?. mockRestore ( ) ;
1593+ fetchSpy ?. mockRestore ( ) ;
1594+ BaseResource . clerk = null as any ;
1595+ } ) ;
1596+
1597+ it ( 'includes token in request body when lastActiveToken exists' , async ( ) => {
1598+ const session = new Session ( {
1599+ status : 'active' ,
1600+ id : 'session_1' ,
1601+ object : 'session' ,
1602+ user : createUser ( { } ) ,
1603+ last_active_organization_id : null ,
1604+ last_active_token : { object : 'token' , jwt : mockJwt } ,
1605+ actor : null ,
1606+ created_at : new Date ( ) . getTime ( ) ,
1607+ updated_at : new Date ( ) . getTime ( ) ,
1608+ } as SessionJSON ) ;
1609+
1610+ SessionTokenCache . clear ( ) ;
1611+
1612+ fetchSpy . mockResolvedValueOnce ( { object : 'token' , jwt : mockJwt } ) ;
1613+
1614+ await session . getToken ( ) ;
1615+
1616+ expect ( fetchSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1617+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] ) . toMatchObject ( {
1618+ path : '/client/sessions/session_1/tokens' ,
1619+ method : 'POST' ,
1620+ body : { organizationId : null , token : mockJwt } ,
1621+ } ) ;
1622+ } ) ;
1623+
1624+ it ( 'does not include token key in request body when lastActiveToken is null (first mint)' , async ( ) => {
1625+ const session = new Session ( {
1626+ status : 'active' ,
1627+ id : 'session_1' ,
1628+ object : 'session' ,
1629+ user : createUser ( { } ) ,
1630+ last_active_organization_id : null ,
1631+ actor : null ,
1632+ created_at : new Date ( ) . getTime ( ) ,
1633+ updated_at : new Date ( ) . getTime ( ) ,
1634+ } as unknown as SessionJSON ) ;
1635+
1636+ SessionTokenCache . clear ( ) ;
1637+
1638+ fetchSpy . mockResolvedValueOnce ( { object : 'token' , jwt : mockJwt } ) ;
1639+
1640+ await session . getToken ( ) ;
1641+
1642+ expect ( fetchSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1643+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] ) . toMatchObject ( {
1644+ path : '/client/sessions/session_1/tokens' ,
1645+ method : 'POST' ,
1646+ body : { organizationId : null } ,
1647+ } ) ;
1648+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] . body ) . not . toHaveProperty ( 'token' ) ;
1649+ } ) ;
1650+
1651+ it ( 'does not include token in request body for template token requests' , async ( ) => {
1652+ const session = new Session ( {
1653+ status : 'active' ,
1654+ id : 'session_1' ,
1655+ object : 'session' ,
1656+ user : createUser ( { } ) ,
1657+ last_active_organization_id : null ,
1658+ last_active_token : { object : 'token' , jwt : mockJwt } ,
1659+ actor : null ,
1660+ created_at : new Date ( ) . getTime ( ) ,
1661+ updated_at : new Date ( ) . getTime ( ) ,
1662+ } as SessionJSON ) ;
1663+
1664+ SessionTokenCache . clear ( ) ;
1665+
1666+ fetchSpy . mockResolvedValueOnce ( { object : 'token' , jwt : mockJwt } ) ;
1667+
1668+ await session . getToken ( { template : 'my-template' } ) ;
1669+
1670+ expect ( fetchSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1671+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] ) . toMatchObject ( {
1672+ path : '/client/sessions/session_1/tokens/my-template' ,
1673+ method : 'POST' ,
1674+ } ) ;
1675+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] . body ) . toEqual ( { } ) ;
1676+ } ) ;
1677+
1678+ it ( 'token value matches lastActiveToken.getRawString() exactly' , async ( ) => {
1679+ const session = new Session ( {
1680+ status : 'active' ,
1681+ id : 'session_1' ,
1682+ object : 'session' ,
1683+ user : createUser ( { } ) ,
1684+ last_active_organization_id : null ,
1685+ last_active_token : { object : 'token' , jwt : mockJwt } ,
1686+ actor : null ,
1687+ created_at : new Date ( ) . getTime ( ) ,
1688+ updated_at : new Date ( ) . getTime ( ) ,
1689+ } as SessionJSON ) ;
1690+
1691+ SessionTokenCache . clear ( ) ;
1692+
1693+ fetchSpy . mockResolvedValueOnce ( { object : 'token' , jwt : mockJwt } ) ;
1694+
1695+ await session . getToken ( ) ;
1696+
1697+ expect ( fetchSpy . mock . calls [ 0 ] [ 0 ] . body . token ) . toBe ( mockJwt ) ;
1698+ } ) ;
1699+ } ) ;
1700+
15771701 describe ( 'origin outage mode fallback' , ( ) => {
15781702 let dispatchSpy : ReturnType < typeof vi . spyOn > ;
15791703 let fetchSpy : ReturnType < typeof vi . spyOn > ;
0 commit comments