Severity: CRITICAL
File: ApiApp/serializers.py
Line: 72
Description
DeviceRegisterSerializer.validate() calls AttestationHandler with positional arguments:
handler = AttestationHandler(nonce, platform, attestation, assertion, device_id, public_key)
But AttestationHandler.__init__ signature is:
def __init__(
self,
nonce: str,
platform: Literal["android", "ios"],
attestation_token: str | None = None,
assertion_token: str | None = None,
key_id: str | None = None,
public_key: EllipticCurvePublicKey | None = None
):
This means device_id is passed as the key_id parameter (5th positional arg), not as a device identifier.
Impact
For Android:
urlsafe_b64decode_padded(device_id) runs on every registration. Most device IDs are valid base64 so this does not crash, but the decoded _key_id bytes are meaningless (device ID bytes, not a key ID). This is silently ignored for Android since _key_id is only used in iOS flows.
For iOS:
- New device registration:
AppleConfig(self._key_id, ...) uses decoded device_id bytes as the App Attest key identifier. This does not match the actual key ID from Apple, so attestation verification will fail or behave unpredictably.
- Subsequent registration with assertion:
Assertion(..., sha256(self._nonce).digest(), self._public_key, config) uses self._key_id (decoded device_id) in the config. This is wrong — the key_id should be the actual App Attest key identifier.
- Result: iOS devices cannot reliably re-register after their first attestation, and the assertion-based verification path is broken.
Root Cause
Positional argument call instead of keyword arguments. The serializer author intended to pass device_id as a separate concept but it silently landed in the key_id parameter slot.
Fix
Use keyword arguments in the serializer call:
handler = AttestationHandler(
nonce=nonce,
platform=platform,
attestation_token=attestation,
assertion_token=assertion,
public_key=public_key
)
key_id is not passed because it is an Apple-specific internal identifier that the device does not send — the server derives it from the attestation response. For iOS re-registration (assertion flow), the key_id should be stored on the device record and retrieved from there.
Additionally, store key_id on the AttestedFCMDevice model so it can be used for subsequent assertion verification.
Severity: CRITICAL
File:
ApiApp/serializers.pyLine: 72
Description
DeviceRegisterSerializer.validate()callsAttestationHandlerwith positional arguments:But
AttestationHandler.__init__signature is:This means
device_idis passed as thekey_idparameter (5th positional arg), not as a device identifier.Impact
For Android:
urlsafe_b64decode_padded(device_id)runs on every registration. Most device IDs are valid base64 so this does not crash, but the decoded_key_idbytes are meaningless (device ID bytes, not a key ID). This is silently ignored for Android since_key_idis only used in iOS flows.For iOS:
AppleConfig(self._key_id, ...)uses decodeddevice_idbytes as the App Attest key identifier. This does not match the actual key ID from Apple, so attestation verification will fail or behave unpredictably.Assertion(..., sha256(self._nonce).digest(), self._public_key, config)usesself._key_id(decoded device_id) in the config. This is wrong — the key_id should be the actual App Attest key identifier.Root Cause
Positional argument call instead of keyword arguments. The serializer author intended to pass
device_idas a separate concept but it silently landed in thekey_idparameter slot.Fix
Use keyword arguments in the serializer call:
key_idis not passed because it is an Apple-specific internal identifier that the device does not send — the server derives it from the attestation response. For iOS re-registration (assertion flow), the key_id should be stored on the device record and retrieved from there.Additionally, store
key_idon theAttestedFCMDevicemodel so it can be used for subsequent assertion verification.