Skip to content

Commit 90a44e3

Browse files
committed
fixup! Add app/device attestation for gated info-server requests
1 parent d8deeed commit 90a44e3

1 file changed

Lines changed: 14 additions & 7 deletions

File tree

src/util/attestation.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,13 @@ const parseTokenResponse = (json: unknown): CachedToken => {
122122
/**
123123
* Run one attestation handshake and return the fresh token, or `undefined` when
124124
* there is nothing to do (no native module / unsupported platform). Never
125-
* caches directly; the caller commits the result.
125+
* caches directly; the caller commits the result. `generation` identifies this
126+
* attempt so a stale (watchdog-released) handshake never mutates shared token
127+
* state that a newer handshake already owns.
126128
*/
127-
const performHandshake = async (): Promise<CachedToken | undefined> => {
129+
const performHandshake = async (
130+
generation: number
131+
): Promise<CachedToken | undefined> => {
128132
// No native module (e.g. unsupported platform / dev environment).
129133
if (EdgeAttestation == null) return undefined
130134

@@ -162,8 +166,10 @@ const performHandshake = async (): Promise<CachedToken | undefined> => {
162166
// Server rejected the assertion: the enrolled key is no longer trusted,
163167
// so any previously-minted token is suspect too. Drop it now so gated
164168
// callers do not keep sending a token the server already rejects while
165-
// re-enrollment is in progress; discard the key and re-attest.
166-
cachedToken = undefined
169+
// re-enrollment is in progress; discard the key and re-attest. Only when
170+
// this is still the current handshake, so a stale (watchdog-released)
171+
// attempt cannot wipe a token a newer handshake already cached.
172+
if (generation === handshakeGeneration) cachedToken = undefined
167173
console.warn(
168174
`[attestation] assertion rejected (${assertResponse.status}); re-attesting`
169175
)
@@ -196,8 +202,9 @@ const performHandshake = async (): Promise<CachedToken | undefined> => {
196202
return parseTokenResponse(await assertResponse.json())
197203
}
198204
// Server rejected the assertion: drop the now-suspect cached token (see
199-
// the iOS branch above) before discarding the key and re-attesting.
200-
cachedToken = undefined
205+
// the iOS branch above), guarded by the current generation, before
206+
// discarding the key and re-attesting.
207+
if (generation === handshakeGeneration) cachedToken = undefined
201208
console.warn(
202209
`[attestation] assertion rejected (${assertResponse.status}); re-attesting`
203210
)
@@ -265,7 +272,7 @@ const runHandshake = (): void => {
265272
// lock released so a newer handshake can start. Tag each attempt so a stale
266273
// one that finally resolves cannot clobber the newer handshake's token.
267274
const generation = ++handshakeGeneration
268-
const handshake: Promise<void> = performHandshake()
275+
const handshake: Promise<void> = performHandshake(generation)
269276
.then(freshToken => {
270277
if (generation !== handshakeGeneration) return
271278
lastFailureAt = 0

0 commit comments

Comments
 (0)