{"id":16088289,"url":"https://github.com/kylmakalle/devicecheck","last_synced_at":"2025-10-28T18:31:14.130Z","repository":{"id":41532394,"uuid":"354653307","full_name":"Kylmakalle/devicecheck","owner":"Kylmakalle","description":"Reduce fraudulent use of your services by managing device state and asserting app integrity via Apple DeviceCheck API with this Python wrapper.","archived":false,"fork":false,"pushed_at":"2024-08-17T03:31:56.000Z","size":39,"stargazers_count":24,"open_issues_count":1,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-06T08:09:13.031Z","etag":null,"topics":["api","apple","appsec","devicecheck","ios","macos","python","security","swift"],"latest_commit_sha":null,"homepage":"https://developer.apple.com/documentation/devicecheck/accessing-and-modifying-per-device-data","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kylmakalle.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-04T21:48:57.000Z","updated_at":"2024-11-20T17:32:30.000Z","dependencies_parsed_at":"2024-05-03T23:27:46.633Z","dependency_job_id":"9171fc23-2c40-4e6b-9fea-2c134205d649","html_url":"https://github.com/Kylmakalle/devicecheck","commit_stats":{"total_commits":27,"total_committers":3,"mean_commits":9.0,"dds":0.07407407407407407,"last_synced_commit":"41b520d822a2bec12a64c81d0b0ae125c1749996"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kylmakalle%2Fdevicecheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kylmakalle%2Fdevicecheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kylmakalle%2Fdevicecheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kylmakalle%2Fdevicecheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kylmakalle","download_url":"https://codeload.github.com/Kylmakalle/devicecheck/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238696489,"owners_count":19515178,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","apple","appsec","devicecheck","ios","macos","python","security","swift"],"created_at":"2024-10-09T13:35:41.393Z","updated_at":"2025-10-28T18:31:08.731Z","avatar_url":"https://github.com/Kylmakalle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Apple DeviceCheck\n\n\u003c!-- [![codecov](https://codecov.io/gh/Kylmakalle/devicecheck/branch/main/graph/badge.svg?token=2IKMSQUGH3)](https://codecov.io/gh/Kylmakalle/devicecheck) --\u003e\n\n[Accessing and Modifying Per-Device Data](https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data)\n\nUse a token from your app to validate requests, query and modify two per-device binary digits stored on Apple servers.\n\n# Features\n\n- Prevent API \u0026 Content abuse with validating requests via Apple device token\n- Query and modify two bits of data to achieve up to **four remote states** saved on Apple servers\n- Easy to use configuration\n- [Examples](tests/integration)\n- Integrations with modern web frameworks\n\n# Prepare\n\nVisit https://developer.apple.com/account/resources/authkeys/list and create new **Key** with **DeviceCheck** permission\n\n# Install\n\n```\npip install devicecheck\n```\n\n# Usage (Python)\n\n### Setup\n\n```python\nfrom devicecheck import DeviceCheck\n\ndevice_check = DeviceCheck(\n    team_id=\"XX7AN23E0Z\",  # https://developer.apple.com/account/#/membership/\n    bundle_id=\"com.akentev.app\",\n    key_id=\"JSAD983ENA\",  # Generated at https://developer.apple.com/account/resources/authkeys/list\n    private_key=\"/path/to/AuthKey_JSAD983ENA.p8\",\n    # Generated file at https://developer.apple.com/account/resources/authkeys/list\n    dev_environment=True,  # True if using development Apple environment, False if using in production.\n    # Remember to set dev_environment=False in production!\n)\n```\n\n### Asyncio setup\n```python\nfrom devicecheck.asyncio import AsyncioDeviceCheck\n```\nThe rest will be the same, except for network methods must be `await`'ed\n\n### Validate device\n\n```python\nresult = device_check.validate_device_token(device_token)\n\nif result.is_ok:\n    print('OK! Device is valid')\nelse:\n    print('Bad news. Unable to validate device')\n```\n\n### Update bits data\n\n```python\n# Can use both integers, strings and booleans. Will be converted with bool(value)\nresult = device_check.update_two_bits(device_token, bit_0=1, bit_1=False)\n\n# Can update bits separately\nresult = device_check.update_two_bits(device_token, bit_0=True)\n\nif result.is_ok:\n    print('Bits updated')\nelse:\n    print(f'Something went wrong. {result}')\n```\n\n### Query bits data\n\n```python\n# Can use both integers, strings and booleans\nresult = device_check.query_two_bits(device_token)\n\nif result.is_ok:\n    print(f'First bit {result.bit_0}')  # True\n    print(f'Second bit {result.bit_1}')  # False\n    print(f'Last update time {result.bits_last_update_time}')  # 2020-04\nelse:\n    print(f'Something went wrong. {result}')\n```\n\n# Web server decorators\n\nYou can easily integrate devicecheck to your webserver using a decorator. Specify a supported framework, or leave `None`\nto try universal parser.\n\n```python\nfrom devicecheck.decorators import validate_device  # for sync code\nfrom devicecheck.decorators import DCSupportedFrameworks\nfrom devicecheck import DeviceCheck\n\ndevice_check = DeviceCheck(...)\n\n# Set response that will be returned on invalid token\nINVALID_TOKEN_RESPONSE = ('Invalid device_token', 403)\n\n\n@app.route('/validate')\n@validate_device(device_check, framework=DCSupportedFrameworks.flask, on_invalid_token=INVALID_TOKEN_RESPONSE)\ndef endpoint():\n    return 'Content'\n```\n\n## Sync code\n\nUse sync decorator\n\n```python\nfrom devicecheck.decorators import validate_device\nfrom devicecheck.decorators import DCSupportedFrameworks\n```\n\n### Flask\n\n```python\nINVALID_TOKEN_RESPONSE = ('Invalid device_token', 403)\nframework = DCSupportedFrameworks.flask\n```\n\u003c!-- \n### Django Rest Framework (DRF)\n\n```python\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\nINVALID_TOKEN_RESPONSE = Response('Invalid device_token', status=status.HTTP_403_FORBIDDEN)\nframework = DCSupportedFrameworks.drf\n```\n\n### Django\n\n```python\nfrom django.http import HttpResponse\n\nINVALID_TOKEN_RESPONSE = HttpResponse('Invalid device_token', status_code=403)\nframework = DCSupportedFrameworks.django\n```\n --\u003e\n\n## Async code\n\nUse Async decorator\n\n```python\nfrom devicecheck.decorators import async_validate_device\nfrom devicecheck.decorators import DCSupportedAsyncFrameworks\n```\n\n### Sanic\n\n```python\nfrom sanic.response import text\n\nINVALID_TOKEN_RESPONSE = text('Invalid device_token', status=403)\nframework = DCSupportedAsyncFrameworks.sanic\n```\n\n### FastAPI\n\n```python\nfrom fastapi.responses import PlainTextResponse\n\nINVALID_TOKEN_RESPONSE = PlainTextResponse('Invalid device_token', status_code=403)\nframework = DCSupportedAsyncFrameworks.fastapi\n```\n\n# Tests \u0026 Mock\nWell, it's kinda hard to automate testing, because Devicecheck requires real device (Simulators won't work). In case you\nneed to disable decorators, pass `SKIP_DEVICE_CHECK_DECORATOR=True` environment variable.\n\nYou can also mock validation, pass `MOCK_DEVICE_CHECK_DECORATOR_TOKEN=XXXXXXXXXXXXX`, it will be a hardcoded valid token\nvalue.\n\n```bash\nMOCK_DEVICE_CHECK_DECORATOR_TOKEN=\"device-check-token\" python -m unittest tests/integrational/main.py\n```\n\nFor Debug logs, including requests body, pass a `DEBUG` environment variable.\n\n# Exceptions\n\nLibrary represents an `AppleException` class with attributes `status_code` and `description`\nRequires `raise_on_error=True` parameter for `DeviceCheck` instance.\n\n# Usage (Swift)\n\n### Generate device token\n\n```swift\nimport DeviceCheck\n\npublic func getDeviceToken(completion: @escaping (String?) -\u003e ()) {\n    if #available(iOS 11.0, *) {\n        let currentDevice = DCDevice.current\n        if currentDevice.isSupported\n        {\n            currentDevice.generateToken(completionHandler: { (data, error) in\n                if let tokenData = data {\n                    let tokenString = tokenData.base64EncodedString()\n                    print(\"Received device token\")\n                    completion(tokenString)\n                } else{\n                    print(\"Error generating token: \\(error!.localizedDescription)\")\n                }\n            })\n        } else {\n            print(\"Device is not supported\") // Simulators or etc.\n        }\n    } else {\n        print(\"Device OS is lower than iOS 11\")\n    }\n}\n\n```\n\n### Pass device token in HTTP request\n\nHeader or Body\n\n```swift\ngetDeviceToken { deviceToken in\n    var request = URLRequest(url: \"...\")\n    request.httpMethod = \"POST\"\n    \n    // Header\n    request.setValue(deviceToken, forHTTPHeaderField: \"Device-Token\")\n    \n    // Body\n    request.setValue(\"application/json; charset=utf-8\", forHTTPHeaderField: \"Content-Type\")\n    let json = [\"device_token\": deviceToken] as [String : Any]\n    let jsonData = try! JSONSerialization.data(withJSONObject: json)\n    request.httpBody = jsonData as Data\n    \n    // Send it to server\n    let downloadTask = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in\n        ...\n    })\n}\n```\n\n# License\n\n[MIT](LICENSE)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylmakalle%2Fdevicecheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkylmakalle%2Fdevicecheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylmakalle%2Fdevicecheck/lists"}