Skip to content

Commit c1d3c7d

Browse files
atergaclaudesea-snakegithub-actions[bot]
authored
feat: Remove frontend assets from the II backend canister and simplify canister initialization in testnets (#3693)
## Summary Now that the frontend is served by the standalone `internet_identity_frontend` canister, this PR removes the frontend assets that were still bundled in the backend canister. - **Backend no longer serves frontend assets** — removed `fixup_html`, `ASSET_DIR`, `collect_assets`, and `/.well-known/webauthn` from the backend; `get_static_assets` now only serves `/.config.did.bin` (synchronized config) and `/.well-known/ic-domains` (custom domain support for `backend.id.ai`) - **Removed `fetch_root_key` from backend** — this config is now only on the frontend canister - **Frontend canister HTTP integration tests** — moved asset-serving, certification, well-known endpoint, and caching tests from the backend crate to `src/internet_identity_frontend/tests/integration/http.rs`; backend HTTP tests now only cover `/.config.did.bin`, `/.well-known/ic-domains`, and metrics - **Build & CI updates** — build script split into separate frontend/backend configs; CI downloads `internet_identity_frontend.wasm.gz` for PocketIC tests; e2e matrix simplified (removed `single`/`split` canister axis, frontend canister is now always deployed) - **Vite plugin cleanup** — removed utilities for the old bundled approach; HTML body tags only inserted during dev builds - **Test fixes** — removed `fetch_root_key` integration tests, defaulted captcha config to `CaptchaDisabled`, frontend-specific security header verification ## Test plan - [x] Backend HTTP tests pass (`/.config.did.bin`, `/.well-known/ic-domains`, metrics) - [x] Frontend HTTP tests pass (asset serving, certification, `/.well-known/webauthn`, `/.well-known/ic-domains`, caching) - [x] E2E tests pass with standalone frontend canister - [x] Clippy and formatting checks pass - [x] TypeScript tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: sea-snake <sea-snake@outlook.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 8bf90b3 commit c1d3c7d

File tree

51 files changed

+696
-1056
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+696
-1056
lines changed

.github/workflows/canister-tests.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ jobs:
375375

376376
canister-tests-run:
377377
runs-on: ${{ matrix.os }}
378-
needs: [canister-tests-build, cached-build, docker-build-archive]
378+
needs: [canister-tests-build, cached-build, docker-build-archive, docker-build-internet_identity_frontend]
379379
strategy:
380380
matrix:
381381
os: [ubuntu-latest, macos-latest]
@@ -410,6 +410,12 @@ jobs:
410410
name: archive.wasm.gz
411411
path: .
412412

413+
- name: "Download II frontend wasm"
414+
uses: actions/download-artifact@v4
415+
with:
416+
name: internet_identity_frontend.wasm.gz
417+
path: .
418+
413419
- name: Run PocketIc
414420
run: |
415421
"${POCKET_IC_BIN}" --port-file pocket-ic-port &
@@ -488,15 +494,14 @@ jobs:
488494
needs: [cached-build, test-app-build]
489495
strategy:
490496
matrix:
491-
canister: ["single", "split"] # single = the regular setup with one canister, split = the setup with a separate frontend canister
492497
device: ["desktop", "mobile"]
493498
shard: ["1_6", "2_6", "3_6", "4_6", "5_6", "6_6"]
494499
# Make sure that one failing test does not cancel all other matrix jobs
495500
fail-fast: false
496501

497502
env:
498503
# Suffix used for tagging artifacts
499-
artifact_suffix: next-${{ matrix.canister }}-${{ matrix.device }}-${{ matrix.shard }}
504+
artifact_suffix: next-${{ matrix.device }}-${{ matrix.shard }}
500505
# OpenID provider instance ports (see /src/test_openid_provider)
501506
openid_providers: "11105 11106"
502507
steps:
@@ -567,16 +572,14 @@ jobs:
567572
run: |
568573
# NOTE: dfx install will run the postinstall scripts from dfx.json
569574
dfx canister install internet_identity --wasm internet_identity_test.wasm.gz --argument "(opt record { captcha_config = opt record { max_unsolved_captchas= 50:nat64; captcha_trigger = variant {Static = variant { CaptchaDisabled }}}; related_origins = opt vec { \"https://id.ai\"; \"https://identity.ic0.app\"; \"https://identity.internetcomputer.org\" }; new_flow_origins = opt vec { \"https://id.ai\" }; dummy_auth = opt opt record { prompt_for_index = true }; openid_configs = opt vec { ${{ steps.openid-configs.outputs.OPENID_CONFIGS }} } })"
570-
if [ "${{ matrix.canister == 'split' }}" = "true" ]; then
571-
II_CANISTER_ID=$(dfx canister id internet_identity)
572-
dfx canister install internet_identity_frontend --wasm internet_identity_frontend_test.wasm.gz --argument "(record { backend_canister_id = principal \"$II_CANISTER_ID\"; backend_origin = \"https://backend.id.ai\"; related_origins = opt vec { \"https://id.ai\"; \"https://identity.ic0.app\"; \"https://identity.internetcomputer.org\" }; dummy_auth = opt opt record { prompt_for_index = true } })"
573-
fi
575+
II_CANISTER_ID=$(dfx canister id internet_identity)
576+
dfx canister install internet_identity_frontend --wasm internet_identity_frontend_test.wasm.gz --argument "(record { backend_canister_id = principal \"$II_CANISTER_ID\"; backend_origin = \"https://backend.id.ai\"; related_origins = opt vec { \"https://id.ai\"; \"https://identity.ic0.app\"; \"https://identity.internetcomputer.org\" }; dummy_auth = opt opt record { prompt_for_index = true }; fetch_root_key = opt true })"
574577
dfx canister install test_app --wasm demos/test-app/test_app.wasm
575578
576579
- name: Run dev server
577580
id: dev-server-start
578581
run: |
579-
SEPARATE_FRONTEND_CANISTER=${{ matrix.canister == 'split' && 1 || 0 }} TLS_DEV_SERVER=1 NO_HOT_RELOAD=1 npm run dev | tee -a > dev-server-logs.txt &
582+
SEPARATE_FRONTEND_CANISTER=1 TLS_DEV_SERVER=1 NO_HOT_RELOAD=1 npm run dev | tee -a > dev-server-logs.txt &
580583
dev_server_pid=$!
581584
echo "dev_server_pid=$dev_server_pid" >> "$GITHUB_OUTPUT"
582585

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
"check": "tsc --project ./tsconfig.all.json --noEmit && svelte-check check",
1616
"watch": "npm run check -- --watch",
1717
"opts": "TS_NODE_PROJECT=tsconfig.base.json NODE_OPTIONS='--loader ts-node/esm --experimental-specifier-resolution=node' \"$@\"",
18-
"generate": "npm run generate:types && npm run generate:js && npm run generate:types-issuer && npm run generate:js-issuer",
18+
"generate": "npm run generate:types && npm run generate:js && npm run generate:types-frontend && npm run generate:js-frontend && npm run generate:types-issuer && npm run generate:js-issuer",
1919
"generate:types": "didc bind ./src/internet_identity/internet_identity.did -t ts | ./scripts/rewrite-dfinity-imports > src/frontend/src/lib/generated/internet_identity_types.d.ts",
2020
"generate:js": "didc bind ./src/internet_identity/internet_identity.did -t js | ./scripts/rewrite-dfinity-imports > src/frontend/src/lib/generated/internet_identity_idl.js",
21+
"generate:types-frontend": "didc bind ./src/internet_identity_frontend/internet_identity_frontend.did -t ts | ./scripts/rewrite-dfinity-imports > src/frontend/src/lib/generated/internet_identity_frontend_types.d.ts",
22+
"generate:js-frontend": "didc bind ./src/internet_identity_frontend/internet_identity_frontend.did -t js | ./scripts/rewrite-dfinity-imports > src/frontend/src/lib/generated/internet_identity_frontend_idl.js",
2123
"generate:types-issuer": "didc bind ./demos/vc_issuer/vc_demo_issuer.did -t ts | ./scripts/rewrite-dfinity-imports > src/vc-api/src/generated/vc_issuer_types.ts",
2224
"generate:js-issuer": "didc bind ./demos/vc_issuer/vc_demo_issuer.did -t js | ./scripts/rewrite-dfinity-imports > src/frontend/src/lib/generated/vc_issuer_idl.js",
2325
"screenshots": "npm run opts -- ./src/frontend/screenshots.ts",

scripts/build

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,6 @@ then
109109
echo; echo
110110
fi
111111

112-
init_arg() {
113-
local init_arg
114-
init_arg=$(cat <<'EOF'
115-
(opt record { fetch_root_key = opt true; captcha_config = opt record { max_unsolved_captchas = 50:nat64; captcha_trigger = variant { Static = variant { CaptchaDisabled } } };})
116-
EOF
117-
)
118-
printf '%s\n' "$init_arg"
119-
}
120-
121112
frontend_init_arg() {
122113
local init_arg
123114
init_arg=$(cat <<'EOF'
@@ -177,7 +168,6 @@ function build_canister() {
177168
# indicate the II canister init argument type
178169
ic-wasm "$canister.wasm" -o "$canister.wasm" metadata candid:args -d "(opt InternetIdentityInit)" -v public
179170

180-
181171
# Write metadata for dfx.
182172
# The metadata includes a link to the release, which only exists if this is a release build.
183173
# In case of a release build, the version looks like this: <commit>,<release>,<dirty>
@@ -192,14 +182,13 @@ function build_canister() {
192182
wasm_url="https://github.com/dfinity/internet-identity/releases/download/$release/$asset_name"
193183
wasm_hash_url="https://github.com/dfinity/internet-identity/releases/download/$release/$asset_name.sha256"
194184

195-
init_arg=$(init_arg)
196185
init_guide="Use '(null)' for sensible defaults. See the candid interface for more details."
197186
metadata_json=$(echo '{}' | jq -cMr \
198187
--arg wasm_url "$wasm_url" \
199188
--arg wasm_hash_url "$wasm_hash_url" \
200-
--arg init_arg "$init_arg" \
189+
--arg init_arg "(null)" \
201190
--arg init_guide "$init_guide" \
202-
'. | .pullable = { wasm_url: $wasm_url, wasm_hash_url: $wasm_hash_url, dependencies: [], init_arg: $init_arg, init_guide: $init_guide} ')
191+
'. | .pullable = { wasm_url: $wasm_url, wasm_hash_url: $wasm_hash_url, dependencies: [], init_arg: "(null)", init_guide: $init_guide} ')
203192
ic-wasm "$canister.wasm" -o "$canister.wasm" metadata dfx -d "$metadata_json" -v public
204193
fi
205194

src/canister_tests/src/framework.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ lazy_static! {
8484
get_wasm_path("ARCHIVE_WASM_PREVIOUS".to_string(), &def_path).expect(&err)
8585
};
8686

87+
/** The gzipped Wasm module for the current II frontend build, i.e. the one we're testing */
88+
pub static ref II_FRONTEND_WASM: Vec<u8> = {
89+
let def_path = path::PathBuf::from("..").join("..").join("internet_identity_frontend.wasm.gz");
90+
let err = format!("
91+
Could not find Internet Identity Frontend Wasm module for current build.
92+
93+
I will look for it at {:?}, and you can specify another path with the environment variable II_FRONTEND_WASM (note that I run from {:?}).
94+
95+
In order to build the Wasm module, please run the following command:
96+
./scripts/build --frontend
97+
", &def_path, &std::env::current_dir().map(|x| x.display().to_string()).unwrap_or_else(|_| "an unknown directory".to_string()));
98+
get_wasm_path("II_FRONTEND_WASM".to_string(), &def_path).expect(&err)
99+
};
100+
87101
/** Empty WASM module (without any pre- and post-upgrade hooks. Useful to initialize a canister before loading a stable memory backup. */
88102
pub static ref EMPTY_WASM: Vec<u8> = vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0];
89103
}
@@ -177,6 +191,18 @@ pub fn install_ii_canister_with_arg_and_cycles(
177191
canister_id
178192
}
179193

194+
pub fn install_ii_frontend_canister(
195+
env: &PocketIc,
196+
wasm: Vec<u8>,
197+
arg: InternetIdentityFrontendArgs,
198+
) -> CanisterId {
199+
let bytes =
200+
candid::encode_one(arg).expect("error encoding II frontend installation arg as candid");
201+
let canister_id = env.create_canister();
202+
env.install_canister(canister_id, wasm, bytes, None);
203+
canister_id
204+
}
205+
180206
pub fn arg_with_wasm_hash(wasm: Vec<u8>) -> Option<InternetIdentityInit> {
181207
Some(InternetIdentityInit {
182208
archive_config: Some(ArchiveConfig {
@@ -186,6 +212,10 @@ pub fn arg_with_wasm_hash(wasm: Vec<u8>) -> Option<InternetIdentityInit> {
186212
entries_fetch_limit: 10,
187213
}),
188214
canister_creation_cycles_cost: Some(0),
215+
captcha_config: Some(CaptchaConfig {
216+
max_unsolved_captchas: 500,
217+
captcha_trigger: CaptchaTrigger::Static(StaticCaptchaTrigger::CaptchaEnabled),
218+
}),
189219
..InternetIdentityInit::default()
190220
})
191221
}

src/frontend/src/hooks.server.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1-
import { injectCanisterIdAndConfigPlugin } from "@dfinity/internet-identity-vite-plugins";
21
import type { Handle, ServerInit } from "@sveltejs/kit";
2+
import { building } from "$app/environment";
33
import { localeStore } from "$lib/stores/locale.store";
4-
5-
const transformHtml =
6-
process.env.NODE_ENV === "development"
7-
? (injectCanisterIdAndConfigPlugin({ canisterName: "internet_identity" })
8-
?.transformIndexHtml as (html: string) => string)
9-
: undefined;
4+
import { execSync } from "child_process";
105

116
export const handle: Handle = async ({ event, resolve }) => {
127
const response = await resolve(event);
138
if (
14-
transformHtml !== undefined &&
9+
!building &&
1510
response.headers.get("Content-Type") === "text/html" &&
1611
response.ok
1712
) {
13+
// Get frontend canister id and then fetch it's HTML
14+
const canisterId = execSync("dfx canister id internet_identity_frontend")
15+
.toString()
16+
.trim();
17+
const port = execSync("dfx info webserver-port").toString().trim();
18+
const canisterResponse = await fetch(
19+
`http://${canisterId}.localhost:${port}`,
20+
);
21+
const canisterHtml = await canisterResponse.text();
22+
23+
// Replace the body tag in the dev server HTML with the one from the canister HTML
24+
const bodyTagMatch = canisterHtml.match(/<body [^>]*>/);
1825
const html = await response.text();
19-
return new Response(transformHtml(html), {
26+
if (bodyTagMatch == null) {
27+
throw new Error(
28+
"Could not find body tag in the frontend canister HTML, cannot inject canister ID and config",
29+
);
30+
}
31+
return new Response(html.replace(/<body [^>]*>/, bodyTagMatch[0]), {
2032
...response,
2133
headers: {
2234
...response.headers,

src/frontend/src/lib/components/wizards/addAccessMethod/views/AddAccessMethod.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import Button from "$lib/components/ui/Button.svelte";
77
import Tooltip from "$lib/components/ui/Tooltip.svelte";
88
import { issuerMatches } from "$lib/utils/openID";
9-
import { canisterConfig } from "$lib/globals";
9+
import { backendCanisterConfig } from "$lib/globals";
1010
import type {
1111
OpenIdConfig,
1212
OpenIdCredential,
@@ -46,7 +46,7 @@
4646
}
4747
};
4848
49-
const openIdProviders = canisterConfig.openid_configs?.[0] ?? [];
49+
const openIdProviders = backendCanisterConfig.openid_configs?.[0] ?? [];
5050
5151
const hasCredential = (configIssuer: string): boolean =>
5252
openIdCredentials.some((cred) =>

src/frontend/src/lib/components/wizards/auth/views/PickAuthenticationMethod.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import PasskeyIcon from "$lib/components/icons/PasskeyIcon.svelte";
44
import Alert from "$lib/components/ui/Alert.svelte";
55
import ProgressRing from "$lib/components/ui/ProgressRing.svelte";
6-
import { canisterConfig } from "$lib/globals";
6+
import { backendCanisterConfig } from "$lib/globals";
77
import { waitFor } from "$lib/utils/utils";
88
import Tooltip from "$lib/components/ui/Tooltip.svelte";
99
import type { OpenIdConfig } from "$lib/generated/internet_identity_types";
@@ -32,7 +32,7 @@
3232
};
3333
3434
const supportsPasskeys = window.PublicKeyCredential !== undefined;
35-
const openIdProviders = canisterConfig.openid_configs?.[0] ?? [];
35+
const openIdProviders = backendCanisterConfig.openid_configs?.[0] ?? [];
3636
</script>
3737

3838
<div class="flex flex-col items-stretch gap-5">

src/frontend/src/lib/components/wizards/migration/views/EnterIdentityNumber.svelte

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import Tooltip from "$lib/components/ui/Tooltip.svelte";
99
import { t } from "$lib/stores/locale.store";
1010
import { Trans } from "$lib/components/locale";
11-
import { getPrimaryOrigin, parentIFrameOrigin } from "$lib/globals";
11+
import { getPrimaryOrigin } from "$lib/globals";
1212
1313
interface Props {
1414
onSubmit: (
@@ -19,11 +19,9 @@
1919
2020
const primaryOrigin = getPrimaryOrigin();
2121
const selfServiceOrigin =
22-
parentIFrameOrigin !== undefined
23-
? parentIFrameOrigin
24-
: window.location.origin !== primaryOrigin
25-
? window.location.origin
26-
: "https://identity.ic0.app";
22+
window.location.origin !== primaryOrigin
23+
? window.location.origin
24+
: "https://identity.ic0.app";
2725
2826
let { onSubmit }: Props = $props();
2927

0 commit comments

Comments
 (0)