91 lines
3.7 KiB
JavaScript
91 lines
3.7 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import {
|
|
accessTokenIdentity,
|
|
ACCESS_TOKEN_KEY,
|
|
REFRESH_TOKEN_KEY,
|
|
REMEMBERED_LOGIN_ID_KEY,
|
|
clearSessionTokens,
|
|
getRememberedLoginId,
|
|
persistRememberedLoginId,
|
|
persistTokenPair,
|
|
} from './session.ts'
|
|
|
|
function storage() {
|
|
const values = new Map()
|
|
return {
|
|
getItem: (key) => values.get(key) ?? null,
|
|
setItem: (key, value) => values.set(key, String(value)),
|
|
removeItem: (key) => values.delete(key),
|
|
}
|
|
}
|
|
|
|
function installStorage() {
|
|
globalThis.localStorage = storage()
|
|
globalThis.sessionStorage = storage()
|
|
}
|
|
|
|
test('persists the normal rotating session independently of remembered account preference', () => {
|
|
installStorage()
|
|
persistTokenPair({
|
|
tokenType: 'Bearer',
|
|
accessToken: 'access-token',
|
|
expiresIn: 1440,
|
|
refreshToken: 'refresh-token',
|
|
refreshExpiresIn: 604800,
|
|
})
|
|
|
|
assert.equal(localStorage.getItem(ACCESS_TOKEN_KEY), 'access-token')
|
|
assert.equal(localStorage.getItem(REFRESH_TOKEN_KEY), 'refresh-token')
|
|
assert.equal(sessionStorage.getItem(ACCESS_TOKEN_KEY), null)
|
|
assert.equal(sessionStorage.getItem(REFRESH_TOKEN_KEY), null)
|
|
})
|
|
|
|
test('remembers only the login id and keeps it when session tokens are cleared', () => {
|
|
installStorage()
|
|
persistRememberedLoginId('merchant-user', true)
|
|
localStorage.setItem(ACCESS_TOKEN_KEY, 'access-token')
|
|
localStorage.setItem(REFRESH_TOKEN_KEY, 'refresh-token')
|
|
|
|
clearSessionTokens()
|
|
|
|
assert.equal(getRememberedLoginId(), 'merchant-user')
|
|
assert.equal(localStorage.getItem(REMEMBERED_LOGIN_ID_KEY), 'merchant-user')
|
|
assert.equal(localStorage.getItem(ACCESS_TOKEN_KEY), null)
|
|
assert.equal(localStorage.getItem(REFRESH_TOKEN_KEY), null)
|
|
|
|
persistRememberedLoginId('merchant-user', false)
|
|
assert.equal(getRememberedLoginId(), '')
|
|
})
|
|
|
|
test('normal token rotation never removes either shared token between writes', () => {
|
|
installStorage()
|
|
localStorage.setItem(ACCESS_TOKEN_KEY, 'old-access')
|
|
localStorage.setItem(REFRESH_TOKEN_KEY, 'old-refresh')
|
|
const removed = []
|
|
const remove = localStorage.removeItem
|
|
localStorage.removeItem = key => { removed.push(key); remove(key) }
|
|
persistTokenPair({ accessToken: 'new-access', refreshToken: 'new-refresh' })
|
|
assert.ok(!removed.includes(ACCESS_TOKEN_KEY))
|
|
assert.ok(!removed.includes(REFRESH_TOKEN_KEY))
|
|
})
|
|
|
|
function rawIdentity(overrides = '') {
|
|
return `test.${Buffer.from(`{"user_id":2107003446494334978,"tenant_id":2107003446494334978,"membership_id":2107003446494334978,"app_code":"MERCHANT"${overrides}}`).toString('base64url')}.test`
|
|
}
|
|
test('identity preserves low digits of numeric 19 digit user customer and membership',()=>{
|
|
const original=rawIdentity();const json=Buffer.from(original.split('.')[1],'base64url').toString();
|
|
for(const field of ['user_id','tenant_id','membership_id']){
|
|
const changed=json.replace(`"${field}":2107003446494334978`,`"${field}":2107003446494334979`)
|
|
assert.notEqual(accessTokenIdentity(original),accessTokenIdentity(`test.${Buffer.from(changed).toString('base64url')}.test`))
|
|
}
|
|
})
|
|
test('identity string and numeric integers are equivalent without changing other strings',()=>{
|
|
const a=rawIdentity();const raw=Buffer.from(a.split('.')[1],'base64url').toString();
|
|
const b=`test.${Buffer.from(raw.replaceAll(':2107003446494334978',':"2107003446494334978"')).toString('base64url')}.test`
|
|
assert.equal(accessTokenIdentity(a),accessTokenIdentity(b))
|
|
const decorated=rawIdentity(',"username":'+JSON.stringify('number 2107003446494334979 and "tenant_id":2107003446494334979')+',"note":"1234567890123456789"')
|
|
assert.equal(accessTokenIdentity(a),accessTokenIdentity(decorated))
|
|
assert.ok(accessTokenIdentity(a).includes('2107003446494334978'))
|
|
})
|