Skip to content

Commit

Permalink
Who knew that adding a few focus() calls actually is good enough fo…
Browse files Browse the repository at this point in the history
…r safari with WebAuthn.
  • Loading branch information
sea-snake committed Jan 30, 2025
1 parent 1983e64 commit 59ba3ab
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/frontend/src/featureFlags/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Feature flags with default values
const FEATURE_FLAGS_WITH_DEFAULTS = {
DOMAIN_COMPATIBILITY: false,
DOMAIN_COMPATIBILITY: true, // Just enable it, keep forgetting to do so makes me think I made a mistake and debug unnecessarily
OPENID_AUTHENTICATION: false,
HARDWARE_KEY_TEST: false,
} as const satisfies Record<string, boolean>;
Expand Down
52 changes: 45 additions & 7 deletions src/frontend/src/flows/iframeWebAuthn.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isNullish } from "@dfinity/utils";
import { isNullish, nonNullish } from "@dfinity/utils";

export const WEBAUTHN_IFRAME_PATH = "#iframe/webauthn";

export const webAuthnInIframeFlow = (): Promise<never> => {
console.log("iframe is listening!");
window.addEventListener("message", async (event) => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler Medium

Postmessage handler has no origin check.
// event.source?.postMessage("why does it not work?");
// window.parent.postMessage("why does it not work?", "*");
console.log("Received create credential options", event.data);
window.focus();
const credential = (await navigator.credentials.get(
event.data
)) as PublicKeyCredential;
Expand Down Expand Up @@ -53,19 +54,22 @@ export const webAuthnInIframe = (
options: CredentialRequestOptions
): Promise<Credential> => {
const { rpId, ...publicKey } = options.publicKey ?? {};
console.log("iframe :D");
if (isNullish(rpId)) {
throw new Error("RP id is missing");
}
const callbackPromise = new Promise<Credential>((resolve) => {
const call = () =>
hiddenIframe.contentWindow?.postMessage(
const call = () => {
hiddenIframe.focus();
return hiddenIframe.contentWindow?.postMessage(
{
...options,
publicKey,
},
`https://${rpId}`
"*"
);
setTimeout(call, 2000);
};
setTimeout(call, 3000);
const listener = (event: MessageEvent) => {
console.log("event pre-check", event);
if (
Expand Down Expand Up @@ -103,3 +107,37 @@ export const webAuthnInIframe = (
hiddenIframe.src = `https://${rpId}${WEBAUTHN_IFRAME_PATH}`;
return callbackPromise;
};

interface WebAuthnConditions {
isMobile: boolean;
extensionLoaded: boolean;
rorSupported: boolean;
}

interface WebAuthnAttempt {
iframe: boolean;
rpId?: string;
}

const createConditions = (): WebAuthnConditions => ({
isMobile: false,
extensionLoaded: false,
rorSupported: true,
});

const createWebAuthnAttempts = (

Check warning on line 128 in src/frontend/src/flows/iframeWebAuthn.ts

View workflow job for this annotation

GitHub Actions / frontend-checks

'createWebAuthnAttempts' is assigned a value but never used. Allowed unused vars must match /^_/u
currentOrigin: string,
rpIds: string[],
conditions: WebAuthnConditions = createConditions()
): WebAuthnAttempt[] =>
rpIds
.map((rpId) =>
new URL(currentOrigin).hostname !== rpId ? rpId : undefined
)
.flatMap((rpId) => [
conditions.extensionLoaded && nonNullish(rpId)
? { iframe: true, rpId }
: undefined,
conditions.rorSupported ? { iframe: false, rpId } : undefined,
])
.filter(nonNullish);
11 changes: 4 additions & 7 deletions src/frontend/src/utils/iiConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ import {
IdentityMetadata,
IdentityMetadataRepository,
} from "$src/repositories/identityMetadata";
import {
addAnchorCancelledRpId,
cleanUpRpIdMapper,
getCancelledRpIds,
} from "$src/storage";
import { addAnchorCancelledRpId, getCancelledRpIds } from "$src/storage";
import {
CanisterError,
diagnosticInfo,
Expand Down Expand Up @@ -458,8 +454,8 @@ export class Connection {
// cancelledRpIds = new Set<string | undefined>();
// filteredCredentials = credentials;
const rpId = dynamicRPIdEnabled
? findWebAuthnRpId(currentOrigin, filteredCredentials, relatedDomains())
: undefined;
? findWebAuthnRpId(currentOrigin, filteredCredentials, relatedDomains())
: undefined;

console.log("rpId", rpId);

Expand Down Expand Up @@ -1030,6 +1026,7 @@ export const creationOptions = (
return {
authenticatorSelection: {
userVerification: "preferred",
residentKey: "preferred",
authenticatorAttachment,
},
excludeCredentials: exclude.flatMap((device) =>
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/src/utils/multiWebAuthnIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ export class MultiWebAuthnIdentity extends SignIdentity {
return this._actualIdentity.sign(blob);
}

console.log("this.rpId", this.rpId);
console.log(
"this.rpId",
this.rpId,
isNullish(this.rpId),
window.location.origin === this.rpId
);
const credentialsGet =
isNullish(this.rpId) || window.location.origin === this.rpId
? (options: CredentialRequestOptions) =>
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/src/utils/webAuthnIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ async function _createCredential(
challenge: _createChallengeBuffer(),
pubKeyCredParams: [
{ type: "public-key", alg: PubKeyCoseAlgo.ECDSA_WITH_SHA256 },
{ type: "public-key", alg: PubKeyCoseAlgo.RSA_WITH_SHA256 },
],
rp: {
name: "Internet Identity Service",
Expand Down Expand Up @@ -141,6 +142,7 @@ async function _createCredential(
// list of these algorithms. We only list the ones we support here.
enum PubKeyCoseAlgo {
ECDSA_WITH_SHA256 = -7,
RSA_WITH_SHA256 = -257,
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/internet_identity/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub fn security_headers(integrity_hashes: Vec<String>) -> Vec<HeaderField> {
navigation-override=(),\
payment=(),\
picture-in-picture=(),\
publickey-credentials-get=(self),\
publickey-credentials-get=*,\
screen-wake-lock=(),\
serial=(),\
speaker-selection=(),\
Expand Down

0 comments on commit 59ba3ab

Please sign in to comment.