Skip to content

Commit d1fadfa

Browse files
🐛 [RUM-10101] Persist session cookie to one year when opt-in anonymous user tracking (DataDog#3559)
* 🐛Fix session cookie when opt-in anonymous user tracking only extended to one year when session is expired * Update packages/core/src/domain/session/storeStrategies/sessionInCookie.spec.ts Co-authored-by: Benoît <benoit.zugmeyer@datadoghq.com> * Refactor persist session and expire session in cookie --------- Co-authored-by: Benoît <benoit.zugmeyer@datadoghq.com>
1 parent e2d6535 commit d1fadfa

2 files changed

Lines changed: 74 additions & 16 deletions

File tree

packages/core/src/domain/session/storeStrategies/sessionInCookie.spec.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { resetExperimentalFeatures } from '../../../tools/experimentalFeatures'
21
import { mockClock, getSessionState } from '../../../../test'
32
import { setCookie, deleteCookie, getCookie, getCurrentSite } from '../../../browser/cookie'
43
import type { SessionState } from '../sessionState'
54
import type { Configuration } from '../../configuration'
6-
import { SESSION_TIME_OUT_DELAY } from '../sessionConstants'
5+
import { SESSION_COOKIE_EXPIRATION_DELAY, SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from '../sessionConstants'
76
import { buildCookieOptions, selectCookieStrategy, initCookieStrategy } from './sessionInCookie'
87
import type { SessionStoreStrategy } from './sessionStoreStrategy'
98
import { SESSION_STORE_KEY } from './sessionStoreStrategy'
@@ -103,6 +102,56 @@ describe('session in cookie strategy', () => {
103102
})
104103
})
105104
})
105+
106+
describe('session in cookie strategy when opt-in anonymous user tracking', () => {
107+
const anonymousId = 'device-123'
108+
const sessionState: SessionState = { id: '123', created: '0' }
109+
let cookieStorageStrategy: SessionStoreStrategy
110+
beforeEach(() => {
111+
cookieStorageStrategy = initCookieStrategy(
112+
{ ...DEFAULT_INIT_CONFIGURATION, trackAnonymousUser: true } as Configuration,
113+
{}
114+
)
115+
})
116+
117+
afterEach(() => {
118+
deleteCookie(SESSION_STORE_KEY)
119+
})
120+
it('should persist with anonymous id', () => {
121+
cookieStorageStrategy.persistSession({ ...sessionState, anonymousId })
122+
const session = cookieStorageStrategy.retrieveSession()
123+
expect(session).toEqual({ ...sessionState, anonymousId })
124+
expect(getCookie(SESSION_STORE_KEY)).toBe('id=123&created=0&aid=device-123')
125+
})
126+
127+
it('should expire with anonymous id', () => {
128+
cookieStorageStrategy.expireSession({ ...sessionState, anonymousId })
129+
const session = cookieStorageStrategy.retrieveSession()
130+
expect(session).toEqual({ isExpired: '1', anonymousId })
131+
expect(getCookie(SESSION_STORE_KEY)).toBe('isExpired=1&aid=device-123')
132+
})
133+
134+
it('should persist for one year when opt-in', () => {
135+
const cookieSetSpy = spyOnProperty(document, 'cookie', 'set')
136+
const clock = mockClock()
137+
cookieStorageStrategy.persistSession({ ...sessionState, anonymousId })
138+
expect(cookieSetSpy.calls.argsFor(0)[0]).toContain(
139+
new Date(clock.timeStamp(SESSION_COOKIE_EXPIRATION_DELAY)).toUTCString()
140+
)
141+
clock.cleanup()
142+
})
143+
144+
it('should expire in one year when opt-in', () => {
145+
const cookieSetSpy = spyOnProperty(document, 'cookie', 'set')
146+
const clock = mockClock()
147+
cookieStorageStrategy.expireSession({ ...sessionState, anonymousId })
148+
expect(cookieSetSpy.calls.argsFor(0)[0]).toContain(
149+
new Date(clock.timeStamp(SESSION_COOKIE_EXPIRATION_DELAY)).toUTCString()
150+
)
151+
clock.cleanup()
152+
})
153+
})
154+
106155
describe('session in cookie strategy when opt-out anonymous user tracking', () => {
107156
const anonymousId = 'device-123'
108157
const sessionState: SessionState = { id: '123', created: '0' }
@@ -113,7 +162,6 @@ describe('session in cookie strategy when opt-out anonymous user tracking', () =
113162
})
114163

115164
afterEach(() => {
116-
resetExperimentalFeatures()
117165
deleteCookie(SESSION_STORE_KEY)
118166
})
119167

@@ -125,6 +173,12 @@ describe('session in cookie strategy when opt-out anonymous user tracking', () =
125173
clock.cleanup()
126174
})
127175

176+
it('should not persist with one year when opt-out', () => {
177+
const cookieSetSpy = spyOnProperty(document, 'cookie', 'set')
178+
cookieStorageStrategy.persistSession({ ...sessionState, anonymousId })
179+
expect(cookieSetSpy.calls.argsFor(0)[0]).toContain(new Date(Date.now() + SESSION_EXPIRATION_DELAY).toUTCString())
180+
})
181+
128182
it('should not persist or expire a session with anonymous id when opt-out', () => {
129183
cookieStorageStrategy.persistSession({ ...sessionState, anonymousId })
130184
cookieStorageStrategy.expireSession({ ...sessionState, anonymousId })

packages/core/src/domain/session/storeStrategies/sessionInCookie.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,33 @@ export function initCookieStrategy(configuration: Configuration, cookieOptions:
2626
* This issue concerns only chromium browsers and enabling this on firefox increases cookie write failures.
2727
*/
2828
isLockEnabled: isChromium(),
29-
persistSession: persistSessionCookie(cookieOptions),
29+
persistSession: (sessionState: SessionState) =>
30+
storeSessionCookie(cookieOptions, configuration, sessionState, SESSION_EXPIRATION_DELAY),
3031
retrieveSession: retrieveSessionCookie,
31-
expireSession: (sessionState: SessionState) => expireSessionCookie(cookieOptions, sessionState, configuration),
32+
expireSession: (sessionState: SessionState) =>
33+
storeSessionCookie(
34+
cookieOptions,
35+
configuration,
36+
getExpiredSessionState(sessionState, configuration),
37+
SESSION_TIME_OUT_DELAY
38+
),
3239
}
3340

3441
tryOldCookiesMigration(cookieStore)
3542

3643
return cookieStore
3744
}
3845

39-
function persistSessionCookie(options: CookieOptions) {
40-
return (session: SessionState) => {
41-
setCookie(SESSION_STORE_KEY, toSessionString(session), SESSION_EXPIRATION_DELAY, options)
42-
}
43-
}
44-
45-
function expireSessionCookie(options: CookieOptions, sessionState: SessionState, configuration: Configuration) {
46-
const expiredSessionState = getExpiredSessionState(sessionState, configuration)
47-
// we do not extend cookie expiration date
46+
function storeSessionCookie(
47+
options: CookieOptions,
48+
configuration: Configuration,
49+
sessionState: SessionState,
50+
defaultTimeout: number
51+
) {
4852
setCookie(
4953
SESSION_STORE_KEY,
50-
toSessionString(expiredSessionState),
51-
configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : SESSION_TIME_OUT_DELAY,
54+
toSessionString(sessionState),
55+
configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : defaultTimeout,
5256
options
5357
)
5458
}

0 commit comments

Comments
 (0)