Skip to content

Commit

Permalink
Improve attribute sharing flow for development setups
Browse files Browse the repository at this point in the history
This PR improves the following aspects of the attribute sharing flow:
* For issuers hosted on `localhost` using the a canister id as a subdomain
  II will not make a network request to get the canister ID. Instead,
  the canister id will be parsed from the URL directly.
* If the issuer origin provided by the RP and the derivation origin
  returned by the issuer are the same, then II will not make a lookup
  to `/.well-known/ii-alternative-origins` as not alternative origin is
  being used.

Both of these changes together make it possible to integrate issuers with
II in a local dev setup, by consistently using a canisterId subdomain
of `localhost` as the issuer domain.
  • Loading branch information
Frederik Rothenberger committed Mar 14, 2024
1 parent 6c34591 commit 6ac50b9
Showing 1 changed file with 44 additions and 11 deletions.
55 changes: 44 additions & 11 deletions src/frontend/src/flows/verifiableCredentials/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CredentialSpec,
IssuedCredentialData,
} from "@dfinity/internet-identity-vc-api";
import { Principal } from "@dfinity/principal";
import { isNullish, nonNullish } from "@dfinity/utils";
import { abortedCredentials } from "./abortedCredentials";
import { allowCredentials } from "./allowCredentials";
Expand Down Expand Up @@ -76,7 +77,7 @@ const verifyCredentials = async ({
}: { connection: Connection } & VerifyCredentialsArgs) => {
// Look up the canister ID from the origin
const lookedUp = await withLoader(() =>
lookupCanister({ origin: issuerOrigin })
resolveIssuerCanisterId({ origin: issuerOrigin })
);
if (lookedUp === "not_found") {
return abortedCredentials({ reason: "no_canister_id" });
Expand Down Expand Up @@ -222,6 +223,31 @@ const verifyCredentials = async ({
});
};

const resolveIssuerCanisterId = ({
origin,
}: {
origin: string;
}): Promise<{ ok: string } | "not_found"> => {
const url = new URL(origin);

// special handling for issuers running on localhost
if (url.hostname.endsWith("localhost")) {
// This is a local development environment, infer the canister ID from the hostname directly
// (e.g. http://bd3sg-teaaa-aaaaa-qaaba-cai.localhost:4943 -> bd3sg-teaaa-aaaaa-qaaba-cai)
const canisterId = url.hostname.split(".")[0];
try {
Principal.fromText(canisterId); // make sure the inferred part is actually a canister id, throws if not
return Promise.resolve({ ok: canisterId });
} catch (e) {
console.warn(
`Unable to infer issuer canister id from origin ${origin}: ${e}`
);
}
}

return lookupCanister({ origin });
};

// Lookup the canister by performing a request to the origin and check
// if the server (probably BN) set a header to inform us of the canister ID
const lookupCanister = async ({
Expand Down Expand Up @@ -334,28 +360,35 @@ const getValidatedIssuerDerivationOrigin = async ({
}
| { kind: "ok"; origin: string }
> => {
const result = await vcIssuer.getDerivationOrigin({ origin: issuerOrigin });
const derivationOriginResult = await vcIssuer.getDerivationOrigin({
origin: issuerOrigin,
});

if (result.kind === "error") {
if (derivationOriginResult.kind === "error") {
return { kind: "error", err: "derivation_origin_issuer_error" };
}

result.kind satisfies "origin";
derivationOriginResult.kind satisfies "origin";

// If the derivation origin is the same as the issuer origin, we do not need
// to check the /.well-known/ii-alternative-origins file
if (derivationOriginResult.origin === issuerOrigin) {
return { kind: "ok", origin: issuerOrigin };
}

const validDerivationOrigin = await validateDerivationOrigin(
const validationResult = await validateDerivationOrigin(
issuerOrigin,
result.origin
derivationOriginResult.origin
);
if (validDerivationOrigin.result === "invalid") {
if (validationResult.result === "invalid") {
console.error(
"Invalid derivation origin for issuer",
JSON.stringify(validDerivationOrigin)
`Invalid derivation origin ${derivationOriginResult.origin} for issuer ${issuerOrigin}: ${validationResult.message}`
);
return { kind: "error", err: "invalid_derivation_origin_issuer" };
}
validDerivationOrigin.result satisfies "valid";
validationResult.result satisfies "valid";

return { kind: "ok", origin: result.origin };
return { kind: "ok", origin: derivationOriginResult.origin };
};

// Contact the issuer to issue the credentials
Expand Down

0 comments on commit 6ac50b9

Please sign in to comment.