Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/frontend/src/lib/state/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { writable, type Writable } from "svelte/store";
import { FeatureFlag } from "$lib/utils/featureFlags";
import { isNullish, nonNullish } from "@dfinity/utils";
import { getPrimaryOrigin } from "$lib/globals";

declare global {
interface Window {
Expand Down Expand Up @@ -97,7 +98,20 @@ export const ENABLE_ALL_LOCALES = createFeatureFlagStore(
false,
);

export const GUIDED_UPGRADE = createFeatureFlagStore("GUIDED_UPGRADE", false);
export const GUIDED_UPGRADE = createFeatureFlagStore(
"GUIDED_UPGRADE",
false,
// Enable guide upgrade flow if page is visited from domain other than id.ai
(featureFlag) => {
const primaryOrigin = getPrimaryOrigin();
if (
primaryOrigin !== undefined &&
primaryOrigin !== window.location.origin
) {
featureFlag.temporaryOverride(true);
}
},
);

export default {
DOMAIN_COMPATIBILITY,
Expand Down
22 changes: 20 additions & 2 deletions src/frontend/src/lib/utils/discoverablePasskeyIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import borc from "borc";
import { isNullish, nonNullish } from "@dfinity/utils";
import { extractAAGUID } from "$lib/utils/webAuthn";
import { bufFromBufLike } from "$lib/utils/utils";
import { getPrimaryOrigin } from "$lib/globals";

/**
* From the documentation;
Expand Down Expand Up @@ -171,8 +172,9 @@ export class DiscoverablePasskeyIdentity extends SignIdentity {
}

static async createNew(name: string): Promise<DiscoverablePasskeyIdentity> {
const rpId = getRpId();
const identity = new DiscoverablePasskeyIdentity({
credentialCreationOptions: creationOptions(name),
credentialCreationOptions: creationOptions(name, rpId),
});
await identity.sign(Uint8Array.from("<ic0.app>", (c) => c.charCodeAt(0)));
return identity;
Expand All @@ -187,9 +189,10 @@ export class DiscoverablePasskeyIdentity extends SignIdentity {
result: PublicKeyCredentialWithAttachment,
) => Promise<CosePublicKey>;
}): DiscoverablePasskeyIdentity {
const rpId = getRpId();
return new DiscoverablePasskeyIdentity({
getPublicKey,
credentialRequestOptions: requestOptions(credentialIds),
credentialRequestOptions: requestOptions(credentialIds, rpId),
});
}

Expand Down Expand Up @@ -271,8 +274,20 @@ export class DiscoverablePasskeyIdentity extends SignIdentity {
}
}

// Discoverable passkeys should always be created for primary origin,
// mostly this is through an iframe, but it's unsupported in Safari.
//
// Therefore, we fall back to using related origin requests in Safari.
export const getRpId = () => {
const primaryOrigin = getPrimaryOrigin();
return primaryOrigin !== undefined && primaryOrigin !== window.location.origin
? new URL(primaryOrigin).hostname
: undefined;
};

export const creationOptions = (
name: string,
rpId?: string,
): CredentialCreationOptionsWithoutChallenge => ({
publicKey: {
// Identify the AAGUID of the passkey provider
Expand All @@ -299,6 +314,7 @@ export const creationOptions = (
// How II will identify itself
rp: {
name: "Internet Identity Service",
id: rpId,
},
// User id is set to a random value since it's not used by II,
// the passkey is created before the actual user is created.
Expand All @@ -312,8 +328,10 @@ export const creationOptions = (

export const requestOptions = (
credentialIds?: Uint8Array[],
rpId?: string,
): CredentialRequestOptionsWithoutChallenge => ({
publicKey: {
rpId,
// Either use the specified credential ids or let the user pick a passkey
allowCredentials:
nonNullish(credentialIds) && credentialIds.length > 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
const { children, data }: LayoutProps = $props();
const primaryOrigin = getPrimaryOrigin();
const isDesktopSafari =
/^((?!chrome|chromium|android|edg|opr|firefox).)*safari/i.test(
navigator.userAgent,
) && !/mobile/i.test(navigator.userAgent);
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const supportsIframeCredentialsCreate = !isIOS && !isDesktopSafari;
const shouldEmbed =
supportsIframeCredentialsCreate &&
data.legacyProtocol &&
primaryOrigin !== undefined &&
window.location.origin !== primaryOrigin;
Expand Down
Loading