Package: devicecheck
Apple DeviceCheckAccessing and Modifying Per-Device DataUse a token from your app to validate requests, query and modify two per-device binary digits stored on Apple servers.FeaturesPrevent API & Content abuse with validating requests via Apple device tokenQuery and modify two bits of data to achieve up tofour remote statessaved on Apple serversEasy to use configurationExamplesIntegrations with modern web frameworksPrepareVisithttps://developer.apple.com/account/resources/authkeys/listand create newKeywithDeviceCheckpermissionInstallpip install devicecheckUsage (Python)SetupfromdevicecheckimportDeviceCheckdevice_check=DeviceCheck(team_id="XX7AN23E0Z",# https://developer.apple.com/account/#/membership/bundle_id="com.akentev.app",key_id="JSAD983ENA",# Generated at https://developer.apple.com/account/resources/authkeys/listprivate_key="/path/to/AuthKey_JSAD983ENA.p8",# Generated file at https://developer.apple.com/account/resources/authkeys/listdev_environment=True,# True if using development Apple environment, False if using in production.# Remember to set dev_environment=False in production!)Validate deviceresult=device_check.validate_device_token(device_token)ifresult.is_ok:print('OK! Device is valid')else:print('Bad news. Unable to validate device')Update bits data# Can use both integers, strings and booleans. Will be converted with bool(value)result=device_check.update_two_bits(device_token,bit_0=1,bit_1=False)# Can update bits separatelyresult=device_check.update_two_bits(device_token,bit_0=True)ifresult.is_ok:print('Bits updated')else:print(f'Something went wrong.{result}')Query bits data# Can use both integers, strings and booleansresult=device_check.query_two_bits(device_token)ifresult.is_ok:print(f'First bit{result.bit_0}')# Trueprint(f'Second bit{result.bit_1}')# Falseprint(f'Last update time{result.bits_last_update_time}')# 2020-04else:print(f'Something went wrong.{result}')Web server decoratorsYou can easily integrate devicecheck to your webserver using a decorator. Specify a supported framework, or leaveNoneto try universal parser.fromdevicecheck.decoratorsimportvalidate_device# for sync codefromdevicecheck.decoratorsimportDCSupportedFrameworksfromdevicecheckimportDeviceCheckdevice_check=DeviceCheck(...)# Set response that will be returned on invalid tokenINVALID_TOKEN_RESPONSE=('Invalid device_token',403)@app.route('/validate')@validate_device(device_check,framework=DCSupportedFrameworks.flask,on_invalid_token=INVALID_TOKEN_RESPONSE)defendpoint():return'Content'Sync codeUse sync decoratorfromdevicecheck.decoratorsimportvalidate_devicefromdevicecheck.decoratorsimportDCSupportedFrameworksFlaskINVALID_TOKEN_RESPONSE=('Invalid device_token',403)framework=DCSupportedFrameworks.flaskAsync codeUse Async decoratorfromdevicecheck.decoratorsimportasync_validate_devicefromdevicecheck.decoratorsimportDCSupportedAsyncFrameworksSanicfromsanic.responseimporttextINVALID_TOKEN_RESPONSE=text('Invalid device_token',status=403)framework=DCSupportedAsyncFrameworks.sanicFastAPIfromfastapi.responsesimportPlainTextResponseINVALID_TOKEN_RESPONSE=PlainTextResponse('Invalid device_token',status_code=403)framework=DCSupportedAsyncFrameworks.fastapiTests & MockWell, it's kinda hard to automate testing, because Devicecheck requires real device (Simulators won't work). In case you
need to disable decorators, passSKIP_DEVICE_CHECK_DECORATOR=Trueenvironment variable.You can also mock validation, passMOCK_DEVICE_CHECK_DECORATOR_TOKEN=XXXXXXXXXXXXX, it will be a hardcoded valid token
value.MOCK_DEVICE_CHECK_DECORATOR_TOKEN="device-check-token"python-munittesttests/integrational/main.pyFor Debug logs, including requests body, pass aDEBUGenvironment variable.ExceptionsLibrary represents anAppleExceptionclass with attributesstatus_codeanddescriptionRequiresraise_on_error=Trueparameter forDeviceCheckinstance.Usage (Swift)Generate device tokenimportDeviceCheckpublicfuncgetDeviceToken(completion:@escaping(String?)->()){if#available(iOS11.0,*){letcurrentDevice=DCDevice.currentifcurrentDevice.isSupported{currentDevice.generateToken(completionHandler:{(data,error)iniflettokenData=data{lettokenString=tokenData.base64EncodedString()print("Received device token")completion(tokenString)}else{print("Error generating token:\(error!.localizedDescription)")}})}else{print("Device is not supported")// Simulators or etc.}}else{print("Device OS is lower than iOS 11")}}Pass device token in HTTP requestHeader or BodygetDeviceToken{deviceTokeninvarrequest=URLRequest(url:"...")request.httpMethod="POST"// Headerrequest.setValue(deviceToken,forHTTPHeaderField:"Device-Token")// Bodyrequest.setValue("application/json; charset=utf-8",forHTTPHeaderField:"Content-Type")letjson=["device_token":deviceToken]as[String:Any]letjsonData=try!JSONSerialization.data(withJSONObject:json)request.httpBody=jsonDataasData// Send it to serverletdownloadTask=URLSession.shared.dataTask(with:request,completionHandler:{data,response,errorin...})}LicenseMIT