From 0d59af95e08e58fa87661d756246470ffb60bcd3 Mon Sep 17 00:00:00 2001 From: sea-snake <104725312+sea-snake@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:10:28 +0100 Subject: [PATCH] Implement OpenID add/remove accounts in identity management (#2762) * Implement mock openID actor methods * Implement (un)link account on manage page. * Move Google client id to env variable. * Move Google client id to env variable. * Update CSP in test. * Update CSP in test. * Update CSP in test. * Add OpenID to showcase * Add OpenID to showcase * Apply prettier to json * Fix showcase * Fix showcase * Move callback path to shared constant. --- package.json | 4 +- src/canister_tests/src/framework.rs | 2 +- src/frontend/src/components/icons.ts | 26 +++ src/frontend/src/environment.ts | 2 + src/frontend/src/featureFlags/index.ts | 4 +- src/frontend/src/flows/manage/index.ts | 82 +++++++- .../flows/manage/linkedAccountsSection.json | 14 ++ .../src/flows/manage/linkedAccountsSection.ts | 139 +++++++++++++ src/frontend/src/flows/redirect.ts | 45 ++++ src/frontend/src/index.ts | 4 + src/frontend/src/styles/main.css | 39 +++- src/frontend/src/utils/i18n.ts | 5 + src/frontend/src/utils/iiConnection.ts | 10 +- src/frontend/src/utils/mockOpenID.ts | 23 +-- src/frontend/src/utils/openID.ts | 192 ++++++++++++++++++ src/frontend/test-setup.ts | 1 + src/internet_identity/src/http.rs | 2 +- src/showcase/src/constants.ts | 3 + src/showcase/src/pages/displayManage.astro | 7 + .../displayManageCredentialsMultiple.astro | 88 ++++++++ .../displayManageCredentialsSingle.astro | 76 +++++++ .../src/pages/displayManageSingle.astro | 7 + .../src/pages/displayManageTempKey.astro | 7 + vite.config.ts | 3 + 24 files changed, 747 insertions(+), 38 deletions(-) create mode 100644 src/frontend/src/flows/manage/linkedAccountsSection.json create mode 100644 src/frontend/src/flows/manage/linkedAccountsSection.ts create mode 100644 src/frontend/src/flows/redirect.ts create mode 100644 src/frontend/src/utils/openID.ts create mode 100644 src/showcase/src/pages/displayManageCredentialsMultiple.astro create mode 100644 src/showcase/src/pages/displayManageCredentialsSingle.astro diff --git a/package.json b/package.json index bb595442cf..87dd45436d 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "private": true, "license": "SEE LICENSE IN LICENSE.md", "scripts": { - "dev": "II_FETCH_ROOT_KEY=1 II_DUMMY_CAPTCHA=1 vite", - "host": "II_FETCH_ROOT_KEY=1 II_DUMMY_CAPTCHA=1 vite --host", + "dev": "II_FETCH_ROOT_KEY=1 II_DUMMY_CAPTCHA=1 II_OPENID_GOOGLE_CLIENT_ID=45431994619-cbbfgtn7o0pp0dpfcg2l66bc4rcg7qbu.apps.googleusercontent.com vite", + "host": "II_FETCH_ROOT_KEY=1 II_DUMMY_CAPTCHA=1 II_OPENID_GOOGLE_CLIENT_ID=45431994619-cbbfgtn7o0pp0dpfcg2l66bc4rcg7qbu.apps.googleusercontent.com vite --host", "showcase": "astro dev --root ./src/showcase", "build": "tsc --noEmit && vite build", "check": "tsc --project ./tsconfig.all.json --noEmit", diff --git a/src/canister_tests/src/framework.rs b/src/canister_tests/src/framework.rs index 0bc05dc661..e980a61d2a 100644 --- a/src/canister_tests/src/framework.rs +++ b/src/canister_tests/src/framework.rs @@ -433,7 +433,7 @@ xr-spatial-tracking=()", let rgx = Regex::new( "^default-src 'none';\ connect-src 'self' https:;\ -img-src 'self' data:;\ +img-src 'self' data: https://\\*.googleusercontent.com;\ script-src 'strict-dynamic' ('[^']+' )*'unsafe-inline' 'unsafe-eval' https:;\ base-uri 'none';\ form-action 'none';\ diff --git a/src/frontend/src/components/icons.ts b/src/frontend/src/components/icons.ts index f82f1829d6..0c8be576f7 100644 --- a/src/frontend/src/components/icons.ts +++ b/src/frontend/src/components/icons.ts @@ -445,3 +445,29 @@ export const cypherIcon = html` /> `; + +export const googleIcon = html` + + + + + + +`; diff --git a/src/frontend/src/environment.ts b/src/frontend/src/environment.ts index ed36419d65..1b98538d67 100644 --- a/src/frontend/src/environment.ts +++ b/src/frontend/src/environment.ts @@ -5,3 +5,5 @@ export const VERSION = import.meta.env.II_VERSION ?? ""; export const FETCH_ROOT_KEY = import.meta.env.II_FETCH_ROOT_KEY === "1"; export const DUMMY_AUTH = import.meta.env.II_DUMMY_AUTH === "1"; export const DUMMY_CAPTCHA = import.meta.env.II_DUMMY_CAPTCHA === "1"; +export const II_OPENID_GOOGLE_CLIENT_ID = import.meta.env + .II_OPENID_GOOGLE_CLIENT_ID; diff --git a/src/frontend/src/featureFlags/index.ts b/src/frontend/src/featureFlags/index.ts index 0b0c8d8aa4..14cd4c9b91 100644 --- a/src/frontend/src/featureFlags/index.ts +++ b/src/frontend/src/featureFlags/index.ts @@ -1,6 +1,7 @@ // Feature flags with default values const FEATURE_FLAGS_WITH_DEFAULTS = { DOMAIN_COMPATIBILITY: false, + OPENID_AUTHENTICATION: false, } as const satisfies Record; const LOCALSTORAGE_FEATURE_FLAGS_PREFIX = "ii-localstorage-feature-flags__"; @@ -63,4 +64,5 @@ const initializedFeatureFlags = Object.fromEntries( window.__featureFlags = initializedFeatureFlags; // Export initialized feature flags as named exports -export const { DOMAIN_COMPATIBILITY } = initializedFeatureFlags; +export const { DOMAIN_COMPATIBILITY, OPENID_AUTHENTICATION } = + initializedFeatureFlags; diff --git a/src/frontend/src/flows/manage/index.ts b/src/frontend/src/flows/manage/index.ts index c05f94d57e..e647d662d7 100644 --- a/src/frontend/src/flows/manage/index.ts +++ b/src/frontend/src/flows/manage/index.ts @@ -15,10 +15,13 @@ import { logoutSection } from "$src/components/logout"; import { mainWindow } from "$src/components/mainWindow"; import { toast } from "$src/components/toast"; import { ENABLE_PIN_QUERY_PARAM_KEY, LEGACY_II_URL } from "$src/config"; +import { OPENID_AUTHENTICATION } from "$src/featureFlags"; import { addDevice } from "$src/flows/addDevice/manage/addDevice"; import { dappsExplorer } from "$src/flows/dappsExplorer"; import { KnownDapp, getDapps } from "$src/flows/dappsExplorer/dapps"; import { dappsHeader, dappsTeaser } from "$src/flows/dappsExplorer/teaser"; +import { linkedAccountsSection } from "$src/flows/manage/linkedAccountsSection"; +import copyJson from "$src/flows/manage/linkedAccountsSection.json"; import { TempKeyWarningAction, tempKeyWarningBox, @@ -29,6 +32,14 @@ import { setupKey, setupPhrase } from "$src/flows/recovery/setupRecovery"; import { I18n } from "$src/i18n"; import { AuthenticatedConnection, Connection } from "$src/utils/iiConnection"; import { TemplateElement, renderPage } from "$src/utils/lit-html"; +import { OpenIDCredential } from "$src/utils/mockOpenID"; +import { + GOOGLE_REQUEST_CONFIG, + createAnonymousNonce, + decodeJWT, + isPermissionError, + requestJWT, +} from "$src/utils/openID"; import { PreLoadImage } from "$src/utils/preLoadImage"; import { isProtected, @@ -147,6 +158,9 @@ const displayManageTemplate = ({ onAddDevice, addRecoveryPhrase, addRecoveryKey, + credentials, + onLinkAccount, + onUnlinkAccount, dapps, exploreDapps, identityBackground, @@ -157,6 +171,9 @@ const displayManageTemplate = ({ onAddDevice: () => void; addRecoveryPhrase: () => void; addRecoveryKey: () => void; + credentials: OpenIDCredential[]; + onLinkAccount: () => void; + onUnlinkAccount: (credential: OpenIDCredential) => void; dapps: KnownDapp[]; exploreDapps: () => void; identityBackground: PreLoadImage; @@ -182,6 +199,14 @@ const displayManageTemplate = ({ onAddDevice, warnNoPasskeys, })} + ${OPENID_AUTHENTICATION.isEnabled() + ? linkedAccountsSection({ + credentials, + onLinkAccount, + onUnlinkAccount, + hasOtherAuthMethods: authenticators.length > 0, + }) + : ""} ${recoveryMethodsSection({ recoveries, addRecoveryPhrase, addRecoveryKey })}