{"id":42421194,"url":"https://github.com/tkhq/passkeyapp","last_synced_at":"2026-01-28T02:57:37.222Z","repository":{"id":220196369,"uuid":"751005073","full_name":"tkhq/passkeyapp","owner":"tkhq","description":"Minimal React Native app featuring native passkeys and integration with Turnkey","archived":false,"fork":false,"pushed_at":"2024-10-31T03:17:39.000Z","size":452,"stargazers_count":4,"open_issues_count":3,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-31T04:18:39.359Z","etag":null,"topics":["expo","passkeys","passkeys-demo","react-native","turnkey"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkhq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-01-31T18:44:28.000Z","updated_at":"2024-10-29T06:36:27.000Z","dependencies_parsed_at":"2024-06-13T02:22:16.041Z","dependency_job_id":null,"html_url":"https://github.com/tkhq/passkeyapp","commit_stats":null,"previous_names":["r-n-o/passkeyapp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tkhq/passkeyapp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkhq%2Fpasskeyapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkhq%2Fpasskeyapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkhq%2Fpasskeyapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkhq%2Fpasskeyapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkhq","download_url":"https://codeload.github.com/tkhq/passkeyapp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkhq%2Fpasskeyapp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28835546,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T02:10:51.810Z","status":"ssl_error","status_checked_at":"2026-01-28T02:10:50.806Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["expo","passkeys","passkeys-demo","react-native","turnkey"],"created_at":"2026-01-28T02:57:36.804Z","updated_at":"2026-01-28T02:57:37.216Z","avatar_url":"https://github.com/tkhq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Passkey app\n\nThis repo contains a sample passkey app which creates/uses passkeys. It it built with Expo.\n\nThe functionality is simple:\n\n- \"Sign Up\" creates a new passkey and an associated Turnkey sub-organization\n- \"Sign In\" gets a passkey signature and uses Turnkey's \"Who am I?\" endpoint to retrieve the sub-organization ID\n\nHere's a video of it in action on iOS:\n\nhttps://github.com/r-n-o/passkeyapp/assets/104520680/9fabf71c-d88a-4631-8bfa-14b55c72967b\n\nAnd here it is on Android:\n\nhttps://github.com/r-n-o/passkeyapp/assets/104520680/d71e6945-8962-46a5-98e7-f053c75c28d0\n\n## Turnkey setup\n\nSign up for a new Turnkey organization at app.turnkey.com and create a user able to create sub-organizations (this can be done with policies):\n\n```json\n{\n  \"effect\": \"EFFECT_ALLOW\",\n  \"consensus\": \"approvers.any(user, user.id == '\u003cuser id goes here\u003e')\",\n  \"condition\": \"activity.resource == 'ORGANIZATION' \u0026\u0026 activity.action == 'CREATE'\"\n}\n```\n\nThen generate a fresh API key with the `turnkey` CLI (`turnkey gen -k new-api-key-name --organization \u003corg id\u003e`). You can also visit https://r-n-o.github.io/p256-keygen/ and get values from there if it's simply for testing purposes.\n\nOnce you have org ID, public and private API key:\n\n```sh\ncp .env.example .env\n```\n\nThen insert your values in the new `.env` file\n\n## Running the app locally on iOS\n\n### Provisioning profile\n\nTo trigger passkey prompts you will need a way to sign the app with a proper provisioning profile. Follow the steps outlined in https://docs.expo.dev/app-signing/app-credentials/#provisioning-profiles. TL;DR: run `eas credentials`.\n\n### Expo build \u0026 run recipes\n\n- `npx expo run:ios` will start the app using expo on a simulator\n- To run with expo on your iOS device, connect the device and run `npx expo run:ios --device` (the device needs to have developer mode enabled and be connected to your Mac)\n- `eas build -p ios --profile preview` will trigger a build through expo (online CI). This gets you a QR code to install on any device covered by the provisioning profile\n- `npx expo prebuild --platform ios` will \"prebuild\" and let you build locally with xcode. Then open the project with the `PasskeyApp.xcworkspace` file to build with xcode (this is useful)\n- `eas build --platform ios --local --profile preview` can be used to run a local build without xcode, and will produce a `.ipa` file. The `.ipa` can be dropped on the device through xcode: \"Window\" -\u003e \"Devices and Simulators\", then drop the app under the \"Installed Apps\" section.\n\n## Running the app locally on Android\n\nAndroid requires a signed APK linked to an origin via an `assetlinks.json` file. Follow [these instructions](https://coderwall.com/p/r09hoq/android-generate-release-debug-keystores) and reference your debug keystore from you `eas.json` file:\n\n```json\n\"android\":  {\n    \"buildType\": \"apk\",\n    \"credentialsSource\": \"local\"\n}\n```\n\nThe above `eas.json` section references \"local\" credentials and will look for a \"credentials.json\" file:\n\n```\n{\n    \"android\": {\n      \"keystore\": {\n        \"keystorePath\": \"/Users/rno/.android/debug.keystore\",\n        \"keystorePassword\": \"android\",\n        \"keyAlias\": \"androiddebugkey\",\n        \"keyPassword\": \"android\"\n      }\n    }\n}\n```\n\nThis is \"okay\" to commit to git given it's only a local debug store without any value. DO NOT DO THIS WITH ANY OTHER KEYSTORE!\n\nFinally, you need to grab your certificate's sha256 fingerprint and associate it with your domain by hosting a new file at `/.well-known/assetlinks.json`:\n\n```json\n[\n  {\n    \"relation\": [\n      \"delegate_permission/common.handle_all_urls\",\n      \"delegate_permission/common.get_login_creds\"\n    ],\n    \"target\": {\n      \"namespace\": \"android_app\",\n      \"package_name\": \"xyz.tkhqlabs.passkeyapp\",\n      \"sha256_cert_fingerprints\": [\n        \"55:16:FF:0F:77:8A:DC:5A:B3:33:1F:B3:56:02:8C:C9:C3:02:20:82:CA:13:91:CC:0C:CA:B5:3C:87:56:2B:2B\",\n        \"43:A8:83:EA:B5:9D:C9:03:99:CF:00:5E:17:01:14:0D:7C:22:64:22:9A:34:39:41:FC:F4:3A:FC:E1:24:03:41\"\n      ]\n    }\n  }\n]\n```\n\nYou can get your certificate fingerprint with:\n\n```sh\n$ keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android\n```\n\n### Expo build \u0026 run recipes\n\n- `eas build --platform android --local --profile preview` produces an APK file\n- If you are testing on a real device, enable [developer mode](https://developer.android.com/studio/debug/dev-options) and pair your device with Android Studio (see [this](https://developer.android.com/studio/run/device#wireless)) to get debug logs via [LogCat](https://developer.android.com/studio/debug/logcat). That's really helpful to figure out what's going on.\n- If you are testing on a simulator you need to log into a Google account on the simulator and configure PIN encryption to test passkey functionality. A good test to see if your simulator is set up correctly: visit https://webauthn.io from the Chrome browser and try to sign up / sign in with a passkey before testing your APK\n- Once you have your simulator or real device ready, install your APK with `adb`:\n\n  ```sh\n  $ adb devices -l\n  List of devices attached\n  adb-27131JEGR40336-UUo6mJ._adb-tls-connect._tcp. device product:bluejay model:Pixel_6a device:bluejay transport_id:3\n  emulator-5554          device product:sdk_gphone64_arm64 model:sdk_gphone64_arm64 device:emu64a transport_id:2\n  emulator-5556          device product:sdk_gphone64_arm64 model:sdk_gphone64_arm64 device:emu64a transport_id:1\n\n  # Note the transport option: \"-t3\".\n  # In this case I'm targeting my \"Pixel_6a\" device because it has \"transport_id:3\"\n  $ adb -t3 install \u003cpath/to/apkfile\u003e.apk\n  ```\n\nNote: a more convenient option if you're simply looking to run debug mode for your app: `npx expo run:android -d` will yield a dropdown of available devices and run your app in debug mode! But careful: **this doesn't work to test passkey functionality because the app won't be signed**.\n\n## `http` folder\n\nIn the HTTP folder you'll find a folder with what's hosted at https://passkeyapp.tkhqlabs.xyz. It contains a Cloudflare worker function to give `apple-app-site-association` the right MIME type.\n\nOtherwise it hosts static content:\n\n- `/.well-known/apple-app-site-association` (required for iOS passkeys)\n- `/.well-known/assetlinks.json` (required for Android passkeys)\n- `index.html` file to register and use passkeys on the web (nice to experiment with passkeys moving from web to native or vice versa)\n\nTo run this locally: `npx wrangler pages dev http`.\n\n## Troubleshooting\n\n### com.apple.AuthenticationServices.AuthorizationError Code=1004\n\n```\n[AuthenticationServices] ASAuthorizationController credential request failed with error: Error\nDomain=com.apple.AuthenticationServices.AuthorizationError Code=1004 \"(null)\"\n```\n\nThis happens when the RPID is incorrect. I have no idea why Apple doesn't return a proper error here. The RPID should be the domain name (inverse of the bundle ID). E.g. `passkeyapp.tkhqlabs.xyz`\n\n### This app cannot be installed because its integrity could not be verified\n\nIf you're trying to installed a `.ipa` file on a device without signing it with a provisioning profile linked to this device, that's the message you get. Make sure you select the right profile when building (this also happened to me if I do not select a profile at all: `eas build --platform ios --local` produces a by-default unsigned `.ipa` file!)\n\n### My apple-app-site-association-file (AASA file) isn't updated? What is the app actually loading?\n\nThis is so frustrating. AASA files aren't loaded directly by apps, they're loaded from a special-purpose Apple CDN which caches these files.\n\nSupposedly it's refreshed on install, but I have seen evidence to the contrary in my testing. Solutions:\n\n- use `mode=developer` ([docs](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_associated-domains)): this lets you bypass SSL restrictions and use a self-signed cert. But you might as well use ngrok, it'll provide a valid cert. Developer mode also causes fetching to go straight from app to server instead of using the Apple CDN. This might work for you!\n- check `curl -v https://app-site-association.cdn-apple.com/a/v1/\u003cyour-domain\u003e` to see what your app is \"seeing\". This is useful if you're trying to debug production-like setups where developer mode isn't an option\n\nAnother tool that can be useful: https://branch.io/resources/aasa-validator. It's a validator for AASA files. You do not need \"applinks\" if all you're doing in passkeys so don't trust 100% of the things it says. But it's useful to rule out basic issues like invalid JSON or MIME type. [This article](https://towardsdev.com/swift-associated-domains-universal-links-aasa-webcredentials-c66900df7b7e) is how I found this tool.\n\n### `{\"error\": \"Native error\", \"message\": \"The\"}`\n\nThis is because `react-native-passkey` isn't loaded in your package.json. I'm not sure why but requiring it from `@turnkey/react-native-passkey-stamper` isn't sufficient. You have to require it from the react-native project itself. Is there something we can do in the `@turnkey` package to remove this requirement? Please open an issue or reach out if you know of something!\n\n### Cannot find module '[...]/code/scripts/generate-specs-cli.js`\n\nThis happened for me during builds:\n\n```\nNode found at: /Users/rno/.nvm/versions/node/v18.18.2/bin/node\nnode:internal/modules/cjs/loader:1080\n  throw err;\n  ^\n\nError: Cannot find module '/Users/rno/tkhq/code/scripts/generate-specs-cli.js'\n    at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)\n    at Module._load (node:internal/modules/cjs/loader:922:27)\n    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:86:12)\n    at node:internal/main/run_main_module:23:47 {\n  code: 'MODULE_NOT_FOUND',\n  requireStack: []\n}\n```\n\nThe reason is `CDPATH` was set:\n\n```\necho $CDPATH\n/Users/rno/tkhq/code\n```\n\nThis was hard to figure out, but someone on the internet had the same issue (https://github.com/facebook/react-native/issues/35747):\n\n```\n$ unset CDPATH\n$ rm -rf ios\n$ npm run ios\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkhq%2Fpasskeyapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkhq%2Fpasskeyapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkhq%2Fpasskeyapp/lists"}