React Native (Expo) + JWT Authentication + Rust ICP canister

Overview

React Native (Expo) app with JWT Authentication and IC Rust canister

This is a Proof-of-Concept React Native Expo mobile application that uses JSON Web Tokens to authenticate to an Internet Computer canister.

The main idea behind this PoC is to have an off-chain OpenID authentication service (Auth0 in this case) that mints a JWT that the user can send to the canister to generate a delegated identity. This way, the user can identify themselves on the canister with the same principal across sessions. It offers a trade-off between ease of use for the end user and decentralization of the authentication process.

This PoC has the following components:

Head over to the How it works section for more details.

Requirements

Configure Auth0

Follow these steps to configure Auth0:

  1. Create a Tenant and get your Auth0 Tenant domain, which looks like <TENANT_NAME>.<TENANT_REGION>.auth0.com

  2. Create a Native Application

  3. In the Dashboard > Applications > YOUR_APP > Settings tab, set the Allowed Callback URLs and Allowed Logout URLs to:

    • io.icp0.jwtauthdemo.auth0://<YOUR_AUTH0_TENANT_DOMAIN>/ios/io.icp0.jwtauthdemo/callback
    • io.icp0.jwtauthdemo.auth0://<YOUR_AUTH0_TENANT_DOMAIN>/android/io.icp0.jwtauthdemo/callback

    Where <YOUR_AUTH0_TENANT_DOMAIN> 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 file.

  4. In the Dashboard > Applications > YOUR_APP > Credentials tab, set the Authentication Method to None (instead of Client Secret (Post))

The 1st step of the Auth0 React Native Quickstart interactive guide can be helpful too.

Usage

Install the dependencies:

bun install

Copy the .env.example file to .env:

cp .env.example .env

and replace the values with your own.

Start the IC backend:

# in terminal 1
bun start:dfx
# in terminal 2
bun deploy:ic_backend

Start the off-chain backend:

# in terminal 3
bun start:app_backend

Start the mobile app:

# in terminal 4
cd src/app
cp ../../.env .env
bun expo prebuild
cd ../..
# if you want to start the app for Android
bun start:android
# if you want to start the app for iOS
bun start:ios

You may need to manually start the Android/iOS emulator.

See the expo start CLI docs for more information.

Testing

Unit tests are available for the IC Rust backend canister. Simply run:

./scripts/unit-test.sh

Integration tests are available for the IC Rust backend canister. Simply run:

./scripts/integration-test.sh

How it works

This PoC is highly inspired by this discussion on the Internet Computer forum.

sequenceDiagram
    participant M as Mobile app
    participant A as Authentication Provider
    participant C as Canister
    participant B as Off-chain Backend

    note over A,B: JWK data is shared
    note over M: Generates session PK/SK
    M->>+A: Request id_token with PK
    A->>B: Request new/existing user
    B->>A: User confirmed
    A->>-M: Valid id_token(PK)
    M->>+C: Request prepare_delegation with id_token(PK) signed with SK
    note over C: Validate id_token against JWK
    C-->>C: Extract sub claim from id_token
    C-->>C: Create a canister signature and store in delegation map
    C-->>C: Derive principal from canister signature
    C-->>C: Assign principal to sub in users map
    C->>-M: user_key, expiration
    M->>+C: Request get_delegation with id_token(PK), expiration signed with SK
    note over C: Validate id_token against JWK
    C-->>C: Read delegation from delegation map
    C->>-M: Signed Delegation
    note over M: Session PK/SK is now delegated
    note over M: User is authenticated
    M-->>C: Request authenticated with delegation signed with SK
    note over C: User identified by delegation principal
    C-->>C: Read sub from users map
    C-->>M: Confirm authentication
    M-->>B: Generic request with ic_token
    note over B: User identified by sub claim
    B-->>M: ...

The main steps are:

  1. The JWKs must be fetched from Auth0 and stored in the canister and off-chain backend.

    In the current implementation, the canister fetches them once on deployment and every 1 hour using the HTTPS outcalls and Timers features.

  2. The mobile app generates a new session PK/SK pair;

  3. The mobile app requests an id_token from the authentication provider, setting the nonce claim to the session PK (encoded as a hex string);

  4. 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;

  5. The mobile app sends an update call 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;

  6. The canister performs the following operations:

    a. Validates the id_token against the JWK and by verifies that:

    • it was issued by the JWKs fetched from Auth0
    • it is not expired (exp claim)
    • it was not issued more than 10 minutes ago (iat claim)
    • the issuer is the expected Auth0 tenant (iss claim)
    • the audience is the expected Auth0 application id (aud claim)
    • the session self-authenticating principal derived from the session PK is equal to the caller (nonce claim)

    b. Extracts the sub and nonce claims from the id_token

    c. Hashes the sub and nonce claims together with a random salt. The DER-encoding of this hash is the user_key

    d. Creates a canister signature for the user_key and stores it in the delegation map

    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.

    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.

    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.

  7. The mobile app sends a query call 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.

    This method performs the same validation on the id_token as in the previous step and returns the delegation along with the canister signature.

  8. 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.

    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.

  9. The mobile app can also send authenticated requests to the off-chain backend, which can identify the user by the sub claim as well.

The canister_sig_util crate from the Internet Identity source code is used as an helper for the signatures map.

Roadmap

  • On the canister, periodically fetch the JSON Web Key Sets (JWKS) from Auth0 using the HTTPS outcalls and Timers features.

    Right now, the JWKS are fetched at build time by the build-canister.sh script, stored in data/jwks.json and imported in the canister as raw bytes at compile time (source).

    Fetching the JWKS at runtime is needed because JWKs on Auth0 may rotate.

    Related issue: #1.

  • Integration tests

    Related PRs:

License

MIT License. See LICENSE.

You might also like...
Use Touch ID / Secure Enclave for SSH Authentication!
Use Touch ID / Secure Enclave for SSH Authentication!

SeKey About SeKey is a SSH Agent that allow users to authenticate to UNIX/Linux SSH servers using the Secure Enclave How it Works? The Secure Enclave

End-to-end encryption and mutual authentication for distributed applications.
End-to-end encryption and mutual authentication for distributed applications.

✨ Hands-on Introduction: Build end-to-end encrypted, mutually-authenticated, secure messaging in Rust ✨ Rust and Elixir libraries for end-to-end encry

LibreAuth is a collection of tools for user authentication.

LibreAuth is a collection of tools for user authentication. Features Password / passphrase authentication no character-set limitation reason

Rust-native building blocks for the Cardano blockchain ecosystem

Pallas Rust-native building blocks for the Cardano blockchain ecosystem. Introduction Pallas is an expanding collection of modules that re-implements

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native

🖌 egui: an easy-to-use GUI in pure Rust 👉 Click to run the web demo 👈 egui is a simple, fast, and highly portable immediate mode GUI library for Ru

USN - the first NEAR-native stablecoin

USN USN is a NEAR-native USD stable coin. The contract implements fungible token API according to the following standards: NEP-141 (ERC-20 fashioned)

Utilities for working with native solc and compiling projects.

foundry-compilers Utilities for working with native solc and compiling projects. To also compile contracts during cargo build (so that ethers abigen!

A Bitcoin Native Smart Contract & Finance Layer
A Bitcoin Native Smart Contract & Finance Layer

What's bitcoinOS bitcoinOS A Bitcoin Native Smart Contract & Finance Layer. Bitcoin Wallet as a Smart Contract The first Turing-complete native smart

Package used by the cosmos-rust-interface. Makes direct use of cosmos-rust.

Package used by the cosmos-rust-interface. Makes direct use of cosmos-rust (cosmos‑sdk‑proto, osmosis-proto, cosmrs).

Comments
Owner
Luca Bertelli
The people who are crazy enough to think they can change the world... are the ones who do
Luca Bertelli
Simple PoC to issue JSON Web Tokens (JWTs) with a canister on the Internet Computer.

JWT Issuer Proof of Concept Overview Simple PoC to issue JSON Web Tokens (JWTs) with a canister on the Internet Computer. It allows the issuance of tw

Dominic Wörner 7 Oct 13, 2022
Designed for rapid iteration in IC Canister development projects.

IC_Utils Designed for rapid iteration in IC Canister development projects. IcContext IcContext provides the context for ic canister object invocation,

AlphaQ 83 Nov 13, 2023
rust-native-tls — Bindings for native TLS libraries

rust-native-tls Documentation An abstraction over platform-specific TLS implementations. Specifically, this crate uses SChannel on Windows (via the sc

Steven Fackler 371 Jan 8, 2023
NFT & Marketplace Contracts with royalties and fungible token support. Sample React app included.

NFT Market Reference Implementation A PoC backbone for NFT Marketplaces on NEAR Protocol. Reference Changelog Changelog Progress: basic purchase of NF

NEAR App Examples 156 Apr 28, 2022
Solana NFT marketplace boilerplate with anchor & react & candy-machine-cli

The Heros NFT Marketplace Boilerplate project is designed to let users fork, customize, and deploy their own nft marketplace app to a custom domain, ultra fast.

null 30 Dec 15, 2022
A tutorial for an NFT Market Place Built with Near Protocol and React.js

nft-marketplace-part-1 A tutorial for an NFT Market Place built using Near Protocol and React.js. Preview To run this app locally, follow below steps:

Kohwo Orien 5 Jun 29, 2022
Solana NFT marketplace boilerplate with anchor & react & candy-machine-cli

Solana_Marketplace_HerosNFT The Heros NFT Marketplace Boilerplate project is designed to let users fork, customize, and deploy their own nft marketpla

null 30 Dec 15, 2022
CLI tool for managing your 2FA authentication codes written in pure Rust.

(O)TP (VA)ULT - ova. ova is a simple CLI tool which lets you manage your TOTPs, or basically lets you get your two-way authentication code straight to

Giorgi Anakidze 3 Apr 28, 2023
gRPC client/server for zero-knowledge proof authentication Chaum Pederson Zero-Knowledge Proof in Rust

gRPC client/server for zero-knowledge proof authentication Chaum Pederson Zero-Knowledge Proof in Rust. Chaum Pederson is a zero-knowledge proof proto

Advaita Saha 4 Jun 12, 2023
A safe implementation of the secure remote password authentication and key-exchange protocol (SRP), SRP6a and legacy are as features available.

Secure Remote Password (SRP 6 / 6a) A safe implementation of the secure remote password authentication and key-exchange protocol (SRP version 6a). Ver

Sven Assmann 10 Nov 3, 2022