Skip to content

Commit

Permalink
Simplify and generalize fetchDelegation (#2049)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nmattia authored Nov 16, 2023
1 parent a4d6399 commit 71581fc
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 31 deletions.
50 changes: 25 additions & 25 deletions src/frontend/src/flows/authorize/fetchDelegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -40,8 +40,8 @@ export const fetchDelegation = async (

const result = await connection.prepareDelegation(
derivationOrigin,
sessionKey,
authContext.authRequest.maxTimeToLive
publicKey,
maxTimeToLive
);

if ("error" in result) {
Expand All @@ -52,9 +52,8 @@ export const fetchDelegation = async (

const signed_delegation = await retryGetDelegation(
connection,
userNumber,
derivationOrigin,
sessionKey,
publicKey,
timestamp
);

Expand All @@ -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<SignedDelegation> => {
Expand All @@ -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;
}
Expand Down
18 changes: 12 additions & 6 deletions src/frontend/src/flows/authorize/postMessageInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -13,7 +14,7 @@ export interface Delegation {
expiration: bigint;
targets?: Principal[];
};
signature: Uint8Array;
signature: Signature;
}

/**
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 71581fc

Please sign in to comment.