{"id":13523177,"url":"https://github.com/ilbertt/ic-react-native-jwt-auth","last_synced_at":"2025-04-01T00:31:02.495Z","repository":{"id":229879770,"uuid":"777386729","full_name":"ilbertt/ic-react-native-jwt-auth","owner":"ilbertt","description":"React Native (Expo) + JWT Authentication + Rust ICP canister","archived":false,"fork":false,"pushed_at":"2024-08-24T16:54:05.000Z","size":1278,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-08-24T18:22:48.978Z","etag":null,"topics":["icp","internet-computer","jwt-authentication","react-native","rust","web3"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/ilbertt.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":"2024-03-25T18:54:40.000Z","updated_at":"2024-08-24T16:54:08.000Z","dependencies_parsed_at":"2024-05-02T14:14:00.796Z","dependency_job_id":"c19ba374-df15-432b-b947-b4db137081f5","html_url":"https://github.com/ilbertt/ic-react-native-jwt-auth","commit_stats":null,"previous_names":["ilbertt/ic-react-native-jwt-auth"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilbertt%2Fic-react-native-jwt-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilbertt%2Fic-react-native-jwt-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilbertt%2Fic-react-native-jwt-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilbertt%2Fic-react-native-jwt-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ilbertt","download_url":"https://codeload.github.com/ilbertt/ic-react-native-jwt-auth/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222688173,"owners_count":17023297,"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":["icp","internet-computer","jwt-authentication","react-native","rust","web3"],"created_at":"2024-08-01T06:00:56.537Z","updated_at":"2024-11-02T07:31:23.843Z","avatar_url":"https://github.com/ilbertt.png","language":"Rust","funding_links":[],"categories":["Courses, Tutorials, and Samples"],"sub_categories":["Tutorials and Samples"],"readme":"# React Native (Expo) app with JWT Authentication and IC Rust canister\n\nThis is a Proof-of-Concept [React Native Expo](https://expo.dev/) mobile application that uses [JSON Web Tokens](https://jwt.io/) to authenticate to an [Internet Computer canister](https://internetcomputer.org/docs/current/concepts/canisters-code).\n\nThe main idea behind this PoC is to have an off-chain [OpenID](https://openid.net/specs/openid-connect-core-1_0.html) authentication service ([Auth0](https://auth0.com) in this case) that mints a JWT that the user can send to the canister to generate a delegated [identity](https://internetcomputer.org/docs/current/concepts/glossary/#identity). This way, the user can identify themselves on the canister with the same [principal](https://internetcomputer.org/docs/current/concepts/glossary/#principal) across sessions. It offers a trade-off between ease of use for the end user and decentralization of the authentication process.\n\nThis PoC has the following components:\n\n- React Native Expo mobile app: [src/app](./src/app)\n- off-chain TypeScript backend: [src/app_backend](./src/app_backend)\n- IC Rust backend canister: [src/ic_backend](./src/ic_backend)\n- [Auth0](https://auth0.com/) authentication provider\n\nHead over to the [How it works](#how-it-works) section for more details.\n\n## Requirements\n\n- [Bun](https://bun.sh/) Javascript runtime\n- [Expo's requirements](https://docs.expo.dev/get-started/installation/#requirements) and [local development prerequisites](https://docs.expo.dev/guides/local-app-development/#prerequisites)\n- [Rust](https://www.rust-lang.org/) with the `wasm32-unknown-unknown` target:\n\n    ```bash\n    rustup target add wasm32-unknown-unknown\n    ```\n\n- [dfx](https://internetcomputer.org/docs/current/developer-docs/getting-started/install/) (better if installed with the dfx version manager - [`dfxvm`](https://github.com/dfinity/dfxvm))\n- an [Auth0](https://auth0.com) account\n- an Android/iOS device or simulator\n\n### Configure Auth0\n\nFollow these steps to configure Auth0:\n\n1. [Create a Tenant](https://auth0.com/docs/get-started/auth0-overview/create-tenants) and get your Auth0 Tenant domain, which looks like `\u003cTENANT_NAME\u003e.\u003cTENANT_REGION\u003e.auth0.com`\n2. [Create a Native Application](https://auth0.com/docs/get-started/auth0-overview/create-applications/native-apps)\n3. In the _Dashboard \u003e Applications \u003e YOUR_APP \u003e Settings_ tab, set the **Allowed Callback URLs** and **Allowed Logout URLs** to:\n    - `io.icp0.jwtauthdemo.auth0://\u003cYOUR_AUTH0_TENANT_DOMAIN\u003e/ios/io.icp0.jwtauthdemo/callback`\n    - `io.icp0.jwtauthdemo.auth0://\u003cYOUR_AUTH0_TENANT_DOMAIN\u003e/android/io.icp0.jwtauthdemo/callback`\n\n    Where `\u003cYOUR_AUTH0_TENANT_DOMAIN\u003e` is the Auth0 Tenant domain and `io.icp0.jwtauthdemo` is both the **Android Package Name** and **iOS Bundle Identifier**, as configured in the [app.config.js](./src/app/app.config.js) file.\n4. In the _Dashboard \u003e Applications \u003e YOUR_APP \u003e Credentials_ tab, set the **Authentication Method** to **None** (instead of **Client Secret (Post)**)\n\nThe 1st step of the Auth0 React Native [Quickstart interactive guide](https://auth0.com/docs/quickstart/native/react-native-expo/interactive) can be helpful too.\n\n## Usage\n\nInstall the dependencies:\n\n```bash\nbun install\n```\n\nCopy the [`.env.example`](./.env.example) file to `.env`:\n\n```bash\ncp .env.example .env\n```\nand replace the values with your own.\n\nStart the IC backend:\n\n```bash\n# in terminal 1\nbun start:dfx\n# in terminal 2\nbun deploy:ic_backend\n```\n\nStart the off-chain backend:\n\n```bash\n# in terminal 3\nbun start:app_backend\n```\n\nStart the mobile app:\n\n```bash\n# in terminal 4\ncd src/app\ncp ../../.env .env\nbun expo prebuild\ncd ../..\n# if you want to start the app for Android\nbun start:android\n# if you want to start the app for iOS\nbun start:ios\n```\n\u003e You may need to manually start the Android/iOS emulator.\n\nSee the `expo start` CLI [docs](https://docs.expo.dev/more/expo-cli/#develop) for more information.\n\n## Testing\n\nUnit tests are available for the IC Rust backend canister. Simply run:\n\n```bash\n./scripts/unit-test.sh\n```\n\nIntegration tests are available for the IC Rust backend canister. Simply run:\n\n```bash\n./scripts/integration-test.sh\n```\n\n## How it works\n\nThis PoC is highly inspired by [this discussion](https://forum.dfinity.org/t/25334/7) on the Internet Computer forum.\n\n```mermaid\nsequenceDiagram\n    participant M as Mobile app\n    participant A as Authentication Provider\n    participant C as Canister\n    participant B as Off-chain Backend\n\n    note over A,B: JWK data is shared\n    note over M: Generates session PK/SK\n    M-\u003e\u003e+A: Request id_token with PK\n    A-\u003e\u003eB: Request new/existing user\n    B-\u003e\u003eA: User confirmed\n    A-\u003e\u003e-M: Valid id_token(PK)\n    M-\u003e\u003e+C: Request prepare_delegation with id_token(PK) signed with SK\n    note over C: Validate id_token against JWK\n    C--\u003e\u003eC: Extract sub claim from id_token\n    C--\u003e\u003eC: Create a canister signature and store in delegation map\n    C--\u003e\u003eC: Derive principal from canister signature\n    C--\u003e\u003eC: Assign principal to sub in users map\n    C-\u003e\u003e-M: user_key, expiration\n    M-\u003e\u003e+C: Request get_delegation with id_token(PK), expiration signed with SK\n    note over C: Validate id_token against JWK\n    C--\u003e\u003eC: Read delegation from delegation map\n    C-\u003e\u003e-M: Signed Delegation\n    note over M: Session PK/SK is now delegated\n    note over M: User is authenticated\n    M--\u003e\u003eC: Request authenticated with delegation signed with SK\n    note over C: User identified by delegation principal\n    C--\u003e\u003eC: Read sub from users map\n    C--\u003e\u003eM: Confirm authentication\n    M--\u003e\u003eB: Generic request with ic_token\n    note over B: User identified by sub claim\n    B--\u003e\u003eM: ...\n```\n\nThe main steps are:\n\n0. The JWKs must be fetched from Auth0 and stored in the canister and off-chain backend.\n\n    In the current implementation, the canister fetches them once on deployment and every **1 hour** using the [HTTPS outcalls](https://internetcomputer.org/docs/current/references/https-outcalls-how-it-works/) and [Timers](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/advanced-features/periodic-tasks/) features.\n1. The mobile app generates a new session PK/SK pair;\n2. The mobile app requests an [`id_token`](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) from the authentication provider, setting the `nonce` claim to the session PK (encoded as a hex string);\n3. The authentication provider creates the new user or fetches the existing user on the off-chain backend/database, then mints a valid `id_token` that contains the `nonce` claim as requested;\n4. The mobile app sends an [update call](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/call/overview/#update-calls) to the `prepare_delegation` method of the canister, with the `id_token` as argument. This update call is signed with the session PK/SK pair;\n5. The canister performs the following operations:\n\n    a. Validates the `id_token` against the JWK and by verifies that:\n\n    - it was issued by the JWKs fetched from Auth0\n    - it is not expired (`exp` claim)\n    - it was not issued more than **10** minutes ago (`iat` claim)\n    - the issuer is the expected Auth0 tenant (`iss` claim)\n    - the audience is the expected Auth0 application id (`aud` claim)\n    - the session [self-authenticating principal](https://internetcomputer.org/docs/current/references/ic-interface-spec/#id-classes) derived from the session PK is equal to the caller (`nonce` claim)\n\n    b. Extracts the `sub` and `nonce` claims from the `id_token`\n\n    c. Hashes the `sub` and `nonce` claims together with a random `salt`. The DER-encoding of this hash is the `user_key`\n\n    d. Creates a canister signature for the `user_key` and stores it in the `delegation` map\n\n    e. Derives a self-authenticating principal from the `user_key`. This is the principal with which the mobile app will authenticate to the canister and will be the same across sessions and canister upgrades.\n\n    f. Assigns the principal to the `sub` claim in the `users` map. This map is used to retrieve the user `sub` claim in all the methods that the user will use after completing the authentication flow, see **step 7.**\n\n    If all these steps succeed, the canister returns the `user_key` and the expiration of the delegation, which is set to the `exp` claim of the `id_token`.\n\n6. The mobile app sends a [query call](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/call/overview/#query-calls) to the `get_delegation` method of the canister, with the `id_token` and `expiration` as arguments. This query call is signed with the session PK/SK pair.\n\n    This method performs the same validation on the `id_token` as in the previous step and returns the delegation along with the canister signature.\n\n7. The mobile app can now create a delegated identity, with which it can send subsequent requests to the canister, for example to the `authenticated` method.\n\n    The `authenticated` method is just a demo method to show that the user is authenticated with the delegation obtained from the canister and its `sub` claim can be retrieved from the `users` map.\n\n8. The mobile app can also send authenticated requests to the off-chain backend, which can identify the user by the `sub` claim as well.\n\nThe [canister_sig_util](https://github.com/dfinity/internet-identity/tree/release-2024-03-22/src/canister_sig_util) crate from the Internet Identity source code is used as an helper for the signatures map.\n\n## Roadmap\n\n- [x] On the canister, periodically fetch the [JSON Web Key Sets (JWKS)](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets) from Auth0 using the [HTTPS outcalls](https://internetcomputer.org/docs/current/references/https-outcalls-how-it-works/) and [Timers](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/advanced-features/periodic-tasks/) features.\n\n    Right now, the JWKS are fetched at build time by the [build-canister.sh](./scripts/build-canister.sh) script, stored in `data/jwks.json` and imported in the canister as raw bytes at compile time ([source](https://github.com/ilbertt/ic-react-native-jwt-auth/blob/882539addd4e0e35fe1f1756701296f1ff085239/src/ic_backend/src/id_token.rs#L12)).\n\n    Fetching the JWKS at runtime is needed because [JWK](https://datatracker.ietf.org/doc/html/rfc7517)s on Auth0 may rotate.\n\n    Related issue: https://github.com/ilbertt/ic-react-native-jwt-auth/issues/1.\n- [x] Integration tests\n\n    Related PRs:\n    - https://github.com/ilbertt/ic-react-native-jwt-auth/pull/2\n    - https://github.com/ilbertt/ic-react-native-jwt-auth/pull/3\n\n## License\n\nMIT License. See [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filbertt%2Fic-react-native-jwt-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filbertt%2Fic-react-native-jwt-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filbertt%2Fic-react-native-jwt-auth/lists"}