From 71581fc0fd0abe3bdb6ba783750a2b4ddc3d2a97 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 16 Nov 2023 12:20:52 +0100 Subject: [PATCH] Simplify and generalize fetchDelegation (#2049) * Simplify and generalize fetchDelegation This updates `fetchDelegation` to make the implementation a bit simpler and to generalize it beyond the postMessage flow: * Unused parameter `userNumber` is dropped * Everything related to postMessage (AuthContext) is generalized into actual derivation, ttl, publicKey, etc * The return type is typed a bit more strongly (although has to be coerced) * Clean up --- .../src/flows/authorize/fetchDelegation.ts | 50 +++++++++---------- .../flows/authorize/postMessageInterface.ts | 18 ++++--- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/frontend/src/flows/authorize/fetchDelegation.ts b/src/frontend/src/flows/authorize/fetchDelegation.ts index 7bda002da8..9cf9725536 100644 --- a/src/frontend/src/flows/authorize/fetchDelegation.ts +++ b/src/frontend/src/flows/authorize/fetchDelegation.ts @@ -5,29 +5,29 @@ import { import { toast } from "$src/components/toast"; import { AuthenticatedConnection } from "$src/utils/iiConnection"; import { unknownToString } from "$src/utils/utils"; +import { Signature } from "@dfinity/agent"; import { nonNullish } from "@dfinity/utils"; -import { AuthContext, Delegation } from "./postMessageInterface"; +import { Delegation } from "./postMessageInterface"; /** - * Prepares and fetches a delegation valid for the authenticated user and the application information contained in - * authContext. - * @param userNumber User number resulting from successful authentication. + * Prepares and fetches a delegation valid for the authenticated user and the derivation. * @param connection authenticated II connection resulting from successful authentication. - * @param authContext Information about the authentication request received from the application via window post message. + * @param derivationOrigin the origin for which to create the delegation + * @param publicKey the key to delegate to + * @param maxTimeToLive until when the delegation is valid (nanoseconds since now) * @return Tuple of PublicKey and matching delegation. */ -export const fetchDelegation = async ( - userNumber: bigint, - connection: AuthenticatedConnection, - authContext: AuthContext -): Promise<[PublicKey, Delegation] | { error: unknown }> => { - const sessionKey = Array.from(authContext.authRequest.sessionPublicKey); - - // at this point, derivationOrigin is either validated or undefined - let derivationOrigin = nonNullish(authContext.authRequest.derivationOrigin) - ? authContext.authRequest.derivationOrigin - : authContext.requestOrigin; - +export const fetchDelegation = async ({ + connection, + derivationOrigin, + publicKey, + maxTimeToLive, +}: { + connection: AuthenticatedConnection; + derivationOrigin: string; + publicKey: Uint8Array; + maxTimeToLive?: bigint; +}): Promise<[PublicKey, Delegation] | { error: unknown }> => { // In order to give dapps a stable principal regardless whether they use the legacy (ic0.app) or the new domain (icp0.io) // we map back the derivation origin to the ic0.app domain. const ORIGIN_MAPPING_REGEX = @@ -40,8 +40,8 @@ export const fetchDelegation = async ( const result = await connection.prepareDelegation( derivationOrigin, - sessionKey, - authContext.authRequest.maxTimeToLive + publicKey, + maxTimeToLive ); if ("error" in result) { @@ -52,9 +52,8 @@ export const fetchDelegation = async ( const signed_delegation = await retryGetDelegation( connection, - userNumber, derivationOrigin, - sessionKey, + publicKey, timestamp ); @@ -67,16 +66,17 @@ export const fetchDelegation = async ( expiration: BigInt(signed_delegation.delegation.expiration), targets: undefined, }, - signature: Uint8Array.from(signed_delegation.signature), + signature: Uint8Array.from( + signed_delegation.signature + ) as unknown as Signature, }, ]; }; const retryGetDelegation = async ( connection: AuthenticatedConnection, - userNumber: bigint, hostname: string, - sessionKey: PublicKey, + publicKey: PublicKey, timestamp: bigint, maxRetries = 5 ): Promise => { @@ -85,7 +85,7 @@ const retryGetDelegation = async ( await new Promise((resolve) => { setInterval(resolve, 1000 * i); }); - const res = await connection.getDelegation(hostname, sessionKey, timestamp); + const res = await connection.getDelegation(hostname, publicKey, timestamp); if ("no_such_delegation" in res) { continue; } diff --git a/src/frontend/src/flows/authorize/postMessageInterface.ts b/src/frontend/src/flows/authorize/postMessageInterface.ts index e13ff4a0e2..91d5a405b5 100644 --- a/src/frontend/src/flows/authorize/postMessageInterface.ts +++ b/src/frontend/src/flows/authorize/postMessageInterface.ts @@ -2,6 +2,7 @@ // applications that want to authenticate the user using Internet Identity import { LoginData } from "$src/utils/flowResult"; import { unknownToRecord } from "$src/utils/utils"; +import { Signature } from "@dfinity/agent"; import { Principal } from "@dfinity/principal"; import { isNullish } from "@dfinity/utils"; import { fetchDelegation } from "./fetchDelegation"; @@ -13,7 +14,7 @@ export interface Delegation { expiration: bigint; targets?: Principal[]; }; - signature: Uint8Array; + signature: Signature; } /** @@ -154,11 +155,16 @@ export async function authenticationProtocol({ onProgress("fetching delegation"); - const result = await fetchDelegation( - authSuccess.userNumber, - authSuccess.connection, - authContext - ); + // at this point, derivationOrigin is either validated or undefined + const derivationOrigin = + authContext.authRequest.derivationOrigin ?? authContext.requestOrigin; + + const result = await fetchDelegation({ + connection: authSuccess.connection, + derivationOrigin, + publicKey: authContext.authRequest.sessionPublicKey, + maxTimeToLive: authContext.authRequest.maxTimeToLive, + }); if ("error" in result) { window.opener.postMessage(