Introduction
When a client wants to authenticate a user, it uses a session key (e.g., Ed25519 or ECDSA) and, through the authentication flow (details below), obtains a delegation chain (opens in a new tab) that allows the session key to sign for the user's main identity.
There are two types of session keys - one for an external application (client delegation) to manage a scoped set of user permissions, and one for the main client (NFID delegation) to give users full administrative control of their identity.
Non-ICP networks, like EVM, only need a provider
.
Client Delegations
The delegation chain consists of one delegation, called the client delegation, that delegates from the user identity to the session key. This delegation is created by the NFID Delegator smart contract and signed using a smart contract (canister) signature (opens in a new tab). This delegation is scoped to the client's canisters and has a maximum lifetime of 30 days, with a default of 30 minutes.
NFID Delegation
The NFID client also manages an NFID delegation, delegating from the passkey's public key to a session key managed by this frontend, so that it can interact with its smart contracts without having to invoke the passkey for each signature.
Non-ICP Networks (i.e., EVM chains)
When non-ICP clients want to authenticate a user, it receives a provider
, constructed on the
frontend by the NFID delegation.
From the Point of View of a Client ICP Application:
-
The application frontend creates a session key pair (e.g., Ed25519).
-
It installs a
message
event handler on its ownwindow
. -
It loads the URL
https://nfid.one/#authorize
in aniframe
. LetnfidWindow
be theWindow
object returned from this. -
In the
nfidWindow
, the user logs in, and thenfidWindow
invokeswindow.opener.postMessage(msg, "*")
where
msg
isinterface NFIDReady { kind: "authorize-ready"; }
-
The client application, after receiving the
NFIDReady
message, invokesnfidWindow.postMessage(msg, "https://nfid.one")
where
msg
is a value of typeinterface NFIDAuthRequest { kind: "authorize-client"; sessionPublicKey: Uint8Array; maxTimeToLive?: bigint; derivationOrigin?: string; }
- the
sessionPublicKey
contains the public key of the session key pair. - the
maxTimeToLive
, if present, indicates the desired time span (in nanoseconds) until the requested delegation should expire. - the
derivationOrigin
, if present, indicates an origin that should be used for principal derivation instead of the client origin. Values must match the following regular expression:^https:\/\/[\w-]+(\.raw)?\.(ic0\.app|icp0\.io)$
. NFID will only accept values that are also listed in the HTTP resourcehttps://<canister_id>.ic0.app/.well-known/ii-alternative-origins
of the corresponding canister (see Alternative Frontend Origins (opens in a new tab)).
- the
-
Now the client application window expects a message back, with data
event
. -
If
event.origin
is nothttps://nfid.one
, ignore this message. -
The
event.data
value is a JS object with the following type:interface NFIDAuthResponse { kind: "authorize-client-success"; delegations: [{ delegation: { pubkey: Uint8Array; expiration: bigint; targets?: Principal[]; }; signature: Uint8Array; }]; userPublicKey: Uint8Array; }
where the
userPublicKey
is the user's Identity anddelegations
corresponds to the CBOR-encoded delegation chain as used for authentication on the IC (opens in a new tab). -
It could also receive a failure message of the following type
interface NFIDAuthResponse { kind: "authorize-client-failure"; text: string; }
The client application frontend needs to be able to detect when any of the delegations in the chain has expired and re-authorize the user in that case.
The @nfid/embed is a helpful package for developers to quickly get this set up.
Find more at the source IC reference (opens in a new tab).