Skip to content

Instantly share code, notes, and snippets.

@schirrmacher
Last active July 9, 2024 20:32
Show Gist options
  • Save schirrmacher/d9e0a1109d58aae1611f1725c7ec7f53 to your computer and use it in GitHub Desktop.
Save schirrmacher/d9e0a1109d58aae1611f1725c7ec7f53 to your computer and use it in GitHub Desktop.
DeviceCheck Backend Example Implementation for Validating iOS Device Authenticity
import jwt from "jsonwebtoken";
import uuid from "uuid";
import config from "../../../config";
import { DeviceCheckService, DeviceCheckParams } from "./DeviceCheckService";
import BaseService from "./BaseService";
export class AppleDeviceCheckService extends BaseService implements DeviceCheckService {
public loggingTag = "AppleDeviceCheck";
public host = "https://api.devicecheck.apple.com/v1/query_two_bits";
public async shouldProceed(params: DeviceCheckParams): Promise<boolean> {
const { token } = params;
const body = {
"device_token": token,
"transaction_id": uuid.v4(),
"timestamp": Date.now()
};
// Create a private key for signing: https://help.apple.com/developer-account/#/devc3cc013b7
const jwToken = jwt.sign({}, config.apple.deviceCheckPrivateKey, {
algorithm: "ES256",
keyid: config.apple.keyid,
issuer: config.apple.teamid
});
const options = {
headers: {
"Authorization": "Bearer " + jwToken
}
};
const deviceCheckResponse = await this.post({ url: this.host, body, options });
const status = deviceCheckResponse.status;
switch (status) {
/*
200 OK - The transaction was successful
200 Bit State Not Found - The bit state wasn't found
400 Bad Device Token - The device token is missing or badly formatted
400 Bad Bits - The bits are missing or badly formatted
400 Bad Timestamp - The timestamp is missing or badly formatted
400 Bad Authorization Token - The authentication token is missing or badly formatted
400 Bad Payload - The payload is missing or badly formatted
401 Invalid Authorization Token - The authentication token can't be verified
401 Authorization Token Expired - The authentication token has expired
403 Forbidden - The specified action isn't allowed
405 Method Not Allowed - The endpoint was used incorrectly
429 Too Many Requests - Too many requests were sent to the server
500 Server Error - An error occurred on the server
503 Service Unavailable - The service is unavailable
*/
// in some cases errors are thrown
// otherwise incoming requests are blocked for external reasons
case 200:
// do nothing, all good
break;
case 401:
throw Error(`${this.loggingTag}: we have to fix authorization`);
case 403:
case 405:
throw Error(`${this.loggingTag}: we made a programming mistake`);
case 429:
case 500:
case 503:
throw Error(`${this.loggingTag}: service not reliable`);
default:
// device check token was probably modified or is invalid
this.logger.error(`${this.loggingTag}: suspicious behavior detected: status ${status})`);
}
// 200 means that it is a real device
// we don't care about the bit states here
return status === 200;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment