Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry with differen rp id #2753

Merged
merged 2 commits into from
Dec 18, 2024
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
106 changes: 106 additions & 0 deletions src/frontend/src/utils/findWebAuthnRpId.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CredentialData } from "./credential-devices";
import {
BETA_DOMAINS,
PROD_DOMAINS,
excludeCredentialsFromOrigins,
findWebAuthnRpId,
} from "./findWebAuthnRpId";

Expand Down Expand Up @@ -165,3 +166,108 @@ describe("findWebAuthnRpId", () => {
);
});
});

describe("excludeCredentialsFromOrigins", () => {
const mockDeviceData = (origin?: string): CredentialData => ({
origin,
credentialId: new ArrayBuffer(1),
pubkey: new ArrayBuffer(1),
});

test("excludes credentials from specified origins", () => {
const credentials = [
mockDeviceData("https://identity.ic0.app"),
mockDeviceData("https://identity.internetcomputer.org"),
mockDeviceData("https://identity.icp0.io"),
];
const originsToExclude = new Set(["https://identity.ic0.app"]);
const currentOrigin = "https://identity.internetcomputer.org";

const result = excludeCredentialsFromOrigins(
credentials,
originsToExclude,
currentOrigin
);

expect(result).toHaveLength(2);
expect(result).toEqual([
mockDeviceData("https://identity.internetcomputer.org"),
mockDeviceData("https://identity.icp0.io"),
]);
});

test("treats undefined credential origins as DEFAULT_DOMAIN", () => {
const credentials = [
mockDeviceData(undefined), // Should be treated as DEFAULT_DOMAIN
mockDeviceData("https://identity.internetcomputer.org"),
];
const originsToExclude = new Set(["https://identity.ic0.app"]); // Should match DEFAULT_DOMAIN
const currentOrigin = "https://identity.internetcomputer.org";

const result = excludeCredentialsFromOrigins(
credentials,
originsToExclude,
currentOrigin
);

expect(result).toHaveLength(1);
expect(result).toEqual([
mockDeviceData("https://identity.internetcomputer.org"),
]);
});

test("treats undefined origins in exclusion set as currentOrigin", () => {
const credentials = [
mockDeviceData("https://identity.ic0.app"),
mockDeviceData("https://identity.internetcomputer.org"),
];
const originsToExclude = new Set([undefined]); // Should be treated as currentOrigin
const currentOrigin = "https://identity.internetcomputer.org";

const result = excludeCredentialsFromOrigins(
credentials,
originsToExclude,
currentOrigin
);

expect(result).toHaveLength(1);
expect(result).toEqual([mockDeviceData("https://identity.ic0.app")]);
});

test("returns empty array when all credentials are excluded", () => {
const credentials = [
mockDeviceData("https://identity.ic0.app"),
mockDeviceData("https://identity.internetcomputer.org"),
];
const originsToExclude = new Set([
"https://identity.ic0.app",
"https://identity.internetcomputer.org",
]);
const currentOrigin = "https://identity.ic0.app";

const result = excludeCredentialsFromOrigins(
credentials,
originsToExclude,
currentOrigin
);

expect(result).toHaveLength(0);
});

test("returns all credentials when no origins to exclude", () => {
const credentials = [
mockDeviceData("https://identity.ic0.app"),
mockDeviceData("https://identity.internetcomputer.org"),
];
const originsToExclude = new Set<string>();
const currentOrigin = "https://identity.ic0.app";

const result = excludeCredentialsFromOrigins(
credentials,
originsToExclude,
currentOrigin
);

expect(result).toEqual(credentials);
});
});
37 changes: 37 additions & 0 deletions src/frontend/src/utils/findWebAuthnRpId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,43 @@ export const relatedDomains = (): string[] => {
return [];
};

export const hasCredentialsFromMultipleOrigins = (
credentials: CredentialData[]
): boolean =>
new Set(credentials.map(({ origin }) => origin ?? DEFAULT_DOMAIN)).size > 1;

/**
* Filters out credentials from specific origins.
*
* This function takes a list of credentials and removes any that match the provided origins.
* If a credential has no origin (undefined), it is treated as if it had the `DEFAULT_DOMAIN`.
* Two origins match if they have the same hostname (domain).
*
* @param credentials - List of credential devices to filter
* @param origins - Set of origins to exclude (undefined values are treated as `currentOrigin`)
* @param currentOrigin - The current origin to use when comparing against undefined origins
* @returns Filtered list of credentials, excluding those from the specified origins
*/
export const excludeCredentialsFromOrigins = (
credentials: CredentialData[],
origins: Set<string | undefined>,
currentOrigin: string
): CredentialData[] => {
if (origins.size === 0) {
return credentials;
}
// Change `undefined` to the current origin.
const originsToExclude = Array.from(origins).map(
(origin) => origin ?? currentOrigin
);
return credentials.filter(
(credential) =>
originsToExclude.filter((originToExclude) =>
sameDomain(credential.origin ?? DEFAULT_DOMAIN, originToExclude)
).length === 0
);
};

const sameDomain = (url1: string, url2: string): boolean =>
new URL(url1).hostname === new URL(url2).hostname;

Expand Down
Loading
Loading