Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kylmakalle/devicecheck
Reduce fraudulent use of your services by managing device state and asserting app integrity via Apple DeviceCheck API with this Python wrapper.
https://github.com/kylmakalle/devicecheck
api apple appsec devicecheck ios macos python security swift
Last synced: 5 days ago
JSON representation
Reduce fraudulent use of your services by managing device state and asserting app integrity via Apple DeviceCheck API with this Python wrapper.
- Host: GitHub
- URL: https://github.com/kylmakalle/devicecheck
- Owner: Kylmakalle
- License: mit
- Created: 2021-04-04T21:48:57.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-08-17T03:31:56.000Z (4 months ago)
- Last Synced: 2024-12-17T00:11:56.526Z (12 days ago)
- Topics: api, apple, appsec, devicecheck, ios, macos, python, security, swift
- Language: Python
- Homepage: https://developer.apple.com/documentation/devicecheck/accessing-and-modifying-per-device-data
- Size: 38.1 KB
- Stars: 24
- Watchers: 3
- Forks: 5
- Open Issues: 1
-
Metadata Files:
- Readme: Readme.md
- License: LICENSE
Awesome Lists containing this project
README
# Apple DeviceCheck
[Accessing and Modifying Per-Device Data](https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data)
Use a token from your app to validate requests, query and modify two per-device binary digits stored on Apple servers.
# Features
- Prevent API & Content abuse with validating requests via Apple device token
- Query and modify two bits of data to achieve up to **four remote states** saved on Apple servers
- Easy to use configuration
- [Examples](tests/integration)
- Integrations with modern web frameworks# Prepare
Visit https://developer.apple.com/account/resources/authkeys/list and create new **Key** with **DeviceCheck** permission
# Install
```
pip install devicecheck
```# Usage (Python)
### Setup
```python
from devicecheck import DeviceCheckdevice_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/list
private_key="/path/to/AuthKey_JSAD983ENA.p8",
# Generated file at https://developer.apple.com/account/resources/authkeys/list
dev_environment=True, # True if using development Apple environment, False if using in production.
# Remember to set dev_environment=False in production!
)
```### Asyncio setup
```python
from devicecheck.asyncio import AsyncioDeviceCheck
```
The rest will be the same, except for network methods must be `await`'ed### Validate device
```python
result = device_check.validate_device_token(device_token)if result.is_ok:
print('OK! Device is valid')
else:
print('Bad news. Unable to validate device')
```### Update bits data
```python
# 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 separately
result = device_check.update_two_bits(device_token, bit_0=True)if result.is_ok:
print('Bits updated')
else:
print(f'Something went wrong. {result}')
```### Query bits data
```python
# Can use both integers, strings and booleans
result = device_check.query_two_bits(device_token)if result.is_ok:
print(f'First bit {result.bit_0}') # True
print(f'Second bit {result.bit_1}') # False
print(f'Last update time {result.bits_last_update_time}') # 2020-04
else:
print(f'Something went wrong. {result}')
```# Web server decorators
You can easily integrate devicecheck to your webserver using a decorator. Specify a supported framework, or leave `None`
to try universal parser.```python
from devicecheck.decorators import validate_device # for sync code
from devicecheck.decorators import DCSupportedFrameworks
from devicecheck import DeviceCheckdevice_check = DeviceCheck(...)
# Set response that will be returned on invalid token
INVALID_TOKEN_RESPONSE = ('Invalid device_token', 403)@app.route('/validate')
@validate_device(device_check, framework=DCSupportedFrameworks.flask, on_invalid_token=INVALID_TOKEN_RESPONSE)
def endpoint():
return 'Content'
```## Sync code
Use sync decorator
```python
from devicecheck.decorators import validate_device
from devicecheck.decorators import DCSupportedFrameworks
```### Flask
```python
INVALID_TOKEN_RESPONSE = ('Invalid device_token', 403)
framework = DCSupportedFrameworks.flask
```## Async code
Use Async decorator
```python
from devicecheck.decorators import async_validate_device
from devicecheck.decorators import DCSupportedAsyncFrameworks
```### Sanic
```python
from sanic.response import textINVALID_TOKEN_RESPONSE = text('Invalid device_token', status=403)
framework = DCSupportedAsyncFrameworks.sanic
```### FastAPI
```python
from fastapi.responses import PlainTextResponseINVALID_TOKEN_RESPONSE = PlainTextResponse('Invalid device_token', status_code=403)
framework = DCSupportedAsyncFrameworks.fastapi
```# Tests & Mock
Well, it's kinda hard to automate testing, because Devicecheck requires real device (Simulators won't work). In case you
need to disable decorators, pass `SKIP_DEVICE_CHECK_DECORATOR=True` environment variable.You can also mock validation, pass `MOCK_DEVICE_CHECK_DECORATOR_TOKEN=XXXXXXXXXXXXX`, it will be a hardcoded valid token
value.```bash
MOCK_DEVICE_CHECK_DECORATOR_TOKEN="device-check-token" python -m unittest tests/integrational/main.py
```For Debug logs, including requests body, pass a `DEBUG` environment variable.
# Exceptions
Library represents an `AppleException` class with attributes `status_code` and `description`
Requires `raise_on_error=True` parameter for `DeviceCheck` instance.# Usage (Swift)
### Generate device token
```swift
import DeviceCheckpublic func getDeviceToken(completion: @escaping (String?) -> ()) {
if #available(iOS 11.0, *) {
let currentDevice = DCDevice.current
if currentDevice.isSupported
{
currentDevice.generateToken(completionHandler: { (data, error) in
if let tokenData = data {
let tokenString = 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 request
Header or Body
```swift
getDeviceToken { deviceToken in
var request = URLRequest(url: "...")
request.httpMethod = "POST"
// Header
request.setValue(deviceToken, forHTTPHeaderField: "Device-Token")
// Body
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let json = ["device_token": deviceToken] as [String : Any]
let jsonData = try! JSONSerialization.data(withJSONObject: json)
request.httpBody = jsonData as Data
// Send it to server
let downloadTask = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
...
})
}
```# License
[MIT](LICENSE)