From 71774d055b411830b130f599835c3856e2597f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sn=C3=A6r=20Seljan=20=C3=9E=C3=B3roddsson?= <112904566+snaerseljan@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:29:49 +0000 Subject: [PATCH] feat(services-bff): My pages bff setup (#16543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix user menu test * Updates to environment and config * Update infra allowed external api urls to be hard coded * Simplify client urls with bff postfix in it * Add ingress to project and remove logout redirect path in favour of client base url * Add docker express to services bff * update config simpler syntax * chore: nx format:write update dirty files * Update config and redis dev setup * Update crypto service to include algorithm in the encryption, explain better in comments what encrypt/decrypt is doing and update crypto test to not use mock * Remove CORS entirely in favour of client proxy config * Update error handling in bff backend, refactor infra and handle error query param in client * When proxy service errors then handle as unauthorized. Update targetUrl to be defensive, i.e. no undefined possible. * Remove unnecessary Uint8Array conversion * Simplify the BFFUser object to not have dateOfBirth and remove double scope field which was due to backwards compatibility * Update cookies to share constants, update options to be more secure * access token expire time latency by 5 sec * remove omit * Update user profile cache ttl * update cache ttl again and rename baseUrl to issuerUrl in ids service * reaname var * remove params from cache attempt that where not used in the callback * Clean up old session in login callback if it exists * Fix login callback cache clean up and revoke refresh token * Update logout flow to clean up, revoke tokens and better validation. Also deletes the logout callback * remove unused import * Simplify error in favour of enhanced fetch * created enhanced fetch module, moved pkce service to services, updated proxy service and a little refactor * par support flag not optional * Fix typo * Add better validation to crypto decryption function * Update validate uri to be more secure, create test for validate uri. Update port range in environment * Remove state param from logout to ensure it will not be passed to redirect uri * Adding more tests and increasing security in the function * Refactor after reading comments from coderabbit * remove private from method for test * Move portal scopes to shareable location. * Remove unused import * Add no_refresh query to user endpoint in backend * Polling and broadcaster added to react spa bff library * Enhanced security in pkce service.and improve error handling to be more secure * Update usePolling to have better types and secure resumabiltiy. * Refactor useBroadcaster. * Add client logic to handle the case if bff server goes down * Fix tests and builds * Fix portal infra local vars * DX infra setup for services-bff * Remove error log from revokeRefreshToken since it is handled by enhancedFetch and update download service local url * Rename cached toke fields to be prefixed with encrypted and fix where encryption was missing. Also fix for revoking wrong token * Better handling on errors in auth service * Update api requests formatting and handling to handle exceptions and errors better. * Update apps/services/bff/src/app/bff.config.ts simpler redis config Co-authored-by: Eiríkur Heiðar Nilsson * cleanup after commit from github * Update after our pull request AI suggested the change * Remove broadcaster mocks * Remove redundant timeout in favour of poller * Fix portal config, fix redis cache module init, update bff provider to handle logout in before redirect * Remove timeout in logout broadcasting and throw the error in postRequest if not successful plain text response * Revert the timeout in the logout * chore: charts update dirty files * Rename queries to dto for consistency in monorepo and add log for logout callback * Fix cli error that got merged from main * Fix prettier formatting error * chore: nx format:write update dirty files * fix storybook build * ci: trigger from levy user * fix: use portals-admin, added portal-env test * Revert manual validation and use library * Use fetch instead of post in download url * Fix type errors and add forward get proxy api request * fix: main conflict * chore: charts update dirty files * fix: prettier issues * chore: prettify * chore: nx format:write update dirty files * ci: add services-bff to helm chart * Fix env vars for feature deploy * Fix health check to be excluded from prefix * update global prefix logic * update bff services options * Remove bff redis name env var * Update bff config again * Update portal env spec for feature branch * chore: charts update dirty files * Update validation error log * Remove database healthcheck * Revert globalprefix options and update liveness and readiness infra checks * chore: charts update dirty files * Add auth controller tests * Add logout log for testing in feature deploy * remove unused * clean up auth controller test * chore: nx format:write update dirty files * Add tests for proxy controller * Add ref to infra for api * update charts * add zed editor config to gitignore * Add support for mocks * chore: nx format:write update dirty files * Fix portal env spec * chore: charts update dirty files * Update mocking server logic for portals * update mock logic * fix: public envs (#16493) * fix: merge conflict * fix: improved zod schema generation * test: update portal-env test for service building * fix: generate feature deploy urls * fix: improve getEnvUrl func * feat: integrated bff to ServiceBuilder * fix: more abstraction to dsl * fix: simplify and cleanup * chore: remove unused file * chore: cleanup dupes * chore: nx format:write update dirty files * chore: more cleanup --------- Co-authored-by: andes-it * Move my-pages over to bff first attempt * chore: remove nx-command impl (#16532) * chore: move nx runcommand cli to a new PR * chore: commit save point * chore: commit save point * Update infra setup * fix tests * chore: charts update dirty files * update my pages infra * fix env in infra * fix infra url * Removed un used import * chore: charts update dirty files * chore: nx format:write update dirty files * fix: revert secret type changes * chore: nx format:write update dirty files * chore: cleanup * Removed un used import * Update after self review * fix feature deployment url * fix tests * fix missing logger * chore: nx format:write update dirty files * update api graphql bff config env var * update api graphql bff config env var * fix tests * fix tests * chore: charts update dirty files * chore: nx format:write update dirty files * grantnamespaces * chore: charts update dirty files * grantnamespace identity server * chore: charts update dirty files * disable global auth on dev * disable global auth on dev * chore: charts update dirty files * Fix UserMenu test * fix portal core tests * test: update bff tests * test: fix scope bad placement * fix: minor cleanup * chore: nx format:write update dirty files * chore: charts update dirty files * Merge branch 'main' into feat/bff-my-pages # Conflicts: # apps/portals/my-pages/project.json # libs/react-spa/bff/src/lib/bff.hooks.ts * Add authority string to bff state * update to new bff hooks * Revert "Merge branch 'main' into feat/bff-my-pages" This reverts commit 3f74e608c0e7f259bc383b5e8221c71e47b68e73. * chore: charts update dirty files * chore: nx format:write update dirty files * Update hooks * chore: charts update dirty files * Update formSubmit handler to proxy bff requests and add external post request to proxy.controller * Fix test * Small fixes * chore: rebuild * update hook after bff addon * Remove unused commit * Add issuer to my-pages * chore: charts update dirty files * chore: nx format:write update dirty files * remove unused env var from my pages infra * chore: charts update dirty files * Introduce legacy user info hook until application system has implemented bff pattern * chore: charts update dirty files * Add useLegacyAuth hook to support both contexts * Revert back to bff only hooks * Update global prefix path for my pages * Fix bff creator logic * fix tests * chore: charts update dirty files * fix tests * chore: charts update dirty files * update charts build error * console.error if no download url is found * fix document type check * fix infra paths * chore: charts update dirty files * feat(application-system-form): Update application-system to use bff (#16973) * Update hooks and frontend code for application system * Add proxy config to application system * Add support to add allowed redirect uris from bff infras * chore: charts update dirty files * chore: nx format:write update dirty files * add application system scopes to my-pages-portal * chore: charts update dirty files * Update mocking setup for all SPAs --------- Co-authored-by: andes-it * update application system ui tests * Fix tests * fix islandis build error * Extend Redis cache keys to be unique between bffs since using same Redis server * Add deprecation messages to old hooks * Fix warning * Fix tests and change separator for cache key * Remove redundant config * Remove options from mock * Fix potential body error * Make sure that bff scopes are uniq * chore: nx format:write update dirty files * Include targetLinkUrl in error redirects * Update base bff infra to be strict about allowed redirect uris * Use bff hooks instead in old auth lib * Add comment about birthday hook and remove user type from isDelegation hook * Remove optional check on profile * Update bff redirect url logic * fix accidental scope switch * chore: charts update dirty files * Update prod url for allowed redirect uris * chore: charts update dirty files * Move comment above hook * Update redirect uris * fix tests * chore: charts update dirty files * Fix prod being null * chore: charts update dirty files * Fix prod url being null when using ctx.env.domain * chore: charts update dirty files * Prod url fix * chore: charts update dirty files * Revert removed auth context * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: Eiríkur Heiðar Nilsson Co-authored-by: Jón Levy --- .../form/infra/application-system-form.ts | 15 +-- apps/application-system/form/project.json | 19 ++- .../application-system/form/proxy.config.json | 6 + apps/application-system/form/src/app/App.tsx | 25 ++-- apps/application-system/form/src/auth.ts | 57 --------- .../form/src/components/Router.tsx | 10 +- .../form/src/environments/environment.ts | 8 -- .../form/src/lib/routes.tsx | 4 +- apps/application-system/form/src/main.tsx | 8 +- .../form/src/mocks/index.ts | 1 + .../form/src/routes/Application.tsx | 6 +- .../modules/documents/document.controller.ts | 6 +- apps/portals/admin/infra/portals-admin.ts | 5 - apps/portals/admin/project.json | 2 +- apps/portals/admin/src/app/App.tsx | 3 +- .../admin/src/environments/environment.ts | 6 - .../my-pages/infra/portals-my-pages.ts | 15 +-- apps/portals/my-pages/project.json | 23 +++- apps/portals/my-pages/proxy.config.json | 6 + apps/portals/my-pages/src/Main.tsx | 7 +- apps/portals/my-pages/src/app/App.tsx | 28 +++-- apps/portals/my-pages/src/auth.ts | 79 ------------ .../src/components/Greeting/Greeting.tsx | 7 +- .../my-pages/src/components/Header/Header.tsx | 6 +- .../src/components/Layout/FullWidthLayout.tsx | 4 +- .../Loaders/AuthOverlay/AuthOverlay.tsx | 4 +- .../my-pages/src/environments/environment.ts | 6 - .../src/screens/Dashboard/Dashboard.tsx | 40 +++--- apps/services/bff/infra/admin-portal.infra.ts | 3 +- .../bff/infra/my-pages-portal.infra.ts | 75 +++++++++++ apps/services/bff/src/app/bff.config.ts | 10 ++ .../app/modules/auth/auth.controller.spec.ts | 13 +- .../bff/src/app/modules/auth/auth.service.ts | 38 +++--- .../bff/src/app/modules/auth/auth.types.ts | 6 + .../auth/token-refresh.service.spec.ts | 12 +- .../src/app/modules/cache/cache.service.ts | 13 +- .../src/app/modules/proxy/proxy.controller.ts | 14 ++- .../src/app/modules/proxy/proxy.service.ts | 57 +++++++-- .../app/modules/user/user.controller.spec.ts | 28 +++-- .../bff/src/app/utils/get-cookie-options.ts | 2 +- .../bff/src/app/utils/validate-uri.ts | 3 + .../bff/src/environment/environment.schema.ts | 15 ++- .../bff/src/environment/environment.ts | 2 +- apps/services/bff/src/main.ts | 2 +- apps/services/bff/test/setup.ts | 3 +- charts/islandis/values.dev.yaml | 96 ++++++++++++-- charts/islandis/values.prod.yaml | 98 +++++++++++++-- charts/islandis/values.staging.yaml | 96 ++++++++++++-- .../application-system-form/values.dev.yaml | 2 - .../application-system-form/values.prod.yaml | 2 - .../values.staging.yaml | 2 - charts/services/portals-admin/values.dev.yaml | 1 - .../services/portals-admin/values.prod.yaml | 1 - .../portals-admin/values.staging.yaml | 1 - .../services/service-portal/values.dev.yaml | 2 - .../services/service-portal/values.prod.yaml | 2 - .../service-portal/values.staging.yaml | 2 - .../values.dev.yaml | 5 +- .../values.prod.yaml | 5 +- .../values.staging.yaml | 5 +- .../values.dev.yaml | 103 +++++++++++++++ .../values.prod.yaml | 105 ++++++++++++++++ .../values.staging.yaml | 103 +++++++++++++++ infra/src/dsl/bff.ts | 83 +++++++++--- infra/src/dsl/feature-values.spec.ts | 6 +- infra/src/dsl/portal-env.spec.ts | 6 +- infra/src/dsl/types/input-types.ts | 6 +- infra/src/uber-charts/islandis.ts | 35 +++--- .../core/src/lib/conditionUtils.spec.ts | 28 ++--- .../core/src/lib/conditionUtils.ts | 6 +- libs/application/core/src/lib/formUtils.ts | 18 +-- libs/application/graphql/src/lib/client.ts | 27 ++-- .../src/fields/Review/index.tsx | 14 +-- .../src/fields/Summary/SpouseSummaryForm.tsx | 21 ++-- .../src/fields/RejectApproveButtons/index.tsx | 18 +-- .../communicationChannels/ChannelList.tsx | 8 +- .../src/fields/Summary.tsx | 24 ++-- .../src/fields/Review/index.tsx | 4 +- .../src/fields/Review/index.tsx | 4 +- .../src/fields/Review/index.tsx | 14 +-- libs/application/types/src/lib/Condition.ts | 6 +- .../src/components/ConditionHandler.tsx | 6 +- .../src/components/DelegationsScreen.tsx | 36 +++--- .../ui-shell/src/components/ScreenFooter.tsx | 16 +-- .../ui-shell/src/lib/FormShell.spec.tsx | 38 ++++-- .../ui-shell/src/lib/FormShell.tsx | 21 ++-- .../ui-shell/src/reducer/ReducerTypes.ts | 4 +- .../ui-shell/src/reducer/reducerUtils.ts | 24 ++-- libs/auth/react/src/lib/auth/AuthContext.tsx | 40 +++--- libs/auth/scopes/src/index.ts | 33 ++--- .../lib/clients/application-system-scopes.ts | 32 +++++ .../src/lib/clients/service-portal-scopes.ts | 1 + .../hooks/useSingleNavigationItem.spec.tsx | 28 +++-- .../assets/src/screens/Overview/Overview.tsx | 4 +- .../core/src/utils/documentFormSubmission.ts | 15 +-- .../DocumentActionBar/DocumentActionBarV2.tsx | 20 +-- .../DocumentActions/DocumentActionsV3.tsx | 72 +++++------ .../DocumentRenderer/PdfDocument.tsx | 14 ++- .../documents/src/utils/downloadDocumentV2.ts | 88 +++---------- .../FinanceSchedule/FinanceSchedule.tsx | 2 +- .../screens/FinanceStatus/FinanceStatus.tsx | 2 +- .../my-pages/graphql/src/lib/client.ts | 14 +-- .../screens/HealthOverview/HealthOverview.tsx | 2 +- .../UserProfileNotificationSettings.tsx | 12 +- .../Forms/ProfileForm/ProfileForm.tsx | 20 +-- .../UserOnboarding/UserOnboarding.tsx | 8 +- .../UserOnboardingModal.tsx | 8 +- .../UserOnboardingModal/components/Header.tsx | 6 +- .../src/screens/BioChild/BioChild.tsx | 2 +- .../src/screens/ChildCustody/ChildCustody.tsx | 2 +- .../src/screens/Company/CompanyInfo.tsx | 2 +- .../src/screens/UserInfo/UserInfo.tsx | 3 +- .../UserInfoOverview/UserInfoOverview.tsx | 2 +- .../src/screens/UserProfile/UserProfile.tsx | 2 +- .../EducationalDetail/EducationalDetail.tsx | 8 +- .../HealthDirectorateDetail.tsx | 3 +- .../src/screens/restrictions/Restrictions.tsx | 16 +-- .../src/components/LogTable/LogTable.tsx | 8 +- .../components/LogTable/LogTableMobile.tsx | 10 +- .../src/screens/Sessions/Sessions.tsx | 2 +- .../src/screens/Parliamentary/index.tsx | 2 +- .../screens/Presidential/OwnerView/index.tsx | 14 +-- .../src/screens/shared/SigneeView/index.tsx | 4 +- libs/react-spa/bff/src/index.ts | 3 +- libs/react-spa/bff/src/lib/BffProvider.tsx | 34 +++-- libs/react-spa/bff/src/lib/bff.hooks.ts | 119 +++++------------- libs/react-spa/bff/src/lib/bff.mocks.ts | 2 + libs/react-spa/bff/src/lib/bff.state.ts | 4 +- libs/react-spa/bff/src/lib/bff.utils.ts | 14 ++- .../src/auth/UserMenu/UserDelegations.tsx | 4 +- .../src/auth/UserMenu/UserMenu.spec.tsx | 76 ++++++----- .../components/src/auth/UserMenu/UserMenu.tsx | 4 +- .../src/auth/UserMenu/UserProfileLocale.tsx | 6 +- libs/shared/mocking/src/msw/startMocking.ts | 21 +++- libs/shared/types/src/lib/bff.ts | 3 + libs/shared/utils/src/lib/isDelegation.ts | 6 +- package.json | 3 +- 137 files changed, 1626 insertions(+), 980 deletions(-) create mode 100644 apps/application-system/form/proxy.config.json delete mode 100644 apps/application-system/form/src/auth.ts create mode 100644 apps/application-system/form/src/mocks/index.ts create mode 100644 apps/portals/my-pages/proxy.config.json delete mode 100644 apps/portals/my-pages/src/auth.ts create mode 100644 apps/services/bff/infra/my-pages-portal.infra.ts create mode 100644 charts/services/services-bff-portals-my-pages/values.dev.yaml create mode 100644 charts/services/services-bff-portals-my-pages/values.prod.yaml create mode 100644 charts/services/services-bff-portals-my-pages/values.staging.yaml create mode 100644 libs/auth/scopes/src/lib/clients/application-system-scopes.ts diff --git a/apps/application-system/form/infra/application-system-form.ts b/apps/application-system/form/infra/application-system-form.ts index 89e449ddd4a3..4b91a779b7f1 100644 --- a/apps/application-system/form/infra/application-system-form.ts +++ b/apps/application-system/form/infra/application-system-form.ts @@ -1,25 +1,12 @@ import { ref, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' -export const serviceSetup = (services: { - api: ServiceBuilder<'api'> -}): ServiceBuilder<'application-system-form'> => +export const serviceSetup = (): ServiceBuilder<'application-system-form'> => service('application-system-form') .namespace('application-system') .liveness('/liveness') .readiness('/readiness') .env({ BASEPATH: '/umsoknir', - SI_PUBLIC_GRAPHQL_PATH: { - dev: '', - prod: '', - staging: '', - local: ref((h) => `http://${h.svc(services.api)}`), - }, - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: { - dev: 'https://identity-server.dev01.devland.is', - staging: 'https://identity-server.staging01.devland.is', - prod: 'https://innskra.island.is', - }, SI_PUBLIC_ENVIRONMENT: ref((h) => h.env.type), }) .secrets({ diff --git a/apps/application-system/form/project.json b/apps/application-system/form/project.json index 16b789598793..77aaf485714a 100644 --- a/apps/application-system/form/project.json +++ b/apps/application-system/form/project.json @@ -64,7 +64,8 @@ "executor": "@nx/webpack:dev-server", "options": { "port": 4242, - "buildTarget": "application-system-form:build" + "buildTarget": "application-system-form:build", + "proxyConfig": "apps/application-system/form/proxy.config.json" }, "configurations": { "production": { @@ -103,16 +104,32 @@ "parallel": false } }, + "start-bff": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "node -r esbuild-register src/cli/cli.ts run-local-env services-bff-portals-my-pages" + ], + "cwd": "infra" + } + }, "dev": { "executor": "nx:run-commands", "options": { "commands": [ "yarn nx run application-system-api:dev", + "yarn nx run service-portal:start-bff", "yarn start application-system-form" ], "parallel": true } }, + "mock": { + "executor": "nx:run-commands", + "options": { + "commands": ["API_MOCKS=true yarn start application-system-form"] + } + }, "docker-static": { "executor": "Intentionally left blank, only so this target is valid when using `nx show projects --with-target docker-static`" } diff --git a/apps/application-system/form/proxy.config.json b/apps/application-system/form/proxy.config.json new file mode 100644 index 000000000000..af8396a18f6d --- /dev/null +++ b/apps/application-system/form/proxy.config.json @@ -0,0 +1,6 @@ +{ + "/bff/*": { + "target": "http://localhost:3010", + "secure": false + } +} diff --git a/apps/application-system/form/src/app/App.tsx b/apps/application-system/form/src/app/App.tsx index 12bbca0bb9c5..b99fac2f3a2d 100644 --- a/apps/application-system/form/src/app/App.tsx +++ b/apps/application-system/form/src/app/App.tsx @@ -1,23 +1,34 @@ import { ApolloProvider } from '@apollo/client' -import { initializeClient } from '@island.is/application/graphql' +import { client } from '@island.is/application/graphql' import { LocaleProvider } from '@island.is/localization' -import { defaultLanguage } from '@island.is/shared/constants' -import { AuthProvider } from '@island.is/auth/react' import { FeatureFlagProvider } from '@island.is/react/feature-flags' +import { defaultLanguage } from '@island.is/shared/constants' +import { applicationSystemScopes } from '@island.is/auth/scopes' +import { BffProvider, createMockedInitialState } from '@island.is/react-spa/bff' +import { Router } from '../components/Router' import { environment } from '../environments' import { BASE_PATH } from '../lib/routes' -import { Router } from '../components/Router' +import { isMockMode } from '../mocks' + +const mockedInitialState = isMockMode + ? createMockedInitialState({ + scopes: applicationSystemScopes, + }) + : undefined export const App = () => ( - + - + - + ) diff --git a/apps/application-system/form/src/auth.ts b/apps/application-system/form/src/auth.ts deleted file mode 100644 index 59765a32bffc..000000000000 --- a/apps/application-system/form/src/auth.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { configure, configureMock } from '@island.is/auth/react' -import { environment } from './environments' -import { - ApiScope, - ApplicationScope, - NationalRegistryScope, - UserProfileScope, - EndorsementsScope, - AuthScope, - MunicipalitiesFinancialAidScope, -} from '@island.is/auth/scopes' - -// Are users mocked? -const userMocked = process.env.API_MOCKS === 'true' - -if (userMocked) { - configureMock({ - profile: { name: 'Mock', locale: 'is', nationalId: '0000000000' }, - }) -} else { - configure({ - baseUrl: `${window.location.origin}/umsoknir`, - redirectPath: '/signin-oidc', - redirectPathSilent: '/silent/signin-oidc', - initiateLoginPath: '/login', - authority: environment.identityServer.authority, - client_id: '@island.is/web', - scope: [ - 'openid', - 'profile', - 'api_resource.scope', - ApplicationScope.read, - ApplicationScope.write, - AuthScope.actorDelegations, - UserProfileScope.read, - UserProfileScope.write, - NationalRegistryScope.individuals, - EndorsementsScope.main, - ApiScope.internal, - ApiScope.internalProcuring, - ApiScope.meDetails, - ApiScope.fishingLicense, - MunicipalitiesFinancialAidScope.read, - MunicipalitiesFinancialAidScope.write, - MunicipalitiesFinancialAidScope.applicant, - ApiScope.assets, - ApiScope.samgongustofaVehicles, - ApiScope.carRecycling, - ApiScope.energyFunds, - ApiScope.vinnueftirlitid, - ApiScope.signatureCollection, - ApiScope.licenses, - ], - post_logout_redirect_uri: `${window.location.origin}`, - userStorePrefix: 'as.', - }) -} diff --git a/apps/application-system/form/src/components/Router.tsx b/apps/application-system/form/src/components/Router.tsx index 2bffb46aa94e..7c6b2cfd4033 100644 --- a/apps/application-system/form/src/components/Router.tsx +++ b/apps/application-system/form/src/components/Router.tsx @@ -1,15 +1,15 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom' -import { useAuth } from '@island.is/auth/react' -import { LoadingScreen } from '@island.is/react/components' -import { BASE_PATH, routes } from '../lib/routes' -import React, { useRef } from 'react' import { useLocale } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' +import { LoadingScreen } from '@island.is/react/components' +import { useRef } from 'react' import { m } from '../lib/messages' +import { BASE_PATH, routes } from '../lib/routes' export const Router = () => { const { formatMessage } = useLocale() - const { userInfo } = useAuth() + const userInfo = useUserInfo() const router = useRef>() if (!userInfo) { diff --git a/apps/application-system/form/src/environments/environment.ts b/apps/application-system/form/src/environments/environment.ts index 301c62a3308d..c421aab25e4c 100644 --- a/apps/application-system/form/src/environments/environment.ts +++ b/apps/application-system/form/src/environments/environment.ts @@ -2,10 +2,6 @@ import { getStaticEnv } from '@island.is/shared/utils' const devConfig = { production: false, - baseApiUrl: 'http://localhost:4444', - identityServer: { - authority: 'https://identity-server.dev01.devland.is', - }, featureFlagSdkKey: 'YcfYCOwBTUeI04mWOWpPdA/KgCHhUk0_k2BdiKMaNh3qA', DD_RUM_CLIENT_TOKEN: 'unknown', DD_RUM_APPLICATION_ID: 'unknown', @@ -15,10 +11,6 @@ const devConfig = { const prodConfig = { production: true, - baseApiUrl: getStaticEnv('SI_PUBLIC_GRAPHQL_PATH') ?? window.location.origin, - identityServer: { - authority: getStaticEnv('SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL'), - }, featureFlagSdkKey: getStaticEnv('SI_PUBLIC_CONFIGCAT_SDK_KEY'), DD_RUM_CLIENT_TOKEN: getStaticEnv('SI_PUBLIC_DD_RUM_CLIENT_TOKEN'), DD_RUM_APPLICATION_ID: getStaticEnv('SI_PUBLIC_DD_RUM_APPLICATION_ID'), diff --git a/apps/application-system/form/src/lib/routes.tsx b/apps/application-system/form/src/lib/routes.tsx index 9a429a5073e0..50bc677d6f05 100644 --- a/apps/application-system/form/src/lib/routes.tsx +++ b/apps/application-system/form/src/lib/routes.tsx @@ -1,12 +1,12 @@ import { Outlet, RouteObject } from 'react-router-dom' -import { UserProfileLocale } from '@island.is/shared/components' import { ErrorShell, HeaderInfoProvider } from '@island.is/application/ui-shell' +import { UserProfileLocale } from '@island.is/shared/components' +import { Layout } from '../components/Layout/Layout' import { Application } from '../routes/Application' import { Applications } from '../routes/Applications' import { AssignApplication } from '../routes/AssignApplication' -import { Layout } from '../components/Layout/Layout' export const BASE_PATH = '/umsoknir' diff --git a/apps/application-system/form/src/main.tsx b/apps/application-system/form/src/main.tsx index bc28060735af..a70b6698485a 100644 --- a/apps/application-system/form/src/main.tsx +++ b/apps/application-system/form/src/main.tsx @@ -1,14 +1,12 @@ import '@island.is/api/mocks' -import React, { StrictMode } from 'react' +import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import { isRunningOnEnvironment } from '@island.is/shared/utils' -import './auth' - -import { environment } from './environments' -import App from './app/App' import { userMonitoring } from '@island.is/user-monitoring' +import App from './app/App' +import { environment } from './environments' if (!isRunningOnEnvironment('local')) { userMonitoring.initDdRum({ diff --git a/apps/application-system/form/src/mocks/index.ts b/apps/application-system/form/src/mocks/index.ts new file mode 100644 index 000000000000..2d1618cb1f70 --- /dev/null +++ b/apps/application-system/form/src/mocks/index.ts @@ -0,0 +1 @@ +export const isMockMode = process.env.API_MOCKS === 'true' diff --git a/apps/application-system/form/src/routes/Application.tsx b/apps/application-system/form/src/routes/Application.tsx index 713ba8a0b2d3..65ea62428dce 100644 --- a/apps/application-system/form/src/routes/Application.tsx +++ b/apps/application-system/form/src/routes/Application.tsx @@ -1,9 +1,9 @@ import { useParams } from 'react-router-dom' +import { coreMessages } from '@island.is/application/core' import { ApplicationForm, ErrorShell } from '@island.is/application/ui-shell' import { useLocale } from '@island.is/localization' -import { coreMessages } from '@island.is/application/core' -import { useAuth } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' type UseParams = { slug: string @@ -12,7 +12,7 @@ type UseParams = { export const Application = () => { const { slug, id } = useParams() as UseParams - const { userInfo } = useAuth() + const userInfo = useUserInfo() const { formatMessage } = useLocale() const nationalRegistryId = userInfo?.profile?.nationalId diff --git a/apps/download-service/src/app/modules/documents/document.controller.ts b/apps/download-service/src/app/modules/documents/document.controller.ts index ffdf2e657ad1..3f775d676675 100644 --- a/apps/download-service/src/app/modules/documents/document.controller.ts +++ b/apps/download-service/src/app/modules/documents/document.controller.ts @@ -67,9 +67,9 @@ export class DocumentController { rawDocumentDTO.fileName }.pdf`, ) - res.header('Pragma: no-cache') - res.header('Cache-Control: no-cache') - res.header('Cache-Control: nmax-age=0') + res.header('Pragma', 'no-cache') + res.header('Cache-Control', 'no-cache') + res.header('Cache-Control', 'nmax-age=0') return res.end(buffer) } diff --git a/apps/portals/admin/infra/portals-admin.ts b/apps/portals/admin/infra/portals-admin.ts index 0830c02fd59d..46f379ce58e6 100644 --- a/apps/portals/admin/infra/portals-admin.ts +++ b/apps/portals/admin/infra/portals-admin.ts @@ -17,11 +17,6 @@ export const serviceSetup = (): ServiceBuilder<'portals-admin'> => }) .env({ BASEPATH: '/stjornbord', - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: { - dev: 'https://identity-server.dev01.devland.is', - staging: 'https://identity-server.staging01.devland.is', - prod: 'https://innskra.island.is', - }, SI_PUBLIC_ENVIRONMENT: ref((h) => h.env.type), }) .secrets({ diff --git a/apps/portals/admin/project.json b/apps/portals/admin/project.json index 53dda7697770..ae909c4cae73 100644 --- a/apps/portals/admin/project.json +++ b/apps/portals/admin/project.json @@ -111,7 +111,7 @@ ] } }, - "mockmode": { + "mock": { "executor": "nx:run-commands", "options": { "commands": ["API_MOCKS=true yarn start portals-admin"] diff --git a/apps/portals/admin/src/app/App.tsx b/apps/portals/admin/src/app/App.tsx index 1627e3b6aa17..64185f145153 100644 --- a/apps/portals/admin/src/app/App.tsx +++ b/apps/portals/admin/src/app/App.tsx @@ -1,4 +1,5 @@ import { ApolloProvider } from '@apollo/client' +import { adminPortalScopes } from '@island.is/auth/scopes' import { LocaleProvider } from '@island.is/localization' import { ApplicationErrorBoundary, @@ -13,7 +14,6 @@ import { client } from '../graphql' import { modules } from '../lib/modules' import { AdminPortalPaths } from '../lib/paths' import { createRoutes } from '../lib/routes' -import { adminPortalScopes } from '@island.is/auth/scopes' const mockedInitialState = isMockMode ? createMockedInitialState({ @@ -27,6 +27,7 @@ export const App = () => ( diff --git a/apps/portals/admin/src/environments/environment.ts b/apps/portals/admin/src/environments/environment.ts index 7e7c859951dc..0d86df13872c 100644 --- a/apps/portals/admin/src/environments/environment.ts +++ b/apps/portals/admin/src/environments/environment.ts @@ -2,9 +2,6 @@ import { getStaticEnv } from '@island.is/shared/utils' const devConfig = { production: false, - identityServer: { - authority: 'https://identity-server.dev01.devland.is', - }, featureFlagSdkKey: 'YcfYCOwBTUeI04mWOWpPdA/KgCHhUk0_k2BdiKMaNh3qA', DD_RUM_CLIENT_TOKEN: 'unknown', DD_RUM_APPLICATION_ID: 'unknown', @@ -14,9 +11,6 @@ const devConfig = { const prodConfig = { production: true, - identityServer: { - authority: getStaticEnv('SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL'), - }, featureFlagSdkKey: getStaticEnv('SI_PUBLIC_CONFIGCAT_SDK_KEY'), DD_RUM_CLIENT_TOKEN: getStaticEnv('SI_PUBLIC_DD_RUM_CLIENT_TOKEN'), DD_RUM_APPLICATION_ID: getStaticEnv('SI_PUBLIC_DD_RUM_APPLICATION_ID'), diff --git a/apps/portals/my-pages/infra/portals-my-pages.ts b/apps/portals/my-pages/infra/portals-my-pages.ts index 52cdc8d58a3b..1c55fd15ae0d 100644 --- a/apps/portals/my-pages/infra/portals-my-pages.ts +++ b/apps/portals/my-pages/infra/portals-my-pages.ts @@ -1,8 +1,6 @@ import { ref, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' -export const serviceSetup = (services: { - graphql: ServiceBuilder<'api'> -}): ServiceBuilder<'service-portal'> => +export const serviceSetup = (): ServiceBuilder<'service-portal'> => service('service-portal') .namespace('service-portal') .liveness('/liveness') @@ -18,18 +16,7 @@ export const serviceSetup = (services: { }) .env({ BASEPATH: '/minarsidur', - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: { - dev: 'https://identity-server.dev01.devland.is', - staging: 'https://identity-server.staging01.devland.is', - prod: 'https://innskra.island.is', - }, SI_PUBLIC_ENVIRONMENT: ref((h) => h.env.type), - SI_PUBLIC_GRAPHQL_API: { - prod: '/api/graphql', - staging: '/api/graphql', - dev: '/api/graphql', - local: ref((h) => `http://${h.svc(services.graphql)}/api/graphql`), - }, }) .secrets({ SI_PUBLIC_CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY', diff --git a/apps/portals/my-pages/project.json b/apps/portals/my-pages/project.json index 82fe67a9a4df..285c3d1a352e 100644 --- a/apps/portals/my-pages/project.json +++ b/apps/portals/my-pages/project.json @@ -62,7 +62,8 @@ "serve": { "executor": "@nx/webpack:dev-server", "options": { - "buildTarget": "service-portal:build" + "buildTarget": "service-portal:build", + "proxyConfig": "apps/portals/my-pages/proxy.config.json" }, "configurations": { "production": { @@ -101,13 +102,31 @@ "parallel": false } }, + "start-bff": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "node -r esbuild-register src/cli/cli.ts run-local-env services-bff-portals-my-pages" + ], + "cwd": "infra" + } + }, "dev": { "executor": "nx:run-commands", "options": { - "commands": ["yarn start service-portal"], + "commands": [ + "yarn nx run service-portal:start-bff", + "yarn start service-portal" + ], "parallel": true } }, + "mock": { + "executor": "nx:run-commands", + "options": { + "commands": ["API_MOCKS=true yarn start service-portal"] + } + }, "docker-static": { "executor": "Intentionally left blank, only so this target is valid when using `nx show projects --with-target docker-static`" } diff --git a/apps/portals/my-pages/proxy.config.json b/apps/portals/my-pages/proxy.config.json new file mode 100644 index 000000000000..af8396a18f6d --- /dev/null +++ b/apps/portals/my-pages/proxy.config.json @@ -0,0 +1,6 @@ +{ + "/bff/*": { + "target": "http://localhost:3010", + "secure": false + } +} diff --git a/apps/portals/my-pages/src/Main.tsx b/apps/portals/my-pages/src/Main.tsx index c03f0404169b..6a779b2cc6a9 100644 --- a/apps/portals/my-pages/src/Main.tsx +++ b/apps/portals/my-pages/src/Main.tsx @@ -1,13 +1,12 @@ import '@island.is/api/mocks' -import React, { StrictMode } from 'react' +import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' -import { userMonitoring } from '@island.is/user-monitoring' import { isRunningOnEnvironment } from '@island.is/shared/utils' +import { userMonitoring } from '@island.is/user-monitoring' -import './auth' -import { environment } from './environments' import { App } from './app/App' +import { environment } from './environments' if (!isRunningOnEnvironment('local')) { userMonitoring.initDdRum({ diff --git a/apps/portals/my-pages/src/app/App.tsx b/apps/portals/my-pages/src/app/App.tsx index 1d16f6f017dc..661fa763ab1b 100644 --- a/apps/portals/my-pages/src/app/App.tsx +++ b/apps/portals/my-pages/src/app/App.tsx @@ -1,21 +1,35 @@ -import { AuthProvider } from '@island.is/auth/react' import { ApolloProvider } from '@apollo/client' -import { client } from '@island.is/portals/my-pages/graphql' +import { servicePortalScopes } from '@island.is/auth/scopes' import { LocaleProvider } from '@island.is/localization' -import { defaultLanguage } from '@island.is/shared/constants' +import { + ApplicationErrorBoundary, + PortalRouter, + isMockMode, +} from '@island.is/portals/core' import { ServicePortalPaths } from '@island.is/portals/my-pages/core' +import { client } from '@island.is/portals/my-pages/graphql' +import { BffProvider, createMockedInitialState } from '@island.is/react-spa/bff' import { FeatureFlagProvider } from '@island.is/react/feature-flags' -import { ApplicationErrorBoundary, PortalRouter } from '@island.is/portals/core' +import { defaultLanguage } from '@island.is/shared/constants' +import { environment } from '../environments' import { modules } from '../lib/modules' import { createRoutes } from '../lib/routes' -import { environment } from '../environments' import * as styles from './App.css' +const mockedInitialState = isMockMode + ? createMockedInitialState({ + scopes: servicePortalScopes, + }) + : undefined + export const App = () => (
- + ( /> - +
diff --git a/apps/portals/my-pages/src/auth.ts b/apps/portals/my-pages/src/auth.ts deleted file mode 100644 index d3ea87433d36..000000000000 --- a/apps/portals/my-pages/src/auth.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { configure, configureMock } from '@island.is/auth/react' -import { - ApiScope, - ApplicationScope, - AuthScope, - DocumentsScope, - EndorsementsScope, - HmsScope, - NationalRegistryScope, - UserProfileScope, -} from '@island.is/auth/scopes' - -import { environment } from './environments' - -const SERVICE_PORTAL_SCOPES = [ - 'openid', - 'profile', - 'api_resource.scope', - ApplicationScope.read, - ApplicationScope.write, - UserProfileScope.read, - UserProfileScope.write, - AuthScope.actorDelegations, - AuthScope.delegations, - AuthScope.consents, - NationalRegistryScope.individuals, - DocumentsScope.main, - EndorsementsScope.main, - EndorsementsScope.admin, - ApiScope.intellectualProperties, - ApiScope.assets, - ApiScope.education, - ApiScope.educationLicense, - ApiScope.financeOverview, - ApiScope.financeSalary, - ApiScope.financeSchedule, - ApiScope.financeLoans, - ApiScope.internal, - ApiScope.internalProcuring, - ApiScope.meDetails, - ApiScope.lawAndOrder, - ApiScope.licenses, - ApiScope.licensesVerify, - ApiScope.company, - ApiScope.vehicles, - ApiScope.workMachines, - ApiScope.healthPayments, - ApiScope.healthMedicines, - ApiScope.healthAssistiveAndNutrition, - ApiScope.healthTherapies, - ApiScope.healthHealthcare, - ApiScope.healthRightsStatus, - ApiScope.healthDentists, - ApiScope.healthOrganDonation, - ApiScope.healthVaccinations, - ApiScope.signatureCollection, -] - -const userMocked = process.env.API_MOCKS === 'true' - -if (userMocked) { - configureMock({ - profile: { name: 'Mock', locale: 'is', nationalId: '0000000000' }, - scopes: SERVICE_PORTAL_SCOPES, - }) -} else { - configure({ - baseUrl: `${window.location.origin}/minarsidur`, - redirectPath: '/signin-oidc', - redirectPathSilent: '/silent/signin-oidc', - initiateLoginPath: '/login', - switchUserRedirectUrl: '/', - authority: environment.identityServer.authority, - client_id: '@island.is/web', - scope: SERVICE_PORTAL_SCOPES, - post_logout_redirect_uri: `${window.location.origin}`, - userStorePrefix: 'sp.', - }) -} diff --git a/apps/portals/my-pages/src/components/Greeting/Greeting.tsx b/apps/portals/my-pages/src/components/Greeting/Greeting.tsx index 39c3afdf4d14..cd14d859f790 100644 --- a/apps/portals/my-pages/src/components/Greeting/Greeting.tsx +++ b/apps/portals/my-pages/src/components/Greeting/Greeting.tsx @@ -1,4 +1,3 @@ -import React, { FC } from 'react' import { Box, GridColumn, @@ -8,13 +7,13 @@ import { Text, } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { useAuth } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { m } from '@island.is/portals/my-pages/core' import * as styles from './Greeting.css' -const Greeting: FC> = () => { +const Greeting = () => { const { formatMessage } = useLocale() - const { userInfo } = useAuth() + const userInfo = useUserInfo() const currentHour = new Date().getHours() const isEveningGreeting = currentHour > 17 || currentHour < 4 diff --git a/apps/portals/my-pages/src/components/Header/Header.tsx b/apps/portals/my-pages/src/components/Header/Header.tsx index e8b8485f2ba1..2439ea377f9b 100644 --- a/apps/portals/my-pages/src/components/Header/Header.tsx +++ b/apps/portals/my-pages/src/components/Header/Header.tsx @@ -1,4 +1,4 @@ -import { useAuth } from '@island.is/auth/react' +import { DocumentsScope } from '@island.is/auth/scopes' import { Box, Button, @@ -12,6 +12,7 @@ import { import { helperStyles, theme } from '@island.is/island-ui/theme' import { useLocale } from '@island.is/localization' import { PortalPageLoader } from '@island.is/portals/core' +import { useUserInfo } from '@island.is/react-spa/bff' import { useFeatureFlagClient } from '@island.is/react/feature-flags' import { LinkResolver, @@ -26,7 +27,6 @@ import { useWindowSize } from 'react-use' import NotificationButton from '../Notifications/NotificationButton' import Sidemenu from '../Sidemenu/Sidemenu' import * as styles from './Header.css' -import { DocumentsScope } from '@island.is/auth/scopes' export type MenuTypes = 'side' | 'user' | 'notifications' | undefined interface Props { @@ -38,7 +38,7 @@ export const Header = ({ position }: Props) => { const { width } = useWindowSize() const ref = useRef(null) const isMobile = width < theme.breakpoints.md - const { userInfo: user } = useAuth() + const user = useUserInfo() // Notification feature flag. Remove after feature is live. const [enableNotificationFlag, setEnableNotificationFlag] = diff --git a/apps/portals/my-pages/src/components/Layout/FullWidthLayout.tsx b/apps/portals/my-pages/src/components/Layout/FullWidthLayout.tsx index 72cf6d83093c..dda69012b523 100644 --- a/apps/portals/my-pages/src/components/Layout/FullWidthLayout.tsx +++ b/apps/portals/my-pages/src/components/Layout/FullWidthLayout.tsx @@ -22,9 +22,9 @@ import { import { Link, matchPath, useNavigate } from 'react-router-dom' import { DocumentsPaths } from '@island.is/portals/my-pages/documents' import { theme } from '@island.is/island-ui/theme' -import { useAuth } from '@island.is/auth/react' import { DocumentsScope } from '@island.is/auth/scopes' import { FinancePaths } from '@island.is/portals/my-pages/finance' +import { useUserInfo } from '@island.is/react-spa/bff' interface FullWidthLayoutWrapperProps { activeParent?: PortalNavigationItem @@ -49,7 +49,7 @@ export const FullWidthLayout: FC = ({ }) => { const navigate = useNavigate() const { formatMessage } = useLocale() - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [navItems, setNavItems] = useState() useEffect(() => { diff --git a/apps/portals/my-pages/src/components/Loaders/AuthOverlay/AuthOverlay.tsx b/apps/portals/my-pages/src/components/Loaders/AuthOverlay/AuthOverlay.tsx index b7cafb6566e7..79e6410f2d8f 100644 --- a/apps/portals/my-pages/src/components/Loaders/AuthOverlay/AuthOverlay.tsx +++ b/apps/portals/my-pages/src/components/Loaders/AuthOverlay/AuthOverlay.tsx @@ -1,12 +1,12 @@ -import { useAuth } from '@island.is/auth/react' import { Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { m } from '@island.is/portals/my-pages/core' +import { useBff } from '@island.is/react-spa/bff' import * as styles from './AuthOverlay.css' const AuthOverlay = () => { - const { authState } = useAuth() + const { authState } = useBff() const { formatMessage } = useLocale() if (authState === 'switching') diff --git a/apps/portals/my-pages/src/environments/environment.ts b/apps/portals/my-pages/src/environments/environment.ts index 7e7c859951dc..0d86df13872c 100644 --- a/apps/portals/my-pages/src/environments/environment.ts +++ b/apps/portals/my-pages/src/environments/environment.ts @@ -2,9 +2,6 @@ import { getStaticEnv } from '@island.is/shared/utils' const devConfig = { production: false, - identityServer: { - authority: 'https://identity-server.dev01.devland.is', - }, featureFlagSdkKey: 'YcfYCOwBTUeI04mWOWpPdA/KgCHhUk0_k2BdiKMaNh3qA', DD_RUM_CLIENT_TOKEN: 'unknown', DD_RUM_APPLICATION_ID: 'unknown', @@ -14,9 +11,6 @@ const devConfig = { const prodConfig = { production: true, - identityServer: { - authority: getStaticEnv('SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL'), - }, featureFlagSdkKey: getStaticEnv('SI_PUBLIC_CONFIGCAT_SDK_KEY'), DD_RUM_CLIENT_TOKEN: getStaticEnv('SI_PUBLIC_DD_RUM_CLIENT_TOKEN'), DD_RUM_APPLICATION_ID: getStaticEnv('SI_PUBLIC_DD_RUM_APPLICATION_ID'), diff --git a/apps/portals/my-pages/src/screens/Dashboard/Dashboard.tsx b/apps/portals/my-pages/src/screens/Dashboard/Dashboard.tsx index 048547bed673..3fc1296ccd3c 100644 --- a/apps/portals/my-pages/src/screens/Dashboard/Dashboard.tsx +++ b/apps/portals/my-pages/src/screens/Dashboard/Dashboard.tsx @@ -1,6 +1,4 @@ -import React, { FC, useEffect, useState } from 'react' -import { Link, useLocation } from 'react-router-dom' -import { useAuth } from '@island.is/auth/react' +import { DocumentsScope } from '@island.is/auth/scopes' import { Box, Button, @@ -12,13 +10,8 @@ import { SkeletonLoader, Text, } from '@island.is/island-ui/core' +import { theme } from '@island.is/island-ui/theme' import { useLocale } from '@island.is/localization' -import { - DocumentsPaths, - DocumentLine, - DocumentLineV3, - useDocumentListV3, -} from '@island.is/portals/my-pages/documents' import { LinkResolver, PlausiblePageviewDetail, @@ -26,21 +19,28 @@ import { m, useDynamicRoutesWithNavigation, } from '@island.is/portals/my-pages/core' -import Greeting from '../../components/Greeting/Greeting' -import DocumentsEmpty from '../../components/DocumentsEmpty/DocumentsEmpty' -import { iconIdMapper, iconTypeToSVG } from '../../utils/Icons/idMapper' +import { + DocumentLine, + DocumentLineV3, + DocumentsPaths, + useDocumentListV3, +} from '@island.is/portals/my-pages/documents' +import { useOrganizations } from '@island.is/portals/my-pages/graphql' +import { useUserInfo } from '@island.is/react-spa/bff' +import { useFeatureFlagClient } from '@island.is/react/feature-flags' +import { getOrganizationLogoUrl } from '@island.is/shared/utils' +import cn from 'classnames' +import { useEffect, useState } from 'react' +import { Link, useLocation } from 'react-router-dom' import { useWindowSize } from 'react-use' -import { theme } from '@island.is/island-ui/theme' +import DocumentsEmpty from '../../components/DocumentsEmpty/DocumentsEmpty' +import Greeting from '../../components/Greeting/Greeting' import { MAIN_NAVIGATION } from '../../lib/masterNavigation' -import { useOrganizations } from '@island.is/portals/my-pages/graphql' +import { iconIdMapper, iconTypeToSVG } from '../../utils/Icons/idMapper' import * as styles from './Dashboard.css' -import cn from 'classnames' -import { getOrganizationLogoUrl } from '@island.is/shared/utils' -import { DocumentsScope } from '@island.is/auth/scopes' -import { useFeatureFlagClient } from '@island.is/react/feature-flags' -export const Dashboard: FC> = () => { - const { userInfo } = useAuth() +export const Dashboard = () => { + const userInfo = useUserInfo() const { data: organizations } = useOrganizations() const { formatMessage } = useLocale() diff --git a/apps/services/bff/infra/admin-portal.infra.ts b/apps/services/bff/infra/admin-portal.infra.ts index 9ed4111a149e..b2cddaf38119 100644 --- a/apps/services/bff/infra/admin-portal.infra.ts +++ b/apps/services/bff/infra/admin-portal.infra.ts @@ -28,6 +28,7 @@ export const serviceSetup = ( clientId: `@admin.island.is/bff-${key}`, clientName, services, + globalPrefix: `/${key}/bff`, }) .readiness(`/${key}/bff/health/check`) .liveness(`/${key}/bff/liveness`) @@ -69,7 +70,7 @@ export const serviceSetup = ( 'nginx.ingress.kubernetes.io/proxy-buffer-size': '8k', }, }, - paths: ['/stjornbord/bff'], + paths: [`/${key}/bff`], }, }) .grantNamespaces('identity-server') diff --git a/apps/services/bff/infra/my-pages-portal.infra.ts b/apps/services/bff/infra/my-pages-portal.infra.ts new file mode 100644 index 000000000000..67792fbb74d0 --- /dev/null +++ b/apps/services/bff/infra/my-pages-portal.infra.ts @@ -0,0 +1,75 @@ +import { ServiceBuilder, json, service } from '../../../../infra/src/dsl/dsl' +import { BffInfraServices } from '../../../../infra/src/dsl/types/input-types' + +const bffName = 'services-bff' +const clientName = 'portals-my-pages' +const serviceName = `${bffName}-${clientName}` +const key = 'minarsidur' + +export const serviceSetup = ( + services: BffInfraServices, +): ServiceBuilder => + service(serviceName) + .namespace(clientName) + .image(bffName) + .redis() + .serviceAccount(bffName) + .env({ + BFF_ALLOWED_EXTERNAL_API_URLS: { + local: json(['http://localhost:3377/download/v1']), + dev: json(['https://api.dev01.devland.is']), + staging: json(['https://api.staging01.devland.is']), + prod: json(['https://api.island.is']), + }, + }) + .bff({ + key, + clientId: '@island.is/web', + clientName, + services, + globalPrefix: '/bff', + }) + .readiness('/bff/health/check') + .liveness('/bff/liveness') + .replicaCount({ + default: 2, + min: 2, + max: 10, + }) + .resources({ + limits: { + cpu: '400m', + memory: '512Mi', + }, + requests: { + cpu: '100m', + memory: '256Mi', + }, + }) + .ingress({ + primary: { + host: { + dev: ['beta'], + staging: ['beta'], + prod: ['', 'www.island.is'], + }, + extraAnnotations: { + dev: { + 'nginx.ingress.kubernetes.io/enable-global-auth': 'false', + 'nginx.ingress.kubernetes.io/proxy-buffering': 'on', + 'nginx.ingress.kubernetes.io/proxy-buffer-size': '8k', + }, + staging: { + 'nginx.ingress.kubernetes.io/enable-global-auth': 'false', + 'nginx.ingress.kubernetes.io/proxy-buffering': 'on', + 'nginx.ingress.kubernetes.io/proxy-buffer-size': '8k', + }, + prod: { + 'nginx.ingress.kubernetes.io/proxy-buffering': 'on', + 'nginx.ingress.kubernetes.io/proxy-buffer-size': '8k', + }, + }, + paths: ['/bff'], + }, + }) + .grantNamespaces('identity-server') diff --git a/apps/services/bff/src/app/bff.config.ts b/apps/services/bff/src/app/bff.config.ts index a4fd95d97d40..191d56696890 100644 --- a/apps/services/bff/src/app/bff.config.ts +++ b/apps/services/bff/src/app/bff.config.ts @@ -11,6 +11,10 @@ export const idsSchema = z.strictObject({ }) const BffConfigSchema = z.object({ + /** + * Unique name of the BFF + */ + name: z.string(), /** * Redis configuration */ @@ -28,6 +32,10 @@ const BffConfigSchema = z.object({ * Bff client base URL */ clientBaseUrl: z.string(), + /** + * Bff client base path (without the domain) for app communicating with the BFF, e.g. /minarsidur + */ + clientBasePath: z.string(), ids: idsSchema, /** * The base64 encoded secret used for encrypting and decrypting tokens. @@ -70,8 +78,10 @@ export const BffConfig = defineConfig({ const bffName = env.required('BFF_NAME') return { + name: bffName, parSupportEnabled: env.optionalJSON('BFF_PAR_SUPPORT_ENABLED') ?? false, clientBaseUrl: env.required('BFF_CLIENT_BASE_URL'), + clientBasePath: env.required('BFF_CLIENT_BASE_PATH'), logoutRedirectUri: env.required('BFF_LOGOUT_REDIRECT_URI'), /** * Our main GraphQL API endpoint diff --git a/apps/services/bff/src/app/modules/auth/auth.controller.spec.ts b/apps/services/bff/src/app/modules/auth/auth.controller.spec.ts index 642a1748df73..ed71a9b0a393 100644 --- a/apps/services/bff/src/app/modules/auth/auth.controller.spec.ts +++ b/apps/services/bff/src/app/modules/auth/auth.controller.spec.ts @@ -11,7 +11,6 @@ import { getLoginSearchParmsFn, mockedTokensResponse as tokensResponse, } from '../../../../test/sharedConstants' -import { environment } from '../../../environment' import { BffConfig } from '../../bff.config' import { IdsService } from '../ids/ids.service' import { ParResponse } from '../ids/ids.types' @@ -82,7 +81,7 @@ describe('AuthController', () => { }) mockConfig = app.get>(BffConfig.KEY) - baseUrlWithKey = `${mockConfig.clientBaseUrl}${environment.keyPath}` + baseUrlWithKey = `${mockConfig.clientBaseUrl}${mockConfig.clientBasePath}` server = request(app.getHttpServer()) }) @@ -114,7 +113,7 @@ describe('AuthController', () => { const [key, value] = setSpy.mock.calls[0] - expect(key).toEqual(`attempt_${SID_VALUE}`) + expect(key).toEqual(`attempt::${mockConfig.name}::${SID_VALUE}`) expect(value).toMatchObject({ originUrl: baseUrlWithKey, codeVerifier: expect.any(String), @@ -312,7 +311,9 @@ describe('AuthController', () => { const loginAttempt = setCacheSpy.mock.calls[0] // Assert - First request should cache the login attempt - expect(setCacheSpy.mock.calls[0]).toContain(`attempt_${SID_VALUE}`) + expect(setCacheSpy.mock.calls[0]).toContain( + `attempt::${mockConfig.name}::${SID_VALUE}`, + ) expect(loginAttempt[1]).toMatchObject({ originUrl: baseUrlWithKey, codeVerifier: expect.any(String), @@ -330,7 +331,9 @@ describe('AuthController', () => { // Assert expect(setCacheSpy).toHaveBeenCalled() - expect(currentLogin[0]).toContain(`current_${SID_VALUE}`) + expect(currentLogin[0]).toContain( + `current::${mockConfig.name}::${SID_VALUE}`, + ) // Check if the cache contains the correct values for the current login expect(currentLogin[1]).toMatchObject(tokensResponse) diff --git a/apps/services/bff/src/app/modules/auth/auth.service.ts b/apps/services/bff/src/app/modules/auth/auth.service.ts index b3472b86f688..4c8ee5b5b02b 100644 --- a/apps/services/bff/src/app/modules/auth/auth.service.ts +++ b/apps/services/bff/src/app/modules/auth/auth.service.ts @@ -16,7 +16,6 @@ import { jwtDecode } from 'jwt-decode' import { IdTokenClaims } from '@island.is/shared/types' import { Algorithm, decode, verify } from 'jsonwebtoken' import { v4 as uuid } from 'uuid' -import { environment } from '../../../environment' import { BffConfig } from '../../bff.config' import { SESSION_COOKIE_NAME } from '../../constants/cookies' import { FIVE_SECONDS_IN_MS } from '../../constants/time' @@ -31,7 +30,7 @@ import { validateUri } from '../../utils/validate-uri' import { CacheService } from '../cache/cache.service' import { IdsService } from '../ids/ids.service' import { LogoutTokenPayload, TokenResponse } from '../ids/ids.types' -import { CachedTokenResponse } from './auth.types' +import { CachedTokenResponse, LoginAttemptData } from './auth.types' import { CallbackLoginDto } from './dto/callback-login.dto' import { CallbackLogoutDto } from './dto/callback-logout.dto' import { LoginDto } from './dto/login.dto' @@ -61,7 +60,7 @@ export class AuthService { */ private createClientBaseUrl() { const baseUrl = new URL(this.config.clientBaseUrl) - baseUrl.pathname = `${baseUrl.pathname}${environment.keyPath}` + baseUrl.pathname = `${baseUrl.pathname}${this.config.clientBasePath}` // Prevent potential issues with malformed URLs. .replace('//', '/') @@ -71,8 +70,13 @@ export class AuthService { /** * Redirects the user to the client base URL with an error query string. */ - private createClientBaseUrlWithError(args: CreateErrorQueryStrArgs) { - return `${this.createClientBaseUrl()}?${createErrorQueryStr(args)}` + private createClientBaseUrlWithError( + args: CreateErrorQueryStrArgs, + targetUrl?: string, + ) { + const baseUrl = targetUrl ?? this.createClientBaseUrl() + + return `${baseUrl}?${createErrorQueryStr(args)}` } /** @@ -80,12 +84,14 @@ export class AuthService { */ private redirectWithError( res: Response, - args?: Partial, + args?: Partial & { targetUrl?: string }, ) { const code = args?.code || 500 const error = args?.error || 'Login failed!' - return res.redirect(this.createClientBaseUrlWithError({ code, error })) + return res.redirect( + this.createClientBaseUrlWithError({ code, error }, args?.targetUrl), + ) } /** @@ -223,7 +229,9 @@ export class AuthService { } catch (error) { this.logger.error('Login failed: ', error) - return this.redirectWithError(res) + return this.redirectWithError(res, { + targetUrl: targetLinkUri, + }) } } @@ -269,13 +277,13 @@ export class AuthService { }) } + let loginAttemptData: LoginAttemptData | undefined + try { // Get login attempt from cache - const loginAttemptData = await this.cacheService.get<{ - targetLinkUri?: string - codeVerifier: string - originUrl: string - }>(this.cacheService.createSessionKeyType('attempt', query.state)) + loginAttemptData = await this.cacheService.get( + this.cacheService.createSessionKeyType('attempt', query.state), + ) // Get tokens and user information from the authorization code const tokenResponse = await this.idsService.getTokens({ @@ -339,7 +347,9 @@ export class AuthService { } catch (error) { this.logger.error('Callback login failed: ', error) - return this.redirectWithError(res) + return this.redirectWithError(res, { + targetUrl: loginAttemptData?.targetLinkUri, + }) } } diff --git a/apps/services/bff/src/app/modules/auth/auth.types.ts b/apps/services/bff/src/app/modules/auth/auth.types.ts index e57e52298603..ecc7a9f26136 100644 --- a/apps/services/bff/src/app/modules/auth/auth.types.ts +++ b/apps/services/bff/src/app/modules/auth/auth.types.ts @@ -21,3 +21,9 @@ export type CachedTokenResponse = Omit< encryptedAccessToken: string encryptedRefreshToken: string } + +export type LoginAttemptData = { + targetLinkUri?: string + codeVerifier: string + originUrl: string +} diff --git a/apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts b/apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts index 8b1dc46546e5..84d091fb0b75 100644 --- a/apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts +++ b/apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts @@ -2,7 +2,6 @@ import { LOGGER_PROVIDER } from '@island.is/logging' import { Test } from '@nestjs/testing' import { CacheService } from '../cache/cache.service' import { IdsService } from '../ids/ids.service' -import { TokenResponse } from '../ids/ids.types' import { AuthService } from './auth.service' import { CachedTokenResponse } from './auth.types' import { TokenRefreshService } from './token-refresh.service' @@ -35,22 +34,13 @@ const mockTokenResponse: CachedTokenResponse = { delegationType: [], locale: 'is', birthdate: '1990-01-01', + iss: 'https://identity-server.dev01.devland.is', }, accessTokenExp: Date.now() + 3600000, // Current time + 1 hour in milliseconds encryptedAccessToken: 'encrypted.access.token', encryptedRefreshToken: 'encrypted.refresh.token', } -// When mocking IdsService.refreshToken response, we need TokenResponse type: -const mockIdsTokenResponse: TokenResponse = { - id_token: 'mock.id.token', - access_token: 'mock.access.token', - refresh_token: 'mock.refresh.token', - expires_in: 3600, - token_type: 'Bearer', - scope: 'openid profile offline_access', -} - describe('TokenRefreshService', () => { let service: TokenRefreshService let authService: AuthService diff --git a/apps/services/bff/src/app/modules/cache/cache.service.ts b/apps/services/bff/src/app/modules/cache/cache.service.ts index 9e2f1b246c3b..e5888f9b2c93 100644 --- a/apps/services/bff/src/app/modules/cache/cache.service.ts +++ b/apps/services/bff/src/app/modules/cache/cache.service.ts @@ -2,12 +2,17 @@ import { Inject, Injectable } from '@nestjs/common' import { Cache as CacheManager } from 'cache-manager' import { CACHE_MANAGER } from '@nestjs/cache-manager' +import { ConfigType } from '@nestjs/config' +import { BffConfig } from '../../bff.config' @Injectable() export class CacheService { constructor( @Inject(CACHE_MANAGER) private readonly cacheManager: CacheManager, + + @Inject(BffConfig.KEY) + private readonly config: ConfigType, ) {} public createKeyError(key: string) { @@ -15,13 +20,17 @@ export class CacheService { } /** - * Creates s unique key with session id. + * Creates s unique key with Bff name and session id. * Type is either 'attempt' or 'current'. * `attempt` represents the login attempt. * `current` represents the current login session. + * + * @example + * createSessionKeyType('attempt', '1234') // attempt::{bffName}::1234 + * createSessionKeyType('current', '1234') // current::{bffName}::1234 */ public createSessionKeyType(type: 'attempt' | 'current', sid: string) { - return `${type}_${sid}` + return `${type}::${this.config.name}::${sid}` } public async save({ diff --git a/apps/services/bff/src/app/modules/proxy/proxy.controller.ts b/apps/services/bff/src/app/modules/proxy/proxy.controller.ts index 74af02361ec6..9ad9d8e017b8 100644 --- a/apps/services/bff/src/app/modules/proxy/proxy.controller.ts +++ b/apps/services/bff/src/app/modules/proxy/proxy.controller.ts @@ -1,4 +1,5 @@ import { + Body, Controller, Get, Post, @@ -9,8 +10,8 @@ import { } from '@nestjs/common' import { Request, Response } from 'express' import { qsValidationPipe } from '../../utils/qs-validation-pipe' -import { ProxyService } from './proxy.service' import { ApiProxyDto } from './dto/api-proxy.dto' +import { ProxyService } from './proxy.service' @Controller({ path: 'api', @@ -29,6 +30,17 @@ export class ProxyController { return this.proxyService.forwardGetApiRequest({ req, res, query }) } + @Post() + async forwardPostApiRequest( + @Req() req: Request, + @Res() res: Response, + @Query(qsValidationPipe) + query: ApiProxyDto, + @Body() body: Record, + ): Promise { + return this.proxyService.forwardPostApiRequest({ req, res, query, body }) + } + @Post('/graphql') async proxyGraphqlRequest( @Req() req: Request, diff --git a/apps/services/bff/src/app/modules/proxy/proxy.service.ts b/apps/services/bff/src/app/modules/proxy/proxy.service.ts index 0f7244ca7de6..27c6536db887 100644 --- a/apps/services/bff/src/app/modules/proxy/proxy.service.ts +++ b/apps/services/bff/src/app/modules/proxy/proxy.service.ts @@ -146,13 +146,15 @@ export class ProxyService { }) { try { const reqHeaderContentType = req.headers['content-type'] + const finalBody = body ?? req.body + const response = await fetch(targetUrl, { method: 'POST', headers: { 'Content-Type': reqHeaderContentType || 'application/json', Authorization: `Bearer ${accessToken}`, }, - body: JSON.stringify(body ?? req.body), + body: finalBody ? JSON.stringify(finalBody) : undefined, agent: (parsedUrl) => { if (parsedUrl.protocol == 'http:') { return customAgent @@ -226,6 +228,27 @@ export class ProxyService { }) } + /** + * Prepares the request for proxying to an external API by validating the URL and getting the access token. + */ + private prepareApiProxyRequest({ + req, + res, + url, + }: { + req: Request + res: Response + url: string + }): Promise { + if (!validateUri(url, this.config.allowedExternalApiUrls)) { + this.logger.error('Invalid external api url provided:', url) + + throw new BadRequestException('Proxing url failed!') + } + + return this.getAccessToken({ req, res }) + } + /** * Forwards an incoming HTTP GET request to the specified URL (provided in the query string), * managing authentication, refreshing tokens if needed, and streaming the response back to the client. @@ -240,20 +263,40 @@ export class ProxyService { query: ApiProxyDto }): Promise { const { url } = query + const accessToken = await this.prepareApiProxyRequest({ req, res, url }) - if (!validateUri(url, this.config.allowedExternalApiUrls)) { - this.logger.error('Invalid external api url provided:', url) - - throw new BadRequestException('Proxying url failed!') - } + this.executeStreamRequest({ + accessToken, + targetUrl: url, + req, + res, + }) + } - const accessToken = await this.getAccessToken({ req, res }) + /** + * Forwards an incoming HTTP POST request to the specified URL (provided in the query string), + * managing authentication, refreshing tokens if needed, and streaming the response back to the client. + */ + async forwardPostApiRequest({ + req, + res, + query, + body, + }: { + req: Request + res: Response + query: ApiProxyDto + body: Record + }) { + const { url } = query + const accessToken = await this.prepareApiProxyRequest({ req, res, url }) this.executeStreamRequest({ accessToken, targetUrl: url, req, res, + body, }) } } diff --git a/apps/services/bff/src/app/modules/user/user.controller.spec.ts b/apps/services/bff/src/app/modules/user/user.controller.spec.ts index 4a2ca620d94b..dba1a6918e0a 100644 --- a/apps/services/bff/src/app/modules/user/user.controller.spec.ts +++ b/apps/services/bff/src/app/modules/user/user.controller.spec.ts @@ -10,7 +10,6 @@ import { getLoginSearchParmsFn, mockedTokensResponse as tokensResponse, } from '../../../../test/sharedConstants' -import { environment } from '../../../environment' import { BffConfig } from '../../bff.config' import { TokenRefreshService } from '../auth/token-refresh.service' import { IdsService } from '../ids/ids.service' @@ -62,7 +61,7 @@ const mockIdsService = { } const createLoginAttempt = (mockConfig: ConfigType) => ({ - originUrl: `${mockConfig.clientBaseUrl}${environment.keyPath}`, + originUrl: `${mockConfig.clientBaseUrl}${mockConfig.clientBasePath}`, codeVerifier: 'test_code_verifier', targetLinkUri: undefined, }) @@ -120,7 +119,10 @@ describe('UserController', () => { it('should return user data when valid session exists', async () => { // Arrange - Set up login attempt in cache - mockCacheStore.set(`attempt_${SID_VALUE}`, createLoginAttempt(mockConfig)) + mockCacheStore.set( + `attempt::${mockConfig.name}::${SID_VALUE}`, + createLoginAttempt(mockConfig), + ) // Initialize session with login await server.get('/login') @@ -150,7 +152,10 @@ describe('UserController', () => { it('should refresh token when access token is expired and refresh=true', async () => { // Arrange - Set up login attempt in cache - mockCacheStore.set(`attempt_${SID_VALUE}`, createLoginAttempt(mockConfig)) + mockCacheStore.set( + `attempt::${mockConfig.name}::${SID_VALUE}`, + createLoginAttempt(mockConfig), + ) // Initialize session await server.get('/login') @@ -164,7 +169,10 @@ describe('UserController', () => { ...mockCachedTokenResponse, accessTokenExp: Date.now() - 1000, // Expired token } - mockCacheStore.set(`current_${SID_VALUE}`, expiredTokenResponse) + mockCacheStore.set( + `current::${mockConfig.name}::${SID_VALUE}`, + expiredTokenResponse, + ) // Act const res = await server @@ -186,7 +194,10 @@ describe('UserController', () => { it('should not refresh token when access token is expired but refresh=false', async () => { // Arrange - Set up login attempt in cache - mockCacheStore.set(`attempt_${SID_VALUE}`, createLoginAttempt(mockConfig)) + mockCacheStore.set( + `attempt::${mockConfig.name}::${SID_VALUE}`, + createLoginAttempt(mockConfig), + ) // Initialize session await server.get('/login') @@ -200,7 +211,10 @@ describe('UserController', () => { ...mockCachedTokenResponse, accessTokenExp: Date.now() - 1000, // Expired token } - mockCacheStore.set(`current_${SID_VALUE}`, expiredTokenResponse) + mockCacheStore.set( + `current::${mockConfig.name}::${SID_VALUE}`, + expiredTokenResponse, + ) // Act const res = await server diff --git a/apps/services/bff/src/app/utils/get-cookie-options.ts b/apps/services/bff/src/app/utils/get-cookie-options.ts index ab59c25646ca..2edfecc2af6f 100644 --- a/apps/services/bff/src/app/utils/get-cookie-options.ts +++ b/apps/services/bff/src/app/utils/get-cookie-options.ts @@ -8,6 +8,6 @@ export const getCookieOptions = (): CookieOptions => { // The lax setting allows cookies to be sent on top-level navigations (such as redirects), // while still providing some protection against CSRF attacks. sameSite: 'lax', - path: environment.keyPath, + path: environment.globalPrefix, } } diff --git a/apps/services/bff/src/app/utils/validate-uri.ts b/apps/services/bff/src/app/utils/validate-uri.ts index 0a084f35b005..e1d76a7605a4 100644 --- a/apps/services/bff/src/app/utils/validate-uri.ts +++ b/apps/services/bff/src/app/utils/validate-uri.ts @@ -13,6 +13,9 @@ import { URL } from 'url' * @param uri - The URI to validate. * @param allowedUris - List of allowed URIs. * @returns True if the URI is valid, false otherwise. + * + * @example validateUri('https://island.is/some-path', ['https://island.is']) // true + * */ export const validateUri = (uri: string, allowedUris: string[]): boolean => { // input validation for extra security diff --git a/apps/services/bff/src/environment/environment.schema.ts b/apps/services/bff/src/environment/environment.schema.ts index c660cfd14845..915a31b9cacd 100644 --- a/apps/services/bff/src/environment/environment.schema.ts +++ b/apps/services/bff/src/environment/environment.schema.ts @@ -1,9 +1,8 @@ import { z } from 'zod' -const KEY_PATH_ENV_VAR = 'BFF_CLIENT_KEY_PATH' - export const environmentSchema = z.strictObject({ production: z.boolean().default(false), + name: z.string({ required_error: 'BFF_NAME is required' }), port: z.preprocess( (val) => (val ? parseInt(val as string, 10) : 3010), z @@ -12,16 +11,16 @@ export const environmentSchema = z.strictObject({ .max(65535), ), /** - * The global prefix for the API + * The global prefix path for the API + * This is used to create the base path for the API */ - keyPath: z + globalPrefix: z .string({ - required_error: `${KEY_PATH_ENV_VAR} is required`, + required_error: 'BFF_GLOBAL_PREFIX is required', }) - .refine((val) => !val.endsWith('/bff'), { - message: `${KEY_PATH_ENV_VAR} must not end with /bff`, + .refine((val) => val.endsWith('/bff'), { + message: 'BFF_GLOBAL_PREFIX must end with /bff', }), - name: z.string({ required_error: 'BFF_NAME is required' }), }) export type BffEnvironment = z.infer diff --git a/apps/services/bff/src/environment/environment.ts b/apps/services/bff/src/environment/environment.ts index f98c1992f1dc..2edef516deeb 100644 --- a/apps/services/bff/src/environment/environment.ts +++ b/apps/services/bff/src/environment/environment.ts @@ -5,7 +5,7 @@ export const isProduction = process.env.NODE_ENV === 'production' const parsedEnvironment = environmentSchema.parse({ production: isProduction, port: process.env.PORT, - keyPath: process.env.BFF_CLIENT_KEY_PATH, + globalPrefix: process.env.BFF_GLOBAL_PREFIX, name: process.env.BFF_NAME, }) diff --git a/apps/services/bff/src/main.ts b/apps/services/bff/src/main.ts index 4a6cf20067ed..3a9b50b4ae6e 100644 --- a/apps/services/bff/src/main.ts +++ b/apps/services/bff/src/main.ts @@ -7,7 +7,7 @@ bootstrap({ appModule: AppModule, name: 'bff', port: environment.port, - globalPrefix: `${environment.keyPath}/bff`, + globalPrefix: environment.globalPrefix, healthCheck: true, jsonBodyLimit: '350kb', }) diff --git a/apps/services/bff/test/setup.ts b/apps/services/bff/test/setup.ts index 9c8cbeee3c85..46fe8641c615 100644 --- a/apps/services/bff/test/setup.ts +++ b/apps/services/bff/test/setup.ts @@ -9,7 +9,8 @@ process.env.IDENTITY_SERVER_CLIENT_SCOPES = '["testscope"]' process.env.IDENTITY_SERVER_CLIENT_ID = '@test_client_id' process.env.BFF_PAR_SUPPORT_ENABLED = 'false' -process.env.BFF_CLIENT_KEY_PATH = '/testclient' +process.env.BFF_GLOBAL_PREFIX = '/testclient/bff' +process.env.BFF_CLIENT_BASE_PATH = '/testclient' process.env.BFF_CLIENT_BASE_URL = 'http://test-client.com' process.env.BFF_ALLOWED_REDIRECT_URIS = '["http://test-client.com/testclient"]' process.env.BFF_ALLOWED_EXTERNAL_API_URLS = '["https://api.external.com"]' diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 61a7e71d2bc0..1f9bd27e19e6 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -933,8 +933,6 @@ application-system-form: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -1804,6 +1802,7 @@ namespaces: - 'services-sessions' - 'contentful-apps' - 'services-university-gateway' + - 'portals-my-pages' portals-admin: enabled: true env: @@ -1812,7 +1811,6 @@ portals-admin: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' @@ -2105,8 +2103,6 @@ service-portal: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -2297,11 +2293,12 @@ services-bff-portals-admin: enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.dev01.devland.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://beta.dev01.devland.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'stjornbord' @@ -2377,6 +2374,91 @@ services-bff-portals-admin: eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/services-bff' create: true name: 'services-bff' +services-bff-portals-my-pages: + enabled: true + env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.dev01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is/minarsidur","https://beta.dev01.devland.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://beta.dev01.devland.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: + - 'identity-server' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/enable-global-auth: 'false' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'beta.dev01.devland.is' + paths: + - '/bff' + namespace: 'portals-my-pages' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 2 + max: 10 + min: 2 + resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' + secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/services-bff' + create: true + name: 'services-bff' services-documents: enabled: true env: diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index 8a912934c7f2..9415f97f64a8 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -921,8 +921,6 @@ application-system-form: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -1668,6 +1666,7 @@ namespaces: - 'services-university-gateway' - 'contentful-apps' - 'contentful-entry-tagger' + - 'portals-my-pages' portals-admin: enabled: true env: @@ -1676,7 +1675,6 @@ portals-admin: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' @@ -1971,8 +1969,6 @@ service-portal: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -2166,11 +2162,12 @@ services-bff-portals-admin: enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.island.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://island.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://island.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://island.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://island.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'stjornbord' @@ -2248,6 +2245,93 @@ services-bff-portals-admin: eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/services-bff' create: true name: 'services-bff' +services-bff-portals-my-pages: + enabled: true + env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.island.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://island.is/minarsidur","https://island.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://island.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://island.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://island.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + grantNamespaces: + - 'identity-server' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'island.is' + paths: + - '/bff' + - host: 'www.island.is' + paths: + - '/bff' + namespace: 'portals-my-pages' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 2 + max: 10 + min: 2 + resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' + secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/services-bff' + create: true + name: 'services-bff' services-documents: enabled: true env: diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index 386325064969..24cf1fc37715 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -930,8 +930,6 @@ application-system-form: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -1542,6 +1540,7 @@ namespaces: - 'license-api' - 'services-sessions' - 'services-university-gateway' + - 'portals-my-pages' portals-admin: enabled: true env: @@ -1550,7 +1549,6 @@ portals-admin: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' @@ -1843,8 +1841,6 @@ service-portal: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' @@ -2035,11 +2031,12 @@ services-bff-portals-admin: enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.staging01.devland.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://beta.staging01.devland.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://beta.staging01.devland.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'stjornbord' @@ -2115,6 +2112,91 @@ services-bff-portals-admin: eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/services-bff' create: true name: 'services-bff' +services-bff-portals-my-pages: + enabled: true + env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.staging01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is/minarsidur","https://beta.staging01.devland.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://beta.staging01.devland.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://beta.staging01.devland.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: + - 'identity-server' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/enable-global-auth: 'false' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'beta.staging01.devland.is' + paths: + - '/bff' + namespace: 'portals-my-pages' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 2 + max: 10 + min: 2 + resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' + secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/services-bff' + create: true + name: 'services-bff' services-documents: enabled: true env: diff --git a/charts/services/application-system-form/values.dev.yaml b/charts/services/application-system-form/values.dev.yaml index e5123408cc89..0db3d27f48ad 100644 --- a/charts/services/application-system-form/values.dev.yaml +++ b/charts/services/application-system-form/values.dev.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/application-system-form/values.prod.yaml b/charts/services/application-system-form/values.prod.yaml index 1d34997cf74b..2c433a692781 100644 --- a/charts/services/application-system-form/values.prod.yaml +++ b/charts/services/application-system-form/values.prod.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/application-system-form/values.staging.yaml b/charts/services/application-system-form/values.staging.yaml index c72a1870b7fb..849679fb5411 100644 --- a/charts/services/application-system-form/values.staging.yaml +++ b/charts/services/application-system-form/values.staging.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_GRAPHQL_PATH: '' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/portals-admin/values.dev.yaml b/charts/services/portals-admin/values.dev.yaml index faa53d46dd89..dca1469e538e 100644 --- a/charts/services/portals-admin/values.dev.yaml +++ b/charts/services/portals-admin/values.dev.yaml @@ -24,7 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' diff --git a/charts/services/portals-admin/values.prod.yaml b/charts/services/portals-admin/values.prod.yaml index 192966e8f623..8b5e7cff49f5 100644 --- a/charts/services/portals-admin/values.prod.yaml +++ b/charts/services/portals-admin/values.prod.yaml @@ -24,7 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' diff --git a/charts/services/portals-admin/values.staging.yaml b/charts/services/portals-admin/values.staging.yaml index 1f527f1c28a0..7db3b0717c80 100644 --- a/charts/services/portals-admin/values.staging.yaml +++ b/charts/services/portals-admin/values.staging.yaml @@ -24,7 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=460' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-external' - 'identity-server' diff --git a/charts/services/service-portal/values.dev.yaml b/charts/services/service-portal/values.dev.yaml index d6286ea74f53..13979e2b3a49 100644 --- a/charts/services/service-portal/values.dev.yaml +++ b/charts/services/service-portal/values.dev.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'dev' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/service-portal/values.prod.yaml b/charts/services/service-portal/values.prod.yaml index 601f924dc3ec..005cc527fab2 100644 --- a/charts/services/service-portal/values.prod.yaml +++ b/charts/services/service-portal/values.prod.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SI_PUBLIC_ENVIRONMENT: 'prod' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/service-portal/values.staging.yaml b/charts/services/service-portal/values.staging.yaml index 0401a390ad67..682ab594ddcd 100644 --- a/charts/services/service-portal/values.staging.yaml +++ b/charts/services/service-portal/values.staging.yaml @@ -24,8 +24,6 @@ env: NODE_OPTIONS: '--max-old-space-size=230' SERVERSIDE_FEATURES_ON: '' SI_PUBLIC_ENVIRONMENT: 'staging' - SI_PUBLIC_GRAPHQL_API: '/api/graphql' - SI_PUBLIC_IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' grantNamespaces: - 'nginx-ingress-internal' - 'nginx-ingress-external' diff --git a/charts/services/services-bff-portals-admin/values.dev.yaml b/charts/services/services-bff-portals-admin/values.dev.yaml index e5b6da0bda2f..4cd414d97164 100644 --- a/charts/services/services-bff-portals-admin/values.dev.yaml +++ b/charts/services/services-bff-portals-admin/values.dev.yaml @@ -20,11 +20,12 @@ name: 'services-bff-portals-admin' enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.dev01.devland.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://beta.dev01.devland.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'stjornbord' diff --git a/charts/services/services-bff-portals-admin/values.prod.yaml b/charts/services/services-bff-portals-admin/values.prod.yaml index ffde3a0cc06e..81f77113e347 100644 --- a/charts/services/services-bff-portals-admin/values.prod.yaml +++ b/charts/services/services-bff-portals-admin/values.prod.yaml @@ -20,11 +20,12 @@ name: 'services-bff-portals-admin' enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.island.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://island.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://island.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://island.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://island.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'stjornbord' diff --git a/charts/services/services-bff-portals-admin/values.staging.yaml b/charts/services/services-bff-portals-admin/values.staging.yaml index 6c61eb1836e1..acc862a29e66 100644 --- a/charts/services/services-bff-portals-admin/values.staging.yaml +++ b/charts/services/services-bff-portals-admin/values.staging.yaml @@ -20,11 +20,12 @@ name: 'services-bff-portals-admin' enabled: true env: BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.staging01.devland.is"]' - BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is/stjornbord"]' BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' BFF_CALLBACKS_BASE_PATH: 'https://beta.staging01.devland.is/stjornbord/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/stjornbord' BFF_CLIENT_BASE_URL: 'https://beta.staging01.devland.is' - BFF_CLIENT_KEY_PATH: '/stjornbord' + BFF_GLOBAL_PREFIX: '/stjornbord/bff' BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'stjornbord' diff --git a/charts/services/services-bff-portals-my-pages/values.dev.yaml b/charts/services/services-bff-portals-my-pages/values.dev.yaml new file mode 100644 index 000000000000..f927a4cd332f --- /dev/null +++ b/charts/services/services-bff-portals-my-pages/values.dev.yaml @@ -0,0 +1,103 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'dev' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'services-bff-portals-my-pages' +enabled: true +env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.dev01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.dev01.devland.is/minarsidur","https://beta.dev01.devland.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://beta.dev01.devland.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: '' +grantNamespaces: + - 'identity-server' +grantNamespacesEnabled: true +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/enable-global-auth: 'false' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'beta.dev01.devland.is' + paths: + - '/bff' +namespace: 'portals-my-pages' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 2 + max: 10 + min: 2 +resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' +secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/services-bff' + create: true + name: 'services-bff' diff --git a/charts/services/services-bff-portals-my-pages/values.prod.yaml b/charts/services/services-bff-portals-my-pages/values.prod.yaml new file mode 100644 index 000000000000..409861769eca --- /dev/null +++ b/charts/services/services-bff-portals-my-pages/values.prod.yaml @@ -0,0 +1,105 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'prod' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'services-bff-portals-my-pages' +enabled: true +env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.island.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://island.is/minarsidur","https://island.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://island.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://island.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://island.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' +grantNamespaces: + - 'identity-server' +grantNamespacesEnabled: true +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'island.is' + paths: + - '/bff' + - host: 'www.island.is' + paths: + - '/bff' +namespace: 'portals-my-pages' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 2 + max: 10 + min: 2 +resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' +secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/services-bff' + create: true + name: 'services-bff' diff --git a/charts/services/services-bff-portals-my-pages/values.staging.yaml b/charts/services/services-bff-portals-my-pages/values.staging.yaml new file mode 100644 index 000000000000..5094a777ea72 --- /dev/null +++ b/charts/services/services-bff-portals-my-pages/values.staging.yaml @@ -0,0 +1,103 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'staging' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'services-bff-portals-my-pages' +enabled: true +env: + BFF_ALLOWED_EXTERNAL_API_URLS: '["https://api.staging01.devland.is"]' + BFF_ALLOWED_REDIRECT_URIS: '["https://beta.staging01.devland.is/minarsidur","https://beta.staging01.devland.is/umsoknir"]' + BFF_CACHE_USER_PROFILE_TTL_MS: '3595000' + BFF_CALLBACKS_BASE_PATH: 'https://beta.staging01.devland.is/bff/callbacks' + BFF_CLIENT_BASE_PATH: '/minarsidur' + BFF_CLIENT_BASE_URL: 'https://beta.staging01.devland.is' + BFF_GLOBAL_PREFIX: '/bff' + BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' + BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' + BFF_NAME: 'minarsidur' + BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + IDENTITY_SERVER_CLIENT_ID: '@island.is/web' + IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' + IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' + REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' + SERVERSIDE_FEATURES_ON: '' +grantNamespaces: + - 'identity-server' +grantNamespacesEnabled: true +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/bff/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/bff/health/check' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 2 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-bff' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/enable-global-auth: 'false' + nginx.ingress.kubernetes.io/proxy-buffer-size: '8k' + nginx.ingress.kubernetes.io/proxy-buffering: 'on' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'beta.staging01.devland.is' + paths: + - '/bff' +namespace: 'portals-my-pages' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 2 + max: 10 + min: 2 +resources: + limits: + cpu: '400m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '256Mi' +secrets: + BFF_TOKEN_SECRET_BASE64: '/k8s/services-bff/portals-my-pages/BFF_TOKEN_SECRET_BASE64' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-bff/portals-my-pages/IDENTITY_SERVER_CLIENT_SECRET' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/services-bff' + create: true + name: 'services-bff' diff --git a/infra/src/dsl/bff.ts b/infra/src/dsl/bff.ts index ce694c6c683e..76d9aa87a345 100644 --- a/infra/src/dsl/bff.ts +++ b/infra/src/dsl/bff.ts @@ -3,28 +3,64 @@ import { BffInfo, Context, PortalKeys } from './types/input-types' import { adminPortalScopes, + applicationSystemScopes, servicePortalScopes, } from '../../../libs/auth/scopes/src/index' +const MINAR_SIDUR: PortalKeys = 'minarsidur' +const STJORNBORD: PortalKeys = 'stjornbord' + +/** + * Trim any leading and trailing slashes + */ +const sanitizePath = (path: string) => path.replace(/^\/+|\/+$/g, '') + export const getScopes = (key: PortalKeys) => { switch (key) { - case 'minarsidur': - return servicePortalScopes - case 'stjornbord': - return adminPortalScopes + case MINAR_SIDUR: { + const combinedScopes = new Set([ + ...servicePortalScopes, + ...applicationSystemScopes, + ]) + + return [...combinedScopes] + } + + case STJORNBORD: { + const uniqueScopes = new Set([...adminPortalScopes]) + + return [...uniqueScopes] + } + default: throw new Error('Invalid BFF client') } } -export const bffConfig = ({ key, services, clientName, clientId }: BffInfo) => { - const getBaseUrl = (ctx: Context) => - ctx.featureDeploymentName +export const bffConfig = ({ + key, + services, + clientName, + clientId, + globalPrefix, +}: BffInfo) => { + const sanitizeGlobalPrefix = sanitizePath(globalPrefix) + + const getBaseUrl = (ctx: Context) => { + const domain = ctx.featureDeploymentName ? `${ctx.featureDeploymentName}-beta.${ctx.env.domain}` : ctx.env.type === 'prod' ? ctx.env.domain : `beta.${ctx.env.domain}` + return `https://${domain}` + } + + const getRedirectUris = (baseUrl: string, key: PortalKeys) => [ + `${baseUrl}/${key}`, + ...(key === MINAR_SIDUR ? [`${baseUrl}/umsoknir`] : []), + ] + return { env: { IDENTITY_SERVER_CLIENT_SCOPES: json(getScopes(key)), @@ -40,31 +76,38 @@ export const bffConfig = ({ key, services, clientName, clientId }: BffInfo) => { staging: key, prod: key, }, - BFF_CLIENT_KEY_PATH: `/${key}`, + BFF_GLOBAL_PREFIX: globalPrefix, + BFF_CLIENT_BASE_PATH: `/${key}`, BFF_PAR_SUPPORT_ENABLED: 'true', BFF_CLIENT_BASE_URL: { local: 'http://localhost:4200', - dev: ref((ctx) => ctx.svc(`https://${getBaseUrl(ctx)}`)), - staging: ref((ctx) => ctx.svc(`https://${getBaseUrl(ctx)}`)), + dev: ref((ctx) => ctx.svc(getBaseUrl(ctx))), + staging: ref((ctx) => ctx.svc(getBaseUrl(ctx))), prod: 'https://island.is', }, BFF_ALLOWED_REDIRECT_URIS: { - local: json([`http://localhost:4200/${key}`]), - dev: ref((ctx) => json([`https://${getBaseUrl(ctx)}`])), - staging: ref((ctx) => json([`https://${getBaseUrl(ctx)}`])), - prod: json(['https://island.is']), + local: json([ + `http://localhost:4200/${key}`, + // This is a special case for minarsidur, since it serves two applications + ...(key === MINAR_SIDUR ? ['http://localhost:4242/umsoknir'] : []), + ]), + dev: ref((ctx) => json(getRedirectUris(getBaseUrl(ctx), key))), + staging: ref((ctx) => json(getRedirectUris(getBaseUrl(ctx), key))), + prod: json(getRedirectUris('https://island.is', key)), }, BFF_LOGOUT_REDIRECT_URI: { local: `http://localhost:4200/${key}`, - dev: ref((ctx) => `https://${getBaseUrl(ctx)}`), - staging: ref((ctx) => `https://${getBaseUrl(ctx)}`), + dev: ref(getBaseUrl), + staging: ref(getBaseUrl), prod: 'https://island.is', }, BFF_CALLBACKS_BASE_PATH: { - local: `http://localhost:3010/${key}/bff/callbacks`, - dev: ref((c) => `https://${getBaseUrl(c)}/${key}/bff/callbacks`), - staging: ref((c) => `https://${getBaseUrl(c)}/${key}/bff/callbacks`), - prod: ref((c) => `https://${getBaseUrl(c)}/${key}/bff/callbacks`), + local: `http://localhost:3010/${sanitizeGlobalPrefix}/callbacks`, + dev: ref((c) => `${getBaseUrl(c)}/${sanitizeGlobalPrefix}/callbacks`), + staging: ref( + (c) => `${getBaseUrl(c)}/${sanitizeGlobalPrefix}/callbacks`, + ), + prod: ref((c) => `${getBaseUrl(c)}/${sanitizeGlobalPrefix}/callbacks`), }, BFF_PROXY_API_ENDPOINT: { local: 'http://localhost:4444/api/graphql', diff --git a/infra/src/dsl/feature-values.spec.ts b/infra/src/dsl/feature-values.spec.ts index d9b72b3d3f03..2fbc7bca3530 100644 --- a/infra/src/dsl/feature-values.spec.ts +++ b/infra/src/dsl/feature-values.spec.ts @@ -82,6 +82,7 @@ describe('Feature-deployment support', () => { clientId: '@admin.island.is/bff-stjornbord', clientName: 'portals-admin', services: { api: apiService }, + globalPrefix: '/stjornbord/bff', }) .env({ BFF_ALLOWED_EXTERNAL_API_URLS: { @@ -122,10 +123,11 @@ describe('Feature-deployment support', () => { IDENTITY_SERVER_CLIENT_ID: `@admin.island.is/bff-stjornbord`, IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is', BFF_NAME: 'stjornbord', - BFF_CLIENT_KEY_PATH: `/stjornbord`, + BFF_CLIENT_BASE_PATH: '/stjornbord', + BFF_GLOBAL_PREFIX: `/stjornbord/bff`, BFF_PAR_SUPPORT_ENABLED: 'true', BFF_ALLOWED_REDIRECT_URIS: json([ - 'https://feature-A-beta.dev01.devland.is', + 'https://feature-A-beta.dev01.devland.is/stjornbord', ]), BFF_CLIENT_BASE_URL: 'https://feature-A-beta.dev01.devland.is', BFF_LOGOUT_REDIRECT_URI: 'https://feature-A-beta.dev01.devland.is', diff --git a/infra/src/dsl/portal-env.spec.ts b/infra/src/dsl/portal-env.spec.ts index fe20cab1fd80..df39ad3e13bc 100644 --- a/infra/src/dsl/portal-env.spec.ts +++ b/infra/src/dsl/portal-env.spec.ts @@ -54,6 +54,7 @@ describe('BFF PortalEnv serialization', () => { clientId: `@admin.island.is/bff-stjornbord`, clientName, services, + globalPrefix: '/stjornbord/bff', }) .command('node') .args('main.js') @@ -156,9 +157,10 @@ describe('BFF PortalEnv serialization', () => { IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is', // BFF BFF_NAME: 'stjornbord', - BFF_CLIENT_KEY_PATH: `/${key}`, + BFF_GLOBAL_PREFIX: `/${key}/bff`, BFF_PAR_SUPPORT_ENABLED: 'true', - BFF_ALLOWED_REDIRECT_URIS: json(['https://beta.dev01.devland.is']), + BFF_ALLOWED_REDIRECT_URIS: json([`https://beta.dev01.devland.is/${key}`]), + BFF_CLIENT_BASE_PATH: `/${key}`, BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is', BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is', BFF_CALLBACKS_BASE_PATH: `https://beta.dev01.devland.is/${key}/bff/callbacks`, diff --git a/infra/src/dsl/types/input-types.ts b/infra/src/dsl/types/input-types.ts index 04b75ad4f20e..95db511e89bd 100644 --- a/infra/src/dsl/types/input-types.ts +++ b/infra/src/dsl/types/input-types.ts @@ -1,6 +1,6 @@ +import { ServiceBuilder } from '../dsl' import { FeatureNames } from '../features' import { EnvironmentConfig } from './charts' -import { ServiceBuilder } from '../dsl' import { Optional } from './helpers' export type OpsEnv = 'dev' | 'staging' | 'prod' @@ -22,6 +22,10 @@ export type BffInfo = { clientId: string clientName: string services: BffInfraServices + /** + * Global url prefix for the app + */ + globalPrefix: string env?: EnvironmentVariables secrets?: Secrets } diff --git a/infra/src/uber-charts/islandis.ts b/infra/src/uber-charts/islandis.ts index 28bd6f521aaa..705e3c1113f0 100644 --- a/infra/src/uber-charts/islandis.ts +++ b/infra/src/uber-charts/islandis.ts @@ -1,8 +1,8 @@ import { serviceSetup as apiSetup } from '../../../apps/api/infra/api' -import { serviceSetup as webSetup } from '../../../apps/web/infra/web' -import { serviceSetup as searchIndexerSetup } from '../../../apps/services/search-indexer/infra/search-indexer-service' -import { serviceSetup as contentfulEntryTaggerSetup } from '../../../apps/services/contentful-entry-tagger/infra/contentful-entry-tagger-service' import { serviceSetup as contentfulAppsSetup } from '../../../apps/contentful-apps/infra/contentful-apps' +import { serviceSetup as contentfulEntryTaggerSetup } from '../../../apps/services/contentful-entry-tagger/infra/contentful-entry-tagger-service' +import { serviceSetup as searchIndexerSetup } from '../../../apps/services/search-indexer/infra/search-indexer-service' +import { serviceSetup as webSetup } from '../../../apps/web/infra/web' import { serviceSetup as appSystemApiSetup, @@ -11,40 +11,40 @@ import { import { serviceSetup as appSystemFormSetup } from '../../../apps/application-system/form/infra/application-system-form' // Portals -import { serviceSetup as servicePortalApiSetup } from '../../../apps/services/user-profile/infra/service-portal-api' -import { serviceSetup as servicePortalSetup } from '../../../apps/portals/my-pages/infra/portals-my-pages' - import { serviceSetup as adminPortalSetup } from '../../../apps/portals/admin/infra/portals-admin' +import { serviceSetup as servicePortalSetup } from '../../../apps/portals/my-pages/infra/portals-my-pages' +import { serviceSetup as servicePortalApiSetup } from '../../../apps/services/user-profile/infra/service-portal-api' // Bff's import { serviceSetup as bffAdminPortalServiceSetup } from '../../../apps/services/bff/infra/admin-portal.infra' +import { serviceSetup as bffServicePortalServiceSetup } from '../../../apps/services/bff/infra/my-pages-portal.infra' import { serviceSetup as consultationPortalSetup } from '../../../apps/consultation-portal/infra/samradsgatt' import { serviceSetup as xroadCollectorSetup } from '../../../apps/services/xroad-collector/infra/xroad-collector' import { serviceSetup as licenseApiSetup } from '../../../apps/services/license-api/infra/license-api' -import { serviceSetup as skilavottordWsSetup } from '../../../apps/skilavottord/ws/infra/skilavottord-ws' import { serviceSetup as skilavottordWebSetup } from '../../../apps/skilavottord/web/infra/skilavottord-web' +import { serviceSetup as skilavottordWsSetup } from '../../../apps/skilavottord/ws/infra/skilavottord-ws' -import { serviceSetup as serviceDocumentsSetup } from '../../../apps/services/documents/infra/documents-service' import { serviceSetup as serviceNameRegistryBackendSetup } from '../../../apps/icelandic-names-registry/backend/infra/icelandic-names-registry-backend' +import { serviceSetup as serviceDocumentsSetup } from '../../../apps/services/documents/infra/documents-service' import { serviceSetup as storybookSetup } from '../../../libs/island-ui/storybook/infra/storybook' import { serviceSetup as downloadServiceSetup } from '../../../apps/download-service/infra/download-service' -import { serviceSetup as endorsementServiceSetup } from '../../../apps/services/endorsements/api/infra/endorsement-system-api' import { serviceSetup as githubActionsCacheSetup } from '../../../apps/github-actions-cache/infra/github-actions-cache' +import { serviceSetup as endorsementServiceSetup } from '../../../apps/services/endorsements/api/infra/endorsement-system-api' import { - userNotificationServiceSetup, userNotificationCleanUpWorkerSetup, + userNotificationServiceSetup, userNotificationWorkerSetup, } from '../../../apps/services/user-notification/infra/user-notification' import { serviceSetup as adsApiSetup } from '../../../apps/air-discount-scheme/api/infra/api' -import { serviceSetup as adsWebSetup } from '../../../apps/air-discount-scheme/web/infra/web' import { serviceSetup as adsBackendSetup } from '../../../apps/air-discount-scheme/backend/infra/air-discount-scheme-backend' +import { serviceSetup as adsWebSetup } from '../../../apps/air-discount-scheme/web/infra/web' import { serviceSetup as externalContractsTestsSetup } from '../../../apps/external-contracts-tests/infra/external-contracts-tests' @@ -56,9 +56,9 @@ import { } from '../../../apps/services/university-gateway/infra/university-gateway' import { + cleanupSetup as sessionsCleanupWorkerSetup, serviceSetup as sessionsServiceSetup, workerSetup as sessionsWorkerSetup, - cleanupSetup as sessionsCleanupWorkerSetup, } from '../../../apps/services/sessions/infra/sessions' import { serviceSetup as authAdminApiSetup } from '../../../apps/services/auth/admin-api/infra/auth-admin-api' @@ -87,7 +87,6 @@ const appSystemApi = appSystemApiSetup({ }) const appSystemApiWorker = appSystemApiWorkerSetup() -const adminPortal = adminPortalSetup() const nameRegistryBackend = serviceNameRegistryBackendSetup() const adsBackend = adsBackendSetup() @@ -118,9 +117,12 @@ const api = apiSetup({ userNotificationService, }) -const servicePortal = servicePortalSetup({ graphql: api }) +const adminPortal = adminPortalSetup() +const servicePortal = servicePortalSetup() const bffAdminPortalService = bffAdminPortalServiceSetup({ api }) -const appSystemForm = appSystemFormSetup({ api }) +const bffServicePortalService = bffServicePortalServiceSetup({ api }) + +const appSystemForm = appSystemFormSetup() const web = webSetup({ api }) const searchIndexer = searchIndexerSetup() const contentfulEntryTagger = contentfulEntryTaggerSetup() @@ -182,6 +184,7 @@ export const Services: EnvironmentServices = { contentfulApps, contentfulEntryTagger, bffAdminPortalService, + bffServicePortalService, ], staging: [ appSystemApi, @@ -216,6 +219,7 @@ export const Services: EnvironmentServices = { universityGatewayService, universityGatewayWorker, bffAdminPortalService, + bffServicePortalService, ], dev: [ appSystemApi, @@ -254,6 +258,7 @@ export const Services: EnvironmentServices = { universityGatewayService, universityGatewayWorker, bffAdminPortalService, + bffServicePortalService, ], } diff --git a/libs/application/core/src/lib/conditionUtils.spec.ts b/libs/application/core/src/lib/conditionUtils.spec.ts index 0eee417536dc..eebc1c2b0d98 100644 --- a/libs/application/core/src/lib/conditionUtils.spec.ts +++ b/libs/application/core/src/lib/conditionUtils.spec.ts @@ -2,27 +2,25 @@ import { AllOrAny, Comparators, Condition, - SingleConditionCheck, - FormValue, ExternalData, + FormValue, + SingleConditionCheck, } from '@island.is/application/types' +import { applicationSystemScopes } from '@island.is/auth/scopes' +import { BffUser } from '@island.is/shared/types' import { buildTextField } from '../lib/fieldBuilders' import { shouldShowFormItem } from './conditionUtils' import { buildSection, buildSubSection } from './formBuilders' -import { User } from '@island.is/shared/types' -import { createOpenIDUser } from '@island.is/testing/fixtures' -const createRandomUser = (): User => { - const user = { - profile: { - nationalId: '1234567890', - name: 'John Doe', - idp: 'idpExample', - subjectType: 'person', - }, - } - return createOpenIDUser(user as User) -} +const createRandomUser = (): BffUser => ({ + profile: { + nationalId: '1234567890', + name: 'John Doe', + idp: 'idpExample', + subjectType: 'person', + } as BffUser['profile'], + scopes: applicationSystemScopes, +}) const createExternalData = ( key: string, diff --git a/libs/application/core/src/lib/conditionUtils.ts b/libs/application/core/src/lib/conditionUtils.ts index a326cff89b6a..f012e3929b19 100644 --- a/libs/application/core/src/lib/conditionUtils.ts +++ b/libs/application/core/src/lib/conditionUtils.ts @@ -8,14 +8,14 @@ import { SingleConditionCheck, StaticCheck, } from '@island.is/application/types' +import { BffUser } from '@island.is/shared/types' import { getValueViaPath } from './formUtils' -import { User } from '@island.is/shared/types' const applyStaticConditionalCheck = ( formValue: FormValue, externalData: ExternalData, check: StaticCheck, - user: User | null, + user: BffUser | null, ): boolean => { const { value, questionId, comparator, externalDataId, userPropId } = check let isValid = false @@ -72,7 +72,7 @@ export const shouldShowFormItem = ( formItem: FormItem, formValue: FormValue, externalData: ExternalData = {}, - user: User | null, + user: BffUser | null, ): boolean => { const { condition } = formItem if (!condition) { diff --git a/libs/application/core/src/lib/formUtils.ts b/libs/application/core/src/lib/formUtils.ts index b7f704749a77..426bc2289b32 100644 --- a/libs/application/core/src/lib/formUtils.ts +++ b/libs/application/core/src/lib/formUtils.ts @@ -3,30 +3,30 @@ // So disabling the check for now import deepmerge from 'deepmerge' -import isArray from 'lodash/isArray' import get from 'lodash/get' +import isArray from 'lodash/isArray' import HtmlParser from 'react-html-parser' -import { shouldShowFormItem } from './conditionUtils' import { Application, ExternalData, - FormValue, - Form, Field, - RecordObject, + Form, FormItemTypes, FormLeaf, FormNode, FormText, FormTextArray, + FormTextWithLocale, + FormValue, + RecordObject, Section, StaticText, StaticTextObject, SubSection, - FormTextWithLocale, } from '@island.is/application/types' -import { Locale, User } from '@island.is/shared/types' +import { BffUser, Locale } from '@island.is/shared/types' +import { shouldShowFormItem } from './conditionUtils' const containsArray = (obj: RecordObject) => { let contains = false @@ -116,7 +116,7 @@ export const getSectionsInForm = ( form: Form, answers: FormValue, externalData: ExternalData, - user: User | null, + user: BffUser | null, ): Section[] => { const sections: Section[] = [] form.children.forEach((child) => { @@ -137,7 +137,7 @@ export const getSubSectionsInSection = ( section: Section, answers: FormValue, externalData: ExternalData, - user: User | null, + user: BffUser | null, ): SubSection[] => { const subSections: SubSection[] = [] section?.children.forEach((child) => { diff --git a/libs/application/graphql/src/lib/client.ts b/libs/application/graphql/src/lib/client.ts index 05cb020f0f08..cdff65e1947a 100644 --- a/libs/application/graphql/src/lib/client.ts +++ b/libs/application/graphql/src/lib/client.ts @@ -1,19 +1,18 @@ -import fetch from 'cross-fetch' import { ApolloClient, - InMemoryCache, - HttpLink, ApolloLink, + HttpLink, + InMemoryCache, } from '@apollo/client' import { onError } from '@apollo/client/link/error' import { RetryLink } from '@apollo/client/link/retry' -import { authLink } from '@island.is/auth/react' +import fetch from 'cross-fetch' const retryLink = new RetryLink() const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) - graphQLErrors.map(({ message, locations, path, extensions }) => { + graphQLErrors.map(({ message, path, extensions }) => { const problem = JSON.stringify(extensions?.problem, null, ' ') console.log( `[GraphQL error]: Message: ${message}, Path: ${path}, Problem: ${problem}`, @@ -23,14 +22,12 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { if (networkError) console.log(`[Network error]: ${networkError}`) }) -export const initializeClient = (baseApiUrl: string) => { - const httpLink = new HttpLink({ - uri: ({ operationName }) => `${baseApiUrl}/api/graphql?op=${operationName}`, - fetch, - }) +const httpLink = new HttpLink({ + uri: ({ operationName }) => `/bff/api/graphql?op=${operationName}`, + fetch, +}) - return new ApolloClient({ - link: ApolloLink.from([retryLink, errorLink, authLink, httpLink]), - cache: new InMemoryCache(), - }) -} +export const client = new ApolloClient({ + link: ApolloLink.from([retryLink, errorLink, httpLink]), + cache: new InMemoryCache(), +}) diff --git a/libs/application/templates/aosh/transfer-of-machine-ownership/src/fields/Review/index.tsx b/libs/application/templates/aosh/transfer-of-machine-ownership/src/fields/Review/index.tsx index 9c53ebdf6ee3..781208d42582 100644 --- a/libs/application/templates/aosh/transfer-of-machine-ownership/src/fields/Review/index.tsx +++ b/libs/application/templates/aosh/transfer-of-machine-ownership/src/fields/Review/index.tsx @@ -1,18 +1,18 @@ +import { getValueViaPath } from '@island.is/application/core' import { FieldBaseProps } from '@island.is/application/types' -import { FC, useState } from 'react' import { Box } from '@island.is/island-ui/core' +import { useUserInfo } from '@island.is/react-spa/bff' +import { FC, useState } from 'react' +import { useFormContext } from 'react-hook-form' +import { MachineLocation, Operator, ReviewState } from '../../shared' import { ApplicationStatus } from '../ApplicationStatus' -import { Overview } from '../Overview' import { Location } from '../Location' +import { Overview } from '../Overview' import { ReviewOperatorRepeater } from '../ReviewOperatorRepeater' -import { Operator, MachineLocation, ReviewState } from '../../shared' -import { getValueViaPath } from '@island.is/application/core' -import { useAuth } from '@island.is/auth/react' -import { useFormContext } from 'react-hook-form' export const Review: FC> = (props) => { const { application } = props - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [step, setStep] = useState('states') const [location, setLocation] = useState( diff --git a/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx b/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx index 73ac6315a451..6ea1fefede88 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx @@ -1,20 +1,20 @@ -import React, { useEffect, useState } from 'react' +import { FieldBaseProps } from '@island.is/application/types' import { Box } from '@island.is/island-ui/core' -import { useAuth } from '@island.is/auth/react' -import * as m from '../../lib/messages' -import { SummaryComment as SummaryCommentType } from '../../lib/types' -import { Routes } from '../../lib/constants' -import { formatAddress, spouseFormItems } from '../../lib/formatters' +import { useUserInfo } from '@island.is/react-spa/bff' +import React, { useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' import DescriptionText from '../../components/DescriptionText/DescriptionText' import DirectTaxPaymentModal from '../../components/DirectTaxPaymentsModal/DirectTaxPaymentModal' -import SummaryComment from '../../components/Summary/SummaryComment' import ContactInfo from '../../components/Summary/ContactInfo' +import DirectTaxPaymentCell from '../../components/Summary/DirectTaxPaymentCell' import Files from '../../components/Summary/Files' import FormInfo from '../../components/Summary/FormInfo' -import DirectTaxPaymentCell from '../../components/Summary/DirectTaxPaymentCell' +import SummaryComment from '../../components/Summary/SummaryComment' import UserInfo from '../../components/Summary/UserInfo' -import { FieldBaseProps } from '@island.is/application/types' +import { Routes } from '../../lib/constants' +import { formatAddress, spouseFormItems } from '../../lib/formatters' +import * as m from '../../lib/messages' +import { SummaryComment as SummaryCommentType } from '../../lib/types' import { getSpouseSummaryConstants } from './utils' export const SpouseSummaryForm = ({ @@ -26,8 +26,7 @@ export const SpouseSummaryForm = ({ const [isModalOpen, setIsModalOpen] = useState(false) const { setValue } = useFormContext() - - const { userInfo } = useAuth() + const userInfo = useUserInfo() useEffect(() => { setValue('spouseName', userInfo?.profile.name) diff --git a/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx b/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx index 1d3a19a11e16..2365c247ae24 100644 --- a/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx +++ b/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx @@ -1,21 +1,21 @@ -import { FC, useState } from 'react' -import { DefaultEvents, FieldBaseProps } from '@island.is/application/types' -import { Box, Divider, Button } from '@island.is/island-ui/core' -import { RejectConfirmationModal } from './RejectConfirmationModal' -import { review } from '../../lib/messages' -import { useLocale } from '@island.is/localization' import { useMutation } from '@apollo/client' -import { SUBMIT_APPLICATION } from '@island.is/application/graphql' -import { useAuth } from '@island.is/auth/react' import { getValueViaPath } from '@island.is/application/core' +import { SUBMIT_APPLICATION } from '@island.is/application/graphql' +import { DefaultEvents, FieldBaseProps } from '@island.is/application/types' +import { Box, Button, Divider } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' +import { FC, useState } from 'react' import { Routes } from '../../lib/constants' +import { review } from '../../lib/messages' +import { RejectConfirmationModal } from './RejectConfirmationModal' export const RejectApproveButtons: FC< React.PropsWithChildren > = (props) => { const { formatMessage } = useLocale() const { application, refetch, goToScreen } = props - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [rejectModalVisibility, setRejectModalVisibility] = useState(false) const [loading, setLoading] = useState(false) diff --git a/libs/application/templates/official-journal-of-iceland/src/components/communicationChannels/ChannelList.tsx b/libs/application/templates/official-journal-of-iceland/src/components/communicationChannels/ChannelList.tsx index 4bd41c749c34..1f434599e5bc 100644 --- a/libs/application/templates/official-journal-of-iceland/src/components/communicationChannels/ChannelList.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/components/communicationChannels/ChannelList.tsx @@ -1,10 +1,10 @@ import { Icon, Table as T } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { general } from '../../lib/messages' +import { useUserInfo } from '@island.is/react-spa/bff' +import set from 'lodash/set' import { useApplication } from '../../hooks/useUpdateApplication' +import { general } from '../../lib/messages' import { InputFields } from '../../lib/types' -import set from 'lodash/set' -import { useAuth } from '@island.is/auth/react' type Props = { applicationId: string @@ -18,7 +18,7 @@ export const ChannelList = ({ applicationId, onEditChannel }: Props) => { applicationId, }) - const { userInfo } = useAuth() + const userInfo = useUserInfo() const defaultName = userInfo?.profile?.name const defaultEmail = userInfo?.profile?.email diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx index e436624c5ac6..946abdbf7d5b 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx @@ -1,4 +1,3 @@ -import { useUserInfo } from '@island.is/auth/react' import { AlertMessage, Box, @@ -10,25 +9,26 @@ import { Tag, Text, } from '@island.is/island-ui/core' -import { Property } from '../components/property/Property' -import { advert, error, publishing, summary } from '../lib/messages' -import { OJOIFieldBaseProps } from '../lib/types' import { useLocale } from '@island.is/localization' -import { MINIMUM_WEEKDAYS, Routes } from '../lib/constants' -import { addWeekdays, getFastTrack, parseZodIssue } from '../lib/utils' +import { useUserInfo } from '@island.is/react-spa/bff' +import { useEffect } from 'react' +import { ZodCustomIssue } from 'zod' +import { Property } from '../components/property/Property' import { useCategories } from '../hooks/useCategories' +import { useDepartment } from '../hooks/useDepartment' +import { usePrice } from '../hooks/usePrice' +import { useType } from '../hooks/useType' +import { useApplication } from '../hooks/useUpdateApplication' +import { MINIMUM_WEEKDAYS, Routes } from '../lib/constants' import { advertValidationSchema, publishingValidationSchema, signatureValidationSchema, } from '../lib/dataSchema' -import { useApplication } from '../hooks/useUpdateApplication' -import { ZodCustomIssue } from 'zod' -import { useType } from '../hooks/useType' -import { useDepartment } from '../hooks/useDepartment' -import { usePrice } from '../hooks/usePrice' -import { useEffect } from 'react' +import { advert, error, publishing, summary } from '../lib/messages' import { signatures } from '../lib/messages/signatures' +import { OJOIFieldBaseProps } from '../lib/types' +import { addWeekdays, getFastTrack, parseZodIssue } from '../lib/utils' export const Summary = ({ application, diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Review/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Review/index.tsx index d335f9f4adfa..6719a7276213 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Review/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Review/index.tsx @@ -1,6 +1,6 @@ import { FieldBaseProps } from '@island.is/application/types' -import { useAuth } from '@island.is/auth/react' import { Box } from '@island.is/island-ui/core' +import { useUserInfo } from '@island.is/react-spa/bff' import { FC, useState } from 'react' import { ReviewState } from '../../shared' import { ApplicationStatus } from '../ApplicationStatus' @@ -8,7 +8,7 @@ import { Overview } from '../Overview' import { ReviewConclusion } from '../ReviewConclusion' export const Review: FC> = (props) => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [step, setStep] = useState('states') const reviewerNationalId = userInfo?.profile.nationalId || null diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Review/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Review/index.tsx index d335f9f4adfa..6719a7276213 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Review/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Review/index.tsx @@ -1,6 +1,6 @@ import { FieldBaseProps } from '@island.is/application/types' -import { useAuth } from '@island.is/auth/react' import { Box } from '@island.is/island-ui/core' +import { useUserInfo } from '@island.is/react-spa/bff' import { FC, useState } from 'react' import { ReviewState } from '../../shared' import { ApplicationStatus } from '../ApplicationStatus' @@ -8,7 +8,7 @@ import { Overview } from '../Overview' import { ReviewConclusion } from '../ReviewConclusion' export const Review: FC> = (props) => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [step, setStep] = useState('states') const reviewerNationalId = userInfo?.profile.nationalId || null diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Review/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Review/index.tsx index e3504e589fed..1bb68e141442 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Review/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Review/index.tsx @@ -1,19 +1,19 @@ +import { getValueViaPath } from '@island.is/application/core' import { FieldBaseProps } from '@island.is/application/types' -import { FC, useState } from 'react' import { Box } from '@island.is/island-ui/core' +import { useUserInfo } from '@island.is/react-spa/bff' +import { FC, useState } from 'react' +import { CoOwnerAndOperator, ReviewState } from '../../shared' import { ApplicationStatus } from '../ApplicationStatus' -import { Overview } from '../Overview' -import { ReviewConclusion } from '../ReviewConclusion' import { Insurance } from '../Insurance' +import { Overview } from '../Overview' import { ReviewCoOwnerAndOperatorRepeater } from '../ReviewCoOwnerAndOperatorRepeater' -import { CoOwnerAndOperator, ReviewState } from '../../shared' -import { getValueViaPath } from '@island.is/application/core' -import { useAuth } from '@island.is/auth/react' +import { ReviewConclusion } from '../ReviewConclusion' import { ReviewMainOperator } from '../ReviewMainOperator' export const Review: FC> = (props) => { const { application } = props - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [step, setStep] = useState('states') const [insurance, setInsurance] = useState( getValueViaPath(application.answers, 'insurance.value', undefined), diff --git a/libs/application/types/src/lib/Condition.ts b/libs/application/types/src/lib/Condition.ts index 6deb44e11833..65f9df3f3004 100644 --- a/libs/application/types/src/lib/Condition.ts +++ b/libs/application/types/src/lib/Condition.ts @@ -1,5 +1,5 @@ -import { FormValue, ExternalData } from './Application' -import { User } from '@island.is/shared/types' +import { BffUser } from '@island.is/shared/types' +import { ExternalData, FormValue } from './Application' export enum Comparators { EQUALS = 'eq', @@ -46,7 +46,7 @@ export type StaticCheck = QuestionCheck | ExternalDataCheck | UserPropCheck export type DynamicCheck = ( formValue: FormValue, externalData: ExternalData, - user: User | null, + user: BffUser | null, ) => boolean export type SingleConditionCheck = StaticCheck | DynamicCheck diff --git a/libs/application/ui-shell/src/components/ConditionHandler.tsx b/libs/application/ui-shell/src/components/ConditionHandler.tsx index 9b2e6bf0f865..c335aebe36ca 100644 --- a/libs/application/ui-shell/src/components/ConditionHandler.tsx +++ b/libs/application/ui-shell/src/components/ConditionHandler.tsx @@ -6,10 +6,10 @@ import { FormItemTypes, FormValue, } from '@island.is/application/types' -import { useAuth } from '@island.is/auth/react' -import { FieldDef, MultiFieldScreen } from '../types' +import { useUserInfo } from '@island.is/react-spa/bff' import { convertMultiFieldToScreen } from '../reducer/reducerUtils' +import { FieldDef, MultiFieldScreen } from '../types' // Use this component to optimize performance for applying conditions in response to form value changes for multifields export const ConditionHandler: FC< @@ -21,7 +21,7 @@ export const ConditionHandler: FC< }> > = ({ answerQuestions, externalData, formValue, screen }) => { const data = useWatch({ defaultValue: formValue }) as FormValue - const { userInfo: user } = useAuth() + const user = useUserInfo() useEffect(() => { const newScreen = convertMultiFieldToScreen( diff --git a/libs/application/ui-shell/src/components/DelegationsScreen.tsx b/libs/application/ui-shell/src/components/DelegationsScreen.tsx index 37b76da98f6e..440e528d1ccf 100644 --- a/libs/application/ui-shell/src/components/DelegationsScreen.tsx +++ b/libs/application/ui-shell/src/components/DelegationsScreen.tsx @@ -1,31 +1,31 @@ -import intersection from 'lodash/intersection' -import React, { Dispatch, SetStateAction, useEffect, useState } from 'react' import { useQuery } from '@apollo/client' -import { useAuth } from '@island.is/auth/react' -import { ACTOR_DELEGATIONS } from '@island.is/application/graphql' -import { - Text, - Box, - Page, - GridContainer, - Stack, - Icon, -} from '@island.is/island-ui/core' import { coreDelegationsMessages, coreMessages, getTypeFromSlug, } from '@island.is/application/core' +import { ACTOR_DELEGATIONS } from '@island.is/application/graphql' import { getApplicationTemplateByTypeId } from '@island.is/application/template-loader' -import { LoadingShell } from './LoadingShell' -import { format as formatKennitala } from 'kennitala' -import { useLocale } from '@island.is/localization' -import { ScreenType, DelegationsScreenDataType, Delegation } from '../types' import { FeatureFlagClient, Features } from '@island.is/feature-flags' +import { + Box, + GridContainer, + Icon, + Page, + Stack, + Text, +} from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { useBff } from '@island.is/react-spa/bff' import { useFeatureFlagClient } from '@island.is/react/feature-flags' -import { useNavigate } from 'react-router-dom' import * as kennitala from 'kennitala' +import { format as formatKennitala } from 'kennitala' +import intersection from 'lodash/intersection' +import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { Delegation, DelegationsScreenDataType, ScreenType } from '../types' import * as styles from './DelegationsScreen.css' +import { LoadingShell } from './LoadingShell' enum DelegationType { LegalGuardian = 'LegalGuardian', @@ -50,7 +50,7 @@ export const DelegationsScreen = ({ }) const { formatMessage } = useLocale() const type = getTypeFromSlug(slug) - const { switchUser, userInfo: user } = useAuth() + const { switchUser, userInfo: user } = useBff() const featureFlagClient: FeatureFlagClient = useFeatureFlagClient() const navigate = useNavigate() diff --git a/libs/application/ui-shell/src/components/ScreenFooter.tsx b/libs/application/ui-shell/src/components/ScreenFooter.tsx index 4e4ed12dd51a..253e4665c354 100644 --- a/libs/application/ui-shell/src/components/ScreenFooter.tsx +++ b/libs/application/ui-shell/src/components/ScreenFooter.tsx @@ -1,17 +1,17 @@ -import { FC } from 'react' -import { Box, Button, ButtonTypes, GridColumn } from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { formatText, coreMessages } from '@island.is/application/core' +import { coreMessages, formatText } from '@island.is/application/core' import { Application, - FormModes, - SubmitField, CallToAction, + FormModes, FormText, + SubmitField, } from '@island.is/application/types' +import { Box, Button, ButtonTypes, GridColumn } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { FC } from 'react' +import { useUserInfo } from '@island.is/react-spa/bff' import * as styles from './ScreenFooter.css' -import { useAuth } from '@island.is/auth/react' interface FooterProps { application: Application @@ -70,7 +70,7 @@ export const ScreenFooter: FC> = ({ nextButtonText, }) => { const { formatMessage } = useLocale() - const { userInfo: user } = useAuth() + const user = useUserInfo() const hasSubmitField = submitField !== undefined const isLastScreen = activeScreenIndex === numberOfScreens - 1 const showGoBack = diff --git a/libs/application/ui-shell/src/lib/FormShell.spec.tsx b/libs/application/ui-shell/src/lib/FormShell.spec.tsx index ad1e34606324..050d8886064f 100644 --- a/libs/application/ui-shell/src/lib/FormShell.spec.tsx +++ b/libs/application/ui-shell/src/lib/FormShell.spec.tsx @@ -1,19 +1,25 @@ -import { act, render, screen } from '@testing-library/react' import '@testing-library/jest-dom' +import { act, render, screen } from '@testing-library/react' import { z } from 'zod' -import { FormShell } from './FormShell' -import { buildForm, buildDescriptionField } from '@island.is/application/core' +import { ApolloProvider } from '@apollo/client' +import { buildDescriptionField, buildForm } from '@island.is/application/core' +import { client } from '@island.is/application/graphql' import { Application, + ApplicationStatus, ApplicationTypes, Form, - ApplicationStatus, } from '@island.is/application/types' -import { initializeClient } from '@island.is/application/graphql' -import { ApolloProvider } from '@apollo/client' +import { applicationSystemScopes } from '@island.is/auth/scopes' import { LocaleProvider } from '@island.is/localization' -import { createMemoryRouter, RouterProvider } from 'react-router-dom' +import { BffProvider, createMockedInitialState } from '@island.is/react-spa/bff' +import { RouterProvider, createMemoryRouter } from 'react-router-dom' +import { FormShell } from './FormShell' + +const mockedInitialState = createMockedInitialState({ + scopes: applicationSystemScopes, +}) describe(' FormShell', () => { const applicant = '1111112219' @@ -64,9 +70,14 @@ describe(' FormShell', () => { let baseElement const wrapper = await render( - + - + + + , ) @@ -81,9 +92,14 @@ describe(' FormShell', () => { it('should render the application title', async () => { await act(async () => { render( - + - + + + , ) diff --git a/libs/application/ui-shell/src/lib/FormShell.tsx b/libs/application/ui-shell/src/lib/FormShell.tsx index 05f2eeffcf95..f2fc28a32899 100644 --- a/libs/application/ui-shell/src/lib/FormShell.tsx +++ b/libs/application/ui-shell/src/lib/FormShell.tsx @@ -5,32 +5,29 @@ import { Form, FormModes, Schema, - SectionChildren, } from '@island.is/application/types' import { Box, GridColumn, GridContainer, GridRow, - Text, } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' +import { ErrorShell } from '../components/ErrorShell' +import FormStepper from '../components/FormStepper' import Screen from '../components/Screen' +import { useHeaderInfo } from '../context/HeaderInfoProvider' +import { useApplicationTitle } from '../hooks/useApplicationTitle' +import { useHistorySync } from '../hooks/useHistorySync' import { ApplicationReducer, initializeReducer, } from '../reducer/ApplicationFormReducer' import { ActionTypes } from '../reducer/ReducerTypes' -import { useHistorySync } from '../hooks/useHistorySync' -import { useApplicationTitle } from '../hooks/useApplicationTitle' -import { useHeaderInfo } from '../context/HeaderInfoProvider' -import * as styles from './FormShell.css' -import { ErrorShell } from '../components/ErrorShell' -import { useAuth } from '@island.is/auth/react' -import { useLocale } from '@island.is/localization' -import { MessageDescriptor } from 'react-intl' -import FormStepper from '../components/FormStepper' import { getFormComponent } from '../utils' +import * as styles from './FormShell.css' export const FormShell: FC< React.PropsWithChildren<{ @@ -42,7 +39,7 @@ export const FormShell: FC< > = ({ application, nationalRegistryId, form, dataSchema }) => { const [updateForbidden, setUpdateForbidden] = useState(false) const { setInfo } = useHeaderInfo() - const { userInfo: user } = useAuth() + const user = useUserInfo() const [state, dispatch] = useReducer( ApplicationReducer, { diff --git a/libs/application/ui-shell/src/reducer/ReducerTypes.ts b/libs/application/ui-shell/src/reducer/ReducerTypes.ts index 5d404b6808c0..9f3c33254544 100644 --- a/libs/application/ui-shell/src/reducer/ReducerTypes.ts +++ b/libs/application/ui-shell/src/reducer/ReducerTypes.ts @@ -4,8 +4,8 @@ import { Schema, Section, } from '@island.is/application/types' +import { BffUser } from '@island.is/shared/types' import { FormScreen } from '../types' -import { User } from '@island.is/shared/types' export interface ApplicationUIState { application: Application @@ -16,7 +16,7 @@ export interface ApplicationUIState { screens: FormScreen[] sections: Section[] historyReason: 'initial' | 'navigate' | 'pop' - user: User | null + user: BffUser | null } export enum ActionTypes { diff --git a/libs/application/ui-shell/src/reducer/reducerUtils.ts b/libs/application/ui-shell/src/reducer/reducerUtils.ts index fd0b20f12b52..72c76a0f9c92 100644 --- a/libs/application/ui-shell/src/reducer/reducerUtils.ts +++ b/libs/application/ui-shell/src/reducer/reducerUtils.ts @@ -1,5 +1,3 @@ -import get from 'lodash/get' -import isArray from 'lodash/isArray' import { findSectionIndex, findSubSectionIndex, @@ -13,9 +11,9 @@ import { ExternalData, ExternalDataProvider, Field, + FieldTypes, Form, FormItemTypes, - FieldTypes, FormLeaf, FormNode, FormValue, @@ -24,7 +22,10 @@ import { Section, SubSection, } from '@island.is/application/types' +import get from 'lodash/get' +import isArray from 'lodash/isArray' +import { BffUser } from '@island.is/shared/types' import { ExternalDataProviderScreen, FieldDef, @@ -33,7 +34,6 @@ import { RepeaterScreen, } from '../types' import { answerIsMissing } from '../utils' -import { User } from '@island.is/shared/types' export const screenRequiresAnswer = (screen: FormScreen) => { if (!screen.isNavigable) { @@ -188,7 +188,7 @@ const convertFieldToScreen = ( isParentNavigable = true, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): FieldDef => { return { ...field, @@ -207,7 +207,7 @@ const convertDataProviderToScreen = ( isParentNavigable = true, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): ExternalDataProviderScreen => { return { ...externalDataProvider, @@ -226,7 +226,7 @@ export const convertMultiFieldToScreen = ( isParentNavigable = true, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): MultiFieldScreen => { let isMultiFieldVisible = false const children: FieldDef[] = [] @@ -267,7 +267,7 @@ const convertRepeaterToScreens = ( isParentNavigable = true, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): FormScreen[] => { const { id, children } = repeater const newScreens: FormScreen[] = [] @@ -339,7 +339,7 @@ const convertLeafToScreens = ( isParentNavigable = true, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): FormScreen[] => { if (leaf.type === FormItemTypes.MULTI_FIELD) { return [ @@ -398,7 +398,7 @@ const convertFormNodeToScreens = ( isParentNavigable: boolean, sectionIndex: number, subSectionIndex: number, - user: User | null, + user: BffUser | null, ): FormScreen[] => { const { children } = formNode @@ -472,7 +472,7 @@ export const convertFormToScreens = ( form: Form, answers: FormValue, externalData: ExternalData, - user: User | null, + user: BffUser | null, ): FormScreen[] => { return convertFormNodeToScreens( form, @@ -490,7 +490,7 @@ export const getNavigableSectionsInForm = ( form: Form, answers: FormValue, externalData: ExternalData, - user: User | null, + user: BffUser | null, ): Section[] => { const sections: Section[] = [] diff --git a/libs/auth/react/src/lib/auth/AuthContext.tsx b/libs/auth/react/src/lib/auth/AuthContext.tsx index 007979dfc52b..e6d65ea5d94a 100644 --- a/libs/auth/react/src/lib/auth/AuthContext.tsx +++ b/libs/auth/react/src/lib/auth/AuthContext.tsx @@ -1,8 +1,5 @@ -import { jwtDecode } from 'jwt-decode' import { createContext, useContext } from 'react' -import { User } from '@island.is/shared/types' - import { AuthReducerState, initialState } from './Auth.state' export interface AuthContextType extends AuthReducerState { @@ -31,26 +28,37 @@ export const defaultAuthContext = { export const AuthContext = createContext(defaultAuthContext) -export const useAuth: () => AuthContextType = () => useContext(AuthContext) +const warnDeprecated = (hookName: string, alternative: string) => { + console.warn( + `[Deprecation Warning] "${hookName}" is being replaced by BFF auth pattern Please use "${alternative}" from "libs/react-spa/bff".`, + ) +} -export const useUserInfo = () => { - const { userInfo } = useContext(AuthContext) +/** + * @deprecated Use useBff from `libs/react-spa/bff` instead. + */ +export const useAuth = () => { + warnDeprecated('useAuth', 'useBff') - if (!userInfo) { - throw new Error('User info is not available. Is the user authenticated?') + const context = useContext(AuthContext) + + if (!context) { + throw new Error('useAuth must be used within a AuthProvider') } - return userInfo + return context } -export const useUserDecodedIdToken = () => { - const userInfo = useUserInfo() +/** + * @deprecated Use useUserInfo from `libs/react-spa/bff` instead. + */ +export const useUserInfo = () => { + warnDeprecated('useUserInfo', 'useUserInfo') + const { userInfo } = useAuth() - if (!userInfo.id_token) { - throw new Error( - 'Decoded ID token is not available. Is the user authenticated?', - ) + if (!userInfo) { + throw new Error('User info is not available. Is the user authenticated?') } - return jwtDecode(userInfo.id_token) + return userInfo } diff --git a/libs/auth/scopes/src/index.ts b/libs/auth/scopes/src/index.ts index ddb61ffe1938..8beed0a9f8ae 100644 --- a/libs/auth/scopes/src/index.ts +++ b/libs/auth/scopes/src/index.ts @@ -1,28 +1,29 @@ export * from './lib/admin-portal.scope' -export * from './lib/sessions-scope' export * from './lib/air-discount-scheme.scope' -export * from './lib/application.scope' +export * from './lib/aosh.scope' export * from './lib/api.scope' +export * from './lib/application.scope' export * from './lib/auth.scope' export * from './lib/authAdmin.scope' +export * from './lib/clients/admin-portal-scopes' +export * from './lib/clients/application-system-scopes' +export * from './lib/clients/service-portal-scopes' +export * from './lib/district-commissioners.scope' export * from './lib/documents.scope' -export * from './lib/userProfile.scope' -export * from './lib/nationalRegistry.scope' export * from './lib/endorsements.scope' -export * from './lib/municipalitiesFinancialAid.scope' export * from './lib/fishingLicense.scope' -export * from './lib/vehicles.scope' +export * from './lib/hms.scope' +export * from './lib/judicial-system.scope' +export * from './lib/license-api.scope' +export * from './lib/mms.scope' +export * from './lib/municipalitiesFinancialAid.scope' +export * from './lib/nationalRegistry.scope' +export * from './lib/recycling-fund.scope' export * from './lib/rls.scope' -export * from './lib/tr.scope' +export * from './lib/sessions-scope' export * from './lib/sjukra.scope' -export * from './lib/license-api.scope' export * from './lib/stjornarradid.scope' -export * from './lib/aosh.scope' -export * from './lib/district-commissioners.scope' -export * from './lib/hms.scope' -export * from './lib/recycling-fund.scope' +export * from './lib/tr.scope' export * from './lib/universityGateway.scope' -export * from './lib/mms.scope' -export * from './lib/judicial-system.scope' -export * from './lib/clients/admin-portal-scopes' -export * from './lib/clients/service-portal-scopes' +export * from './lib/userProfile.scope' +export * from './lib/vehicles.scope' diff --git a/libs/auth/scopes/src/lib/clients/application-system-scopes.ts b/libs/auth/scopes/src/lib/clients/application-system-scopes.ts new file mode 100644 index 000000000000..26f5ae2d271f --- /dev/null +++ b/libs/auth/scopes/src/lib/clients/application-system-scopes.ts @@ -0,0 +1,32 @@ +import { ApiScope } from '../api.scope' +import { ApplicationScope } from '../application.scope' +import { AuthScope } from '../auth.scope' +import { EndorsementsScope } from '../endorsements.scope' +import { MunicipalitiesFinancialAidScope } from '../municipalitiesFinancialAid.scope' +import { NationalRegistryScope } from '../nationalRegistry.scope' +import { UserProfileScope } from '../userProfile.scope' + +export const applicationSystemScopes = [ + 'api_resource.scope', + ApiScope.assets, + ApiScope.carRecycling, + ApiScope.energyFunds, + ApiScope.fishingLicense, + ApiScope.internal, + ApiScope.internalProcuring, + ApiScope.licenses, + ApiScope.meDetails, + ApiScope.samgongustofaVehicles, + ApiScope.signatureCollection, + ApiScope.vinnueftirlitid, + ApplicationScope.read, + ApplicationScope.write, + AuthScope.actorDelegations, + EndorsementsScope.main, + MunicipalitiesFinancialAidScope.applicant, + MunicipalitiesFinancialAidScope.read, + MunicipalitiesFinancialAidScope.write, + NationalRegistryScope.individuals, + UserProfileScope.read, + UserProfileScope.write, +] diff --git a/libs/auth/scopes/src/lib/clients/service-portal-scopes.ts b/libs/auth/scopes/src/lib/clients/service-portal-scopes.ts index a92a6a77914e..2ddd1818346c 100644 --- a/libs/auth/scopes/src/lib/clients/service-portal-scopes.ts +++ b/libs/auth/scopes/src/lib/clients/service-portal-scopes.ts @@ -30,6 +30,7 @@ export const servicePortalScopes = [ ApiScope.internal, ApiScope.internalProcuring, ApiScope.meDetails, + ApiScope.lawAndOrder, ApiScope.licenses, ApiScope.licensesVerify, ApiScope.company, diff --git a/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx b/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx index 2734c258ba63..dabe74719e1b 100644 --- a/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx +++ b/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx @@ -1,24 +1,22 @@ -import { BrowserRouter } from 'react-router-dom' -import { ReactNode, FC } from 'react' import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client' +import { BffProvider, createMockedInitialState } from '@island.is/react-spa/bff' +import { FeatureFlagClient } from '@island.is/react/feature-flags' +import { defaultLanguage } from '@island.is/shared/constants' import { renderHook } from '@testing-library/react' +import { FC, ReactNode } from 'react' import { IntlProvider } from 'react-intl' -import { MockedAuthProvider } from '@island.is/auth/react' -import { defaultLanguage } from '@island.is/shared/constants' +import { BrowserRouter } from 'react-router-dom' import { testCases } from '../../test/useSingleNavigationItem-test-cases' +import { PortalContext, PortalMeta } from '../components/PortalProvider' import { PortalModule, PortalNavigationItem, PortalRoute, } from '../types/portalCore' -import { FeatureFlagClient } from '@island.is/react/feature-flags' -import { createMockUser } from '@island.is/auth/react' -import { useSingleNavigationItem } from './useSingleNavigationItem' import { prepareRouterData } from '../utils/router/prepareRouterData' -import { PortalContext, PortalMeta } from '../components/PortalProvider' +import { useSingleNavigationItem } from './useSingleNavigationItem' -const user = { profile: { name: 'Peter' } } -const userInfo = createMockUser(user) +const mockedInitialState = createMockedInitialState() const MockedPortalProvider: FC< React.PropsWithChildren<{ @@ -53,7 +51,7 @@ describe('useSingleNavigationItem hook', () => { beforeAll(async () => { const routerData = await prepareRouterData({ - userInfo, + userInfo: mockedInitialState.userInfo, featureFlagClient: {} as FeatureFlagClient, modules: testModules, }) @@ -70,7 +68,11 @@ describe('useSingleNavigationItem hook', () => { // Ignoring error because we don't need translations for tests }} > - + { {children} - +
) diff --git a/libs/portals/my-pages/assets/src/screens/Overview/Overview.tsx b/libs/portals/my-pages/assets/src/screens/Overview/Overview.tsx index a82c0c953f01..7c50b7c03e90 100644 --- a/libs/portals/my-pages/assets/src/screens/Overview/Overview.tsx +++ b/libs/portals/my-pages/assets/src/screens/Overview/Overview.tsx @@ -12,15 +12,12 @@ import { import { useLocale, useNamespaces } from '@island.is/localization' import { CardLoader, - EmptyState, - ErrorScreen, FootNote, formSubmit, IntroHeader, m, SAMGONGUSTOFA_SLUG, } from '@island.is/portals/my-pages/core' -import { useUserInfo } from '@island.is/auth/react' import { VehicleCard } from '../../components/VehicleCard' import { @@ -36,6 +33,7 @@ import { exportVehicleOwnedDocument } from '../../utils/vehicleOwnedMapper' import useDebounce from 'react-use/lib/useDebounce' import { VehiclesDetail } from '@island.is/api/schema' import { Problem } from '@island.is/react-spa/shared' +import { useUserInfo } from '@island.is/react-spa/bff' const defaultFilterValues = { searchQuery: '', diff --git a/libs/portals/my-pages/core/src/utils/documentFormSubmission.ts b/libs/portals/my-pages/core/src/utils/documentFormSubmission.ts index 2f282e0a1f12..5bcf0d83cd29 100644 --- a/libs/portals/my-pages/core/src/utils/documentFormSubmission.ts +++ b/libs/portals/my-pages/core/src/utils/documentFormSubmission.ts @@ -1,8 +1,10 @@ -import { getAccessToken } from '@island.is/auth/react' +import { createBffUrlGenerator } from '@island.is/react-spa/bff' export const formSubmit = async (url: string, annual?: boolean) => { - const token = await getAccessToken() - if (!token) return + const bffUrlGenerator = createBffUrlGenerator() + const bffUrl = bffUrlGenerator('/api', { + url, + }) // Create form elements const form = document.createElement('form') @@ -12,7 +14,7 @@ export const formSubmit = async (url: string, annual?: boolean) => { // Form values form.method = 'post' - form.action = url + form.action = bffUrl form.target = '_blank' if (annual) { @@ -24,11 +26,6 @@ export const formSubmit = async (url: string, annual?: boolean) => { annualInput.value = 'true' } - // National Id values - tokenInput.type = 'hidden' - tokenInput.name = '__accessToken' - tokenInput.value = token - document.body.appendChild(form) form.submit() document.body.removeChild(form) diff --git a/libs/portals/my-pages/documents/src/components/DocumentActionBar/DocumentActionBarV2.tsx b/libs/portals/my-pages/documents/src/components/DocumentActionBar/DocumentActionBarV2.tsx index b9ad8589805b..7769ed7d628c 100644 --- a/libs/portals/my-pages/documents/src/components/DocumentActionBar/DocumentActionBarV2.tsx +++ b/libs/portals/my-pages/documents/src/components/DocumentActionBar/DocumentActionBarV2.tsx @@ -1,17 +1,16 @@ import { - Icon, Box, BoxProps, - LoadingDots, Button, + Icon, + LoadingDots, } from '@island.is/island-ui/core' -import { useUserInfo } from '@island.is/auth/react' -import { Tooltip, m } from '@island.is/portals/my-pages/core' import { useLocale } from '@island.is/localization' -import { ActiveDocumentType2 } from '../../lib/types' -import { useDocumentContext } from '../../screens/Overview/DocumentContext' +import { Tooltip, m } from '@island.is/portals/my-pages/core' import { useDocumentList } from '../../hooks/useDocumentList' import { useMailAction } from '../../hooks/useMailActionV2' +import { ActiveDocumentType2 } from '../../lib/types' +import { useDocumentContext } from '../../screens/Overview/DocumentContext' import { downloadFile } from '../../utils/downloadDocumentV2' import * as styles from './DocumentActionBar.css' @@ -37,7 +36,6 @@ export const DocumentActionBar: React.FC = ({ const { activeDocument } = useDocumentContext() const { fetchObject, refetch } = useDocumentList() - const userInfo = useUserInfo() const { formatMessage } = useLocale() @@ -144,7 +142,7 @@ export const DocumentActionBar: React.FC = ({ icon="download" iconType={'outline'} onClick={() => - downloadFile(activeDocument, userInfo, 'download') + downloadFile({ doc: activeDocument, query: 'download' }) } size="medium" colorScheme="light" @@ -158,7 +156,11 @@ export const DocumentActionBar: React.FC = ({ circle icon="print" iconType={'outline'} - onClick={() => downloadFile(activeDocument, userInfo)} + onClick={() => + downloadFile({ + doc: activeDocument, + }) + } size="medium" colorScheme="light" /> diff --git a/libs/portals/my-pages/documents/src/components/DocumentActions/DocumentActionsV3.tsx b/libs/portals/my-pages/documents/src/components/DocumentActions/DocumentActionsV3.tsx index fbe5092c62a3..d48a39bac934 100644 --- a/libs/portals/my-pages/documents/src/components/DocumentActions/DocumentActionsV3.tsx +++ b/libs/portals/my-pages/documents/src/components/DocumentActions/DocumentActionsV3.tsx @@ -1,12 +1,11 @@ import { AlertMessage, Box, Button } from '@island.is/island-ui/core' import { IconMapIcon } from '@island.is/island-ui/core/types' -import { sendForm } from '../../utils/downloadDocumentV2' -import { useUserInfo } from '@island.is/auth/react' +import { useBffUrlGenerator } from '@island.is/react-spa/bff' import { useDocumentContext } from '../../screens/Overview/DocumentContext' const DocumentActions = () => { const { activeDocument } = useDocumentContext() - const userInfo = useUserInfo() + const bffUrlGenerator = useBffUrlGenerator() const DEFAULT_ICON: IconMapIcon = 'document' const actions = activeDocument?.actions const alert = activeDocument?.alert @@ -25,44 +24,47 @@ const DocumentActions = () => { flexDirection="row" flexWrap="wrap" > - {actions.map((a) => { - return ( - - {a.type === 'url' && a.data && ( - - - - )} - {a.type === 'file' && activeDocument && ( + {actions.map((a, index) => ( + + {a.type === 'url' && a.data && ( + - )} - - ) - })} + + )} + {a.type === 'file' && activeDocument && ( + + )} + + ))} )} diff --git a/libs/portals/my-pages/documents/src/components/DocumentRenderer/PdfDocument.tsx b/libs/portals/my-pages/documents/src/components/DocumentRenderer/PdfDocument.tsx index 832fbaf8f4d5..f946c4798c6a 100644 --- a/libs/portals/my-pages/documents/src/components/DocumentRenderer/PdfDocument.tsx +++ b/libs/portals/my-pages/documents/src/components/DocumentRenderer/PdfDocument.tsx @@ -1,12 +1,11 @@ -import { useRef, useState, useEffect } from 'react' -import { useLocale } from '@island.is/localization' import { Box, Button, PdfViewer, Text } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' import { Modal, m } from '@island.is/portals/my-pages/core' -import { useUserInfo } from '@island.is/auth/react' +import { Problem } from '@island.is/react-spa/shared' +import { useEffect, useRef, useState } from 'react' import { ActiveDocumentType2 } from '../../lib/types' import { downloadFile } from '../../utils/downloadDocumentV2' import { messages } from '../../utils/messages' -import { Problem } from '@island.is/react-spa/shared' import * as styles from './DocumentRenderer.css' type PdfDocumentProps = { @@ -23,7 +22,6 @@ export const PdfDocument: React.FC = ({ onClose, }) => { const [scalePDF, setScalePDF] = useState(initScale) - const userInfo = useUserInfo() const ref = useRef(null) const { formatMessage } = useLocale() @@ -120,7 +118,11 @@ export const PdfDocument: React.FC = ({ > diff --git a/libs/portals/my-pages/documents/src/utils/downloadDocumentV2.ts b/libs/portals/my-pages/documents/src/utils/downloadDocumentV2.ts index ab402cc8aa3c..d5d46195e54e 100644 --- a/libs/portals/my-pages/documents/src/utils/downloadDocumentV2.ts +++ b/libs/portals/my-pages/documents/src/utils/downloadDocumentV2.ts @@ -1,89 +1,37 @@ -import { User } from '@island.is/auth/react' +import { createBffUrlGenerator } from '@island.is/react-spa/bff' import { ActiveDocumentType2 } from '../lib/types' -export const sendForm = async (id: string, url: string, userInfo: User) => { - // Create form elements - const form = document.createElement('form') - const documentIdInput = document.createElement('input') - const tokenInput = document.createElement('input') - - const token = userInfo?.access_token - - if (!token) return - - form.appendChild(documentIdInput) - form.appendChild(tokenInput) - - // Form values - form.method = 'post' - form.action = url - form.target = '_blank' - - // Document Id values - documentIdInput.type = 'hidden' - documentIdInput.name = 'documentId' - documentIdInput.value = id - - // National Id values - tokenInput.type = 'hidden' - tokenInput.name = '__accessToken' - tokenInput.value = token - - document.body.appendChild(form) - form.submit() - document.body.removeChild(form) +type DownloadFileArgs = { + doc: ActiveDocumentType2 + query?: string } -export const downloadFile = async ( - doc: ActiveDocumentType2, - userInfo: User, - query?: string, -) => { +export const downloadFile = async ({ doc, query }: DownloadFileArgs) => { let html: string | undefined = undefined - if (doc?.document.type === 'HTML') { + + if (doc.document?.type === 'HTML') { html = doc.document.value && doc.document.value.length > 0 ? doc?.document.value : undefined } + + const downloadUrl = doc?.downloadUrl + if (html) { setTimeout(() => { const win = window.open('', '_blank') win && html && win.document.write(html) win?.focus() }, 250) - } else { - // Create form elements - const form = document.createElement('form') - const documentIdInput = document.createElement('input') - const tokenInput = document.createElement('input') - - const token = userInfo?.access_token - - if (!token) return - - form.appendChild(documentIdInput) - form.appendChild(tokenInput) - - const url = query ? `${doc?.downloadUrl}?action=${query}` : doc?.downloadUrl + } else if (downloadUrl) { + const bffUrlGenerator = createBffUrlGenerator() + const bffUrl = bffUrlGenerator('/api', { + url: query ? `${downloadUrl}?action=${query}` : downloadUrl, + }) - // Form values - form.method = 'post' - form.action = doc?.downloadUrl ? url : '' - form.target = '_blank' - - // Document Id values - documentIdInput.type = 'hidden' - documentIdInput.name = 'documentId' - documentIdInput.value = doc?.id ?? '' - - // National Id values - tokenInput.type = 'hidden' - tokenInput.name = '__accessToken' - tokenInput.value = token - - document.body.appendChild(form) - form.submit() - document.body.removeChild(form) + window.open(bffUrl, '_blank') + } else { + console.error('No download url found') } } diff --git a/libs/portals/my-pages/finance/src/screens/FinanceSchedule/FinanceSchedule.tsx b/libs/portals/my-pages/finance/src/screens/FinanceSchedule/FinanceSchedule.tsx index 79a9b99d44d9..f619ca7a823a 100644 --- a/libs/portals/my-pages/finance/src/screens/FinanceSchedule/FinanceSchedule.tsx +++ b/libs/portals/my-pages/finance/src/screens/FinanceSchedule/FinanceSchedule.tsx @@ -18,7 +18,7 @@ import { import { checkDelegation } from '@island.is/shared/utils' import FinanceScheduleTable from '../../components/FinanceScheduleTable/FinanceScheduleTable' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { m as messages } from '../../lib/messages' import FinanceIntro from '../../components/FinanceIntro' import { useGetPaymentScheduleQuery } from './FinanceSchedule.generated' diff --git a/libs/portals/my-pages/finance/src/screens/FinanceStatus/FinanceStatus.tsx b/libs/portals/my-pages/finance/src/screens/FinanceStatus/FinanceStatus.tsx index 7ffedef09532..070b4f3c19d2 100644 --- a/libs/portals/my-pages/finance/src/screens/FinanceStatus/FinanceStatus.tsx +++ b/libs/portals/my-pages/finance/src/screens/FinanceStatus/FinanceStatus.tsx @@ -35,7 +35,7 @@ import { FinanceStatusOrganizationType, } from './FinanceStatusData.types' import * as styles from './Table.css' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import FinanceIntro from '../../components/FinanceIntro' import { useGetDebtStatusQuery, diff --git a/libs/portals/my-pages/graphql/src/lib/client.ts b/libs/portals/my-pages/graphql/src/lib/client.ts index 3ac76f40d454..a544cd6e8366 100644 --- a/libs/portals/my-pages/graphql/src/lib/client.ts +++ b/libs/portals/my-pages/graphql/src/lib/client.ts @@ -1,22 +1,18 @@ -import fetch from 'cross-fetch' import { ApolloClient, ApolloLink, HttpLink, InMemoryCache, } from '@apollo/client' +import fetch from 'cross-fetch' -import { RetryLink } from '@apollo/client/link/retry' import { onError } from '@apollo/client/link/error' -import { authLink } from '@island.is/auth/react' -import { getStaticEnv } from '@island.is/shared/utils' - -const uri = - getStaticEnv('SI_PUBLIC_GRAPHQL_API') ?? 'http://localhost:4444/api/graphql' +import { RetryLink } from '@apollo/client/link/retry' const httpLink = new HttpLink({ - uri: ({ operationName }) => `${uri}?op=${operationName}`, + uri: ({ operationName }) => `/bff/api/graphql?op=${operationName}`, fetch, + credentials: 'include', }) const retryLink = new RetryLink() @@ -33,7 +29,7 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { }) export const client = new ApolloClient({ - link: ApolloLink.from([retryLink, errorLink, authLink, httpLink]), + link: ApolloLink.from([retryLink, errorLink, httpLink]), cache: new InMemoryCache({ typePolicies: { UserProfile: { diff --git a/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx b/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx index 773ffa36a42c..990a8e0e11e4 100644 --- a/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx +++ b/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx @@ -1,4 +1,4 @@ -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { AlertMessage, Box, diff --git a/libs/portals/my-pages/information/src/components/NotificationSettings/UserProfileNotificationSettings.tsx b/libs/portals/my-pages/information/src/components/NotificationSettings/UserProfileNotificationSettings.tsx index 40ea4877dc8b..237c414c5368 100644 --- a/libs/portals/my-pages/information/src/components/NotificationSettings/UserProfileNotificationSettings.tsx +++ b/libs/portals/my-pages/information/src/components/NotificationSettings/UserProfileNotificationSettings.tsx @@ -1,21 +1,21 @@ -import React, { useEffect, useState } from 'react' -import { NotificationSettingsCard } from './cards/NotificationSettingsCard' import { Divider, SkeletonLoader, Stack, toast, } from '@island.is/island-ui/core' -import { useAuth } from '@island.is/auth/react' import { useLocale } from '@island.is/localization' +import { useEffect, useState } from 'react' +import { NotificationSettingsCard } from './cards/NotificationSettingsCard' -import { SettingsCard } from './cards/SettingsCard/SettingsCard' +import { useUserInfo } from '@island.is/react-spa/bff' +import { Problem } from '@island.is/react-spa/shared' import { mNotifications } from '../../lib/messages' +import { SettingsCard } from './cards/SettingsCard/SettingsCard' import { useUpdateUserProfileSettingsMutation, useUserProfileSettingsQuery, } from './graphql/UserProfile.generated' -import { Problem } from '@island.is/react-spa/shared' type UserProfileNotificationSettings = { documentNotifications: boolean @@ -23,7 +23,7 @@ type UserProfileNotificationSettings = { } export const UserProfileNotificationSettings = () => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() const { formatMessage } = useLocale() const { data: userProfile, diff --git a/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx b/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx index 943ab7adb8f2..3572dd96d6c1 100644 --- a/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx +++ b/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx @@ -1,7 +1,5 @@ import React, { FC, useEffect, useState } from 'react' -import { useAuth } from '@island.is/auth/react' -import { useLocale, useNamespaces } from '@island.is/localization' import { GridColumn, GridContainer, @@ -9,6 +7,7 @@ import { Input, PhoneInput, } from '@island.is/island-ui/core' +import { useLocale, useNamespaces } from '@island.is/localization' import { FeatureFlagClient, Features, @@ -20,19 +19,20 @@ import { useUserProfile, } from '@island.is/portals/my-pages/graphql' +import { useUserInfo } from '@island.is/react-spa/bff' import { msg } from '../../../../lib/messages' import { bankInfoObject } from '../../../../utils/bankInfoHelper' -import { OnboardingIntro } from './components/Intro' -import { InputSection } from './components/InputSection' -import { InputEmail } from './components/Inputs/Email' -import { InputPhone } from './components/Inputs/Phone' import { DropModal } from './components/DropModal' +import { InputSection } from './components/InputSection' import { BankInfoForm } from './components/Inputs/BankInfoForm' +import { InputEmail } from './components/Inputs/Email' import { Nudge } from './components/Inputs/Nudge' import { PaperMail } from './components/Inputs/PaperMail' +import { InputPhone } from './components/Inputs/Phone' import { ReadOnlyWithLinks } from './components/Inputs/ReadOnlyWithLinks' -import { DropModalType } from './types/form' +import { OnboardingIntro } from './components/Intro' import { useConfirmNudgeMutation } from './confirmNudge.generated' +import { DropModalType } from './types/form' enum IdsUserProfileLinks { EMAIL = '/app/user-profile/email', @@ -69,7 +69,7 @@ export const ProfileForm: FC> = ({ const [v2UserProfileEnabled, setV2UserProfileEnabled] = useState(false) const { deleteIslykillValue, loading: deleteLoading } = useDeleteIslykillValue() - const { authority, userInfo } = useAuth() + const userInfo = useUserInfo() const { data: userProfile, loading: userLoading, refetch } = useUserProfile() const featureFlagClient: FeatureFlagClient = useFeatureFlagClient() const { formatMessage } = useLocale() @@ -98,7 +98,9 @@ export const ProfileForm: FC> = ({ * By setting the state to update, the user will exit the onboarding process after updating the desired field. */ const getIDSLink = (linkPath: IdsUserProfileLinks) => { - return `${authority}${linkPath}?state=update&returnUrl=${encodeURIComponent( + return `${ + userInfo.profile.iss + }${linkPath}?state=update&returnUrl=${encodeURIComponent( window.location.toString(), )}` } diff --git a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboarding/UserOnboarding.tsx b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboarding/UserOnboarding.tsx index 037dd5727e7d..627e86f77bb3 100644 --- a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboarding/UserOnboarding.tsx +++ b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboarding/UserOnboarding.tsx @@ -1,20 +1,20 @@ import { Suspense, useEffect, useRef, useState } from 'react' -import { useAuth } from '@island.is/auth/react' import { UserProfileScope } from '@island.is/auth/scopes' import { Features, useFeatureFlagClient } from '@island.is/react/feature-flags' +import { useUserInfo } from '@island.is/react-spa/bff' +import { showUserOnboardingModal } from '../../../utils/showUserOnboardingModal' +import { UserOnboardingModal } from '../UserOnboardingModal/UserOnboardingModal' import { GetUserProfileQuery, useGetUserProfileQuery, } from './UserOnboarding.generated' -import { UserOnboardingModal } from '../UserOnboardingModal/UserOnboardingModal' -import { showUserOnboardingModal } from '../../../utils/showUserOnboardingModal' const isDevelopment = process.env.NODE_ENV === 'development' const UserOnboarding = () => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() // Use userRef to store the userProfile data to preserve the initial value after re-fetch. const userProfile = useRef(null) const isCompany = userInfo?.profile?.subjectType === 'legalEntity' diff --git a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/UserOnboardingModal.tsx b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/UserOnboardingModal.tsx index 96f7a5c2a51a..54256a68259a 100644 --- a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/UserOnboardingModal.tsx +++ b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/UserOnboardingModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { useLocale, useNamespaces } from '@island.is/localization' import { formatPlausiblePathToParams, @@ -11,8 +11,6 @@ import { GridContainer, Button, Box, - Columns, - Column, } from '@island.is/island-ui/core' import { m } from '@island.is/portals/my-pages/core' import { servicePortalCloseOnBoardingModal } from '@island.is/plausible' @@ -21,11 +19,11 @@ import { OnboardingHeader } from './components/Header' import ProfileForm from '../Forms/ProfileForm/ProfileForm' import * as styles from './UserOnboardingModal.css' import { onboardingModalStorage } from '../../../utils/showUserOnboardingModal' -import { useAuth } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' export const UserOnboardingModal = () => { useNamespaces('sp.settings') - const { userInfo } = useAuth() + const userInfo = useUserInfo() const [toggleCloseModal, setToggleCloseModal] = useState(false) const [canDropOverlay, setCanDropOverlay] = useState(false) const [formLoading, setFormLoadingState] = useState(false) diff --git a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/components/Header.tsx b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/components/Header.tsx index ce24187e7999..c1ec366da7da 100644 --- a/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/components/Header.tsx +++ b/libs/portals/my-pages/information/src/components/PersonalInformation/UserOnboardingModal/components/Header.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Box, Button, @@ -8,8 +7,9 @@ import { Text, } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' import { UserLanguageSwitcher } from '@island.is/shared/components' -import { useAuth } from '@island.is/auth/react' + interface OnboardingHeaderProps { dropOnboarding: () => void hideClose: boolean @@ -21,7 +21,7 @@ export const OnboardingHeader = ({ }: OnboardingHeaderProps) => { useNamespaces('sp.settings') const { formatMessage } = useLocale() - const { userInfo: user } = useAuth() + const user = useUserInfo() const closeWindow = formatMessage({ id: 'sp.settings:close-window', defaultMessage: 'Loka glugga', diff --git a/libs/portals/my-pages/information/src/screens/BioChild/BioChild.tsx b/libs/portals/my-pages/information/src/screens/BioChild/BioChild.tsx index 412a0500dec7..bc9cf3707fae 100644 --- a/libs/portals/my-pages/information/src/screens/BioChild/BioChild.tsx +++ b/libs/portals/my-pages/information/src/screens/BioChild/BioChild.tsx @@ -1,5 +1,5 @@ import { useParams } from 'react-router-dom' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { useLocale, useNamespaces } from '@island.is/localization' import { formatNationalId, diff --git a/libs/portals/my-pages/information/src/screens/ChildCustody/ChildCustody.tsx b/libs/portals/my-pages/information/src/screens/ChildCustody/ChildCustody.tsx index f3ee66e01f70..d99423311a7d 100644 --- a/libs/portals/my-pages/information/src/screens/ChildCustody/ChildCustody.tsx +++ b/libs/portals/my-pages/information/src/screens/ChildCustody/ChildCustody.tsx @@ -1,4 +1,4 @@ -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { defineMessage } from 'react-intl' import { Box, diff --git a/libs/portals/my-pages/information/src/screens/Company/CompanyInfo.tsx b/libs/portals/my-pages/information/src/screens/Company/CompanyInfo.tsx index 72f8b257e133..d8d6867ba4ef 100644 --- a/libs/portals/my-pages/information/src/screens/Company/CompanyInfo.tsx +++ b/libs/portals/my-pages/information/src/screens/Company/CompanyInfo.tsx @@ -13,7 +13,7 @@ import { UserInfoLine, } from '@island.is/portals/my-pages/core' import { dateFormat } from '@island.is/shared/constants' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { mCompany } from '../../lib/messages' import { useCompanyRegistryCompanyQuery } from './Company.generated' diff --git a/libs/portals/my-pages/information/src/screens/UserInfo/UserInfo.tsx b/libs/portals/my-pages/information/src/screens/UserInfo/UserInfo.tsx index e9b3c87bd1ab..bc46288257f5 100644 --- a/libs/portals/my-pages/information/src/screens/UserInfo/UserInfo.tsx +++ b/libs/portals/my-pages/information/src/screens/UserInfo/UserInfo.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { defineMessage } from 'react-intl' import { checkDelegation } from '@island.is/shared/utils' import { info } from 'kennitala' @@ -13,7 +12,7 @@ import { InfoLine, InfoLineStack, } from '@island.is/portals/my-pages/core' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { natRegGenderMessageDescriptorRecord, diff --git a/libs/portals/my-pages/information/src/screens/UserInfoOverview/UserInfoOverview.tsx b/libs/portals/my-pages/information/src/screens/UserInfoOverview/UserInfoOverview.tsx index 57a7d0d46b8a..c768bf37f0b7 100644 --- a/libs/portals/my-pages/information/src/screens/UserInfoOverview/UserInfoOverview.tsx +++ b/libs/portals/my-pages/information/src/screens/UserInfoOverview/UserInfoOverview.tsx @@ -7,7 +7,7 @@ import { m, THJODSKRA_SLUG, } from '@island.is/portals/my-pages/core' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { FamilyMemberCard } from '../../components/FamilyMemberCard/FamilyMemberCard' import { spmm } from '../../lib/messages' diff --git a/libs/portals/my-pages/information/src/screens/UserProfile/UserProfile.tsx b/libs/portals/my-pages/information/src/screens/UserProfile/UserProfile.tsx index 6d7ad9105236..c8f1b85e53d2 100644 --- a/libs/portals/my-pages/information/src/screens/UserProfile/UserProfile.tsx +++ b/libs/portals/my-pages/information/src/screens/UserProfile/UserProfile.tsx @@ -2,7 +2,7 @@ import { ISLANDIS_SLUG, IntroHeader, m } from '@island.is/portals/my-pages/core' import ProfileForm from '../../components/PersonalInformation/Forms/ProfileForm/ProfileForm' import { useUserProfile } from '@island.is/portals/my-pages/graphql' import { useLocale } from '@island.is/localization' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { msg } from '../../lib/messages' const UserProfile = () => { diff --git a/libs/portals/my-pages/occupational-licenses/src/screens/v1/EducationalDetail/EducationalDetail.tsx b/libs/portals/my-pages/occupational-licenses/src/screens/v1/EducationalDetail/EducationalDetail.tsx index 9dd0d21a442f..f6de56eab1ea 100644 --- a/libs/portals/my-pages/occupational-licenses/src/screens/v1/EducationalDetail/EducationalDetail.tsx +++ b/libs/portals/my-pages/occupational-licenses/src/screens/v1/EducationalDetail/EducationalDetail.tsx @@ -9,7 +9,7 @@ import { formSubmit, } from '@island.is/portals/my-pages/core' import { useLocale, useNamespaces } from '@island.is/localization' -import { useUserInfo } from '@island.is/auth/react' +import { useUserBirthday, useUserInfo } from '@island.is/react-spa/bff' import { LicenseDetail } from '../../../components/LicenseDetail' import { useEffect, useState } from 'react' import { m } from '@island.is/portals/my-pages/core' @@ -26,7 +26,7 @@ export const EducationDetail = () => { useNamespaces('sp.occupational-licenses') const user = useUserInfo() - const birthday = user.profile.dateOfBirth + const dateOfBirth = useUserBirthday() const { formatDateFns, formatMessage } = useLocale() @@ -100,7 +100,9 @@ export const EducationDetail = () => { } isOldEducationLicense={isOldEducationLicense} name={user.profile.name} - dateOfBirth={birthday ? formatDateFns(birthday, 'dd.MM.yyyy') : undefined} + dateOfBirth={ + dateOfBirth ? formatDateFns(dateOfBirth, 'dd.MM.yyyy') : undefined + } profession={programme} licenseType={programme} publisher={license.type} diff --git a/libs/portals/my-pages/occupational-licenses/src/screens/v1/HealthDirectorateDetail/HealthDirectorateDetail.tsx b/libs/portals/my-pages/occupational-licenses/src/screens/v1/HealthDirectorateDetail/HealthDirectorateDetail.tsx index 2e3dadbc0309..21c01c390087 100644 --- a/libs/portals/my-pages/occupational-licenses/src/screens/v1/HealthDirectorateDetail/HealthDirectorateDetail.tsx +++ b/libs/portals/my-pages/occupational-licenses/src/screens/v1/HealthDirectorateDetail/HealthDirectorateDetail.tsx @@ -8,11 +8,10 @@ import { HEALTH_DIRECTORATE_SLUG, } from '@island.is/portals/my-pages/core' import { useLocale, useNamespaces } from '@island.is/localization' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { LicenseDetail } from '../../../components/LicenseDetail' import { olMessage as om } from '../../../lib/messages' import { m } from '@island.is/portals/my-pages/core' -import { OccupationalLicenseV2LicenseType } from '@island.is/portals/my-pages/graphql' type UseParams = { id: string diff --git a/libs/portals/my-pages/restrictions/src/screens/restrictions/Restrictions.tsx b/libs/portals/my-pages/restrictions/src/screens/restrictions/Restrictions.tsx index 9bdee98efe8b..5cc60e860e7b 100644 --- a/libs/portals/my-pages/restrictions/src/screens/restrictions/Restrictions.tsx +++ b/libs/portals/my-pages/restrictions/src/screens/restrictions/Restrictions.tsx @@ -1,10 +1,9 @@ -import React, { ReactNode, useEffect, useState } from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { Form, useActionData, useLoaderData } from 'react-router-dom' import addDays from 'date-fns/addDays' import startOfDay from 'date-fns/startOfDay' +import { ReactNode, useEffect, useState } from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { Form, useActionData, useLoaderData } from 'react-router-dom' -import { useUserDecodedIdToken } from '@island.is/auth/react' import { AlertMessage, Box, @@ -17,11 +16,12 @@ import { APPLICATION_SERVICE_PROVIDER_SLUG, IntroHeader, } from '@island.is/portals/my-pages/core' -import { Modal } from '@island.is/react/components' +import { useUserInfo } from '@island.is/react-spa/bff' import { useSubmitting } from '@island.is/react-spa/shared' +import { Modal } from '@island.is/react/components' -import { RestrictionsIntent, RestrictionsResponse } from './Restrictions.action' import { m } from '../../lib/messages' +import { RestrictionsIntent, RestrictionsResponse } from './Restrictions.action' import { RestrictionsLoaderResponse } from './Restrictions.loader' import * as styles from './Restrictions.css' @@ -49,7 +49,9 @@ export default function Restrictions() { const [showModal, setShowModal] = useState(false) const { formatMessage } = useLocale() - const { idp } = useUserDecodedIdToken() + const { + profile: { idp }, + } = useUserInfo() const loaderData = useLoaderData() as RestrictionsLoaderResponse const actionData = useActionData() as RestrictionsResponse diff --git a/libs/portals/my-pages/sessions/src/components/LogTable/LogTable.tsx b/libs/portals/my-pages/sessions/src/components/LogTable/LogTable.tsx index dda09614bdf4..14b71cb63a54 100644 --- a/libs/portals/my-pages/sessions/src/components/LogTable/LogTable.tsx +++ b/libs/portals/my-pages/sessions/src/components/LogTable/LogTable.tsx @@ -3,24 +3,24 @@ import React from 'react' import { useIntl } from 'react-intl' import { SessionsSession } from '@island.is/api/schema' -import { useAuth } from '@island.is/auth/react' import { Box, Icon, Table, Text, Tooltip } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' +import { Country, getCountryByCode } from '@island.is/shared/utils' import { m } from '../../lib/messages' import { SessionType } from '../../lib/types/sessionTypes' import { getSessionType } from '../../utils/utils' +import { Client } from '../Client/Client' import * as styles from '../LogTable/LogTable.css' import Person from '../PersonIcon/PersonIcon' -import { Client } from '../Client/Client' -import { Country, getCountryByCode } from '@island.is/shared/utils' interface LogTableProps { data: SessionsSession[] } const LogTable = ({ data }: LogTableProps) => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() const { formatMessage } = useLocale() const { formatDate, formatTime } = useIntl() diff --git a/libs/portals/my-pages/sessions/src/components/LogTable/LogTableMobile.tsx b/libs/portals/my-pages/sessions/src/components/LogTable/LogTableMobile.tsx index 1d1199b99e19..827b3bacee1d 100644 --- a/libs/portals/my-pages/sessions/src/components/LogTable/LogTableMobile.tsx +++ b/libs/portals/my-pages/sessions/src/components/LogTable/LogTableMobile.tsx @@ -2,25 +2,25 @@ import * as kennitala from 'kennitala' import { useIntl } from 'react-intl' import { SessionsSession } from '@island.is/api/schema' -import { useAuth } from '@island.is/auth/react' import { Box, Icon, Table as T, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { ExpandHeader, ExpandRow } from '@island.is/portals/my-pages/core' import { Country, getCountryByCode } from '@island.is/shared/utils' +import { useUserInfo } from '@island.is/react-spa/bff' +import { m } from '../../lib/messages' import { SessionType } from '../../lib/types/sessionTypes' import { getSessionType } from '../../utils/utils' -import Person from '../PersonIcon/PersonIcon' -import * as styles from '../LogTable/LogTable.css' import { Client } from '../Client/Client' -import { m } from '../../lib/messages' +import * as styles from '../LogTable/LogTable.css' +import Person from '../PersonIcon/PersonIcon' interface LogTableProps { sessions: SessionsSession[] } const LogTableMobile = ({ sessions }: LogTableProps) => { - const { userInfo } = useAuth() + const userInfo = useUserInfo() const { formatMessage } = useLocale() const { formatDate, formatTime } = useIntl() diff --git a/libs/portals/my-pages/sessions/src/screens/Sessions/Sessions.tsx b/libs/portals/my-pages/sessions/src/screens/Sessions/Sessions.tsx index 243599f51862..190747e5f618 100644 --- a/libs/portals/my-pages/sessions/src/screens/Sessions/Sessions.tsx +++ b/libs/portals/my-pages/sessions/src/screens/Sessions/Sessions.tsx @@ -1,4 +1,4 @@ -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { Problem } from '@island.is/react-spa/shared' import * as kennitala from 'kennitala' import React, { useState } from 'react' diff --git a/libs/portals/my-pages/signature-collection/src/screens/Parliamentary/index.tsx b/libs/portals/my-pages/signature-collection/src/screens/Parliamentary/index.tsx index 2a565c751471..7428fcc8bb3c 100644 --- a/libs/portals/my-pages/signature-collection/src/screens/Parliamentary/index.tsx +++ b/libs/portals/my-pages/signature-collection/src/screens/Parliamentary/index.tsx @@ -9,7 +9,7 @@ import { m } from '../../lib/messages' import OwnerView from './OwnerView' import SigneeView from '../shared/SigneeView' import { useGetCurrentCollection, useIsOwner } from '../../hooks' -import { useUserInfo } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { AuthDelegationType } from '../../types/schema' const SignatureListsParliamentary = () => { diff --git a/libs/portals/my-pages/signature-collection/src/screens/Presidential/OwnerView/index.tsx b/libs/portals/my-pages/signature-collection/src/screens/Presidential/OwnerView/index.tsx index 397e3776bbe4..8e82cd70e9d2 100644 --- a/libs/portals/my-pages/signature-collection/src/screens/Presidential/OwnerView/index.tsx +++ b/libs/portals/my-pages/signature-collection/src/screens/Presidential/OwnerView/index.tsx @@ -1,3 +1,4 @@ +import { SignatureCollection } from '@island.is/api/schema' import { ActionCard, Box, @@ -7,15 +8,14 @@ import { toast, } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' +import copyToClipboard from 'copy-to-clipboard' +import format from 'date-fns/format' +import { useNavigate } from 'react-router-dom' +import { useGetListsForOwner } from '../../../hooks' import { m } from '../../../lib/messages' import { SignatureCollectionPaths } from '../../../lib/paths' -import { useGetListsForOwner } from '../../../hooks' -import format from 'date-fns/format' import { Skeleton } from '../../../skeletons' -import { useNavigate } from 'react-router-dom' -import { useAuth } from '@island.is/auth/react' -import copyToClipboard from 'copy-to-clipboard' -import { SignatureCollection } from '@island.is/api/schema' import SignedList from '../../shared/SignedList' import CancelCollection from './CancelCollection' @@ -26,7 +26,7 @@ const OwnerView = ({ }) => { useNamespaces('sp.signatureCollection') const navigate = useNavigate() - const { userInfo: user } = useAuth() + const user = useUserInfo() const { formatMessage } = useLocale() const { listsForOwner, loadingOwnerLists } = useGetListsForOwner( currentCollection?.id || '', diff --git a/libs/portals/my-pages/signature-collection/src/screens/shared/SigneeView/index.tsx b/libs/portals/my-pages/signature-collection/src/screens/shared/SigneeView/index.tsx index 536fe190d765..81ff24f09491 100644 --- a/libs/portals/my-pages/signature-collection/src/screens/shared/SigneeView/index.tsx +++ b/libs/portals/my-pages/signature-collection/src/screens/shared/SigneeView/index.tsx @@ -9,7 +9,7 @@ import { useLocale } from '@island.is/localization' import { EmptyState } from '@island.is/portals/my-pages/core' import { useGetListsForUser, useGetSignedList } from '../../../hooks' import { Skeleton } from '../../../skeletons' -import { useAuth } from '@island.is/auth/react' +import { useUserInfo } from '@island.is/react-spa/bff' import { sortAlpha } from '@island.is/shared/utils' import { m } from '../../../lib/messages' import SignedList from '../SignedList' @@ -20,7 +20,7 @@ const SigneeView = ({ }: { currentCollection: SignatureCollection }) => { - const { userInfo: user } = useAuth() + const user = useUserInfo() const { formatMessage } = useLocale() const { signedLists, loadingSignedLists } = useGetSignedList() const { listsForUser, loadingUserLists, getListsForUserError } = diff --git a/libs/react-spa/bff/src/index.ts b/libs/react-spa/bff/src/index.ts index d535a9f34f45..db4a4d586734 100644 --- a/libs/react-spa/bff/src/index.ts +++ b/libs/react-spa/bff/src/index.ts @@ -1,4 +1,5 @@ +export * from './lib/BffContext' export * from './lib/BffProvider' export * from './lib/bff.hooks' -export * from './lib/bff.utils' export * from './lib/bff.mocks' +export * from './lib/bff.utils' diff --git a/libs/react-spa/bff/src/lib/BffProvider.tsx b/libs/react-spa/bff/src/lib/BffProvider.tsx index df65376bc1c5..7ca9502c62ff 100644 --- a/libs/react-spa/bff/src/lib/BffProvider.tsx +++ b/libs/react-spa/bff/src/lib/BffProvider.tsx @@ -14,24 +14,26 @@ const BFF_SERVER_UNAVAILABLE = 'BFF_SERVER_UNAVAILABLE' type BffProviderProps = { children: ReactNode + mockedInitialState?: LoggedInState + applicationBasePath: string /** - * The base path of the application. + * Bff API - Global prefix path + * @example /stjornbord/bff, etc. */ - applicationBasePath: string - mockedInitialState?: LoggedInState + bffGlobalPrefix?: string } export const BffProvider = ({ children, applicationBasePath, mockedInitialState, + bffGlobalPrefix, }: BffProviderProps) => { const [showSessionExpiredScreen, setSessionExpiredScreen] = useState(false) - const bffUrlGenerator = createBffUrlGenerator(applicationBasePath) - const [state, dispatch] = useReducer( - reducer, - mockedInitialState ?? initialState, - ) + const bffUrlGenerator = createBffUrlGenerator(bffGlobalPrefix) + const [state, dispatch] = useReducer(reducer, { + ...(mockedInitialState ?? initialState), + }) const { authState } = state const showErrorScreen = authState === 'error' @@ -147,16 +149,12 @@ export const BffProvider = ({ type: ActionType.SWITCH_USER, }) - window.location.href = bffUrlGenerator( - '/login', - nationalId - ? { - login_hint: nationalId, - } - : { - prompt: 'select_account', - }, - ) + window.location.href = bffUrlGenerator('/login', { + target_link_uri: window.location.href, + ...(nationalId + ? { login_hint: nationalId } + : { prompt: 'select_account' }), + }) }, [bffUrlGenerator], ) diff --git a/libs/react-spa/bff/src/lib/bff.hooks.ts b/libs/react-spa/bff/src/lib/bff.hooks.ts index 5818be055b17..b768ad209ba7 100644 --- a/libs/react-spa/bff/src/lib/bff.hooks.ts +++ b/libs/react-spa/bff/src/lib/bff.hooks.ts @@ -1,119 +1,60 @@ -import { AuthContext } from '@island.is/auth/react' import { createBroadcasterHook } from '@island.is/react-spa/shared' -import { BffUser, User } from '@island.is/shared/types' +import { BffUser } from '@island.is/shared/types' +import { isDefined } from '@island.is/shared/utils' +import * as kennitala from 'kennitala' import { useContext, useMemo } from 'react' import { BffContext, BffContextType } from './BffContext' /** - * Maps an object to a BffUser type. - */ -export const mapToBffUser = (input: User): BffUser => { - const { - profile: { - sid, - birthdate, - nationalId, - name, - idp, - actor, - subjectType, - delegationType, - locale, - }, - scopes, - } = input - - // Return a mapped BffUser object - return { - scopes: scopes || [], - profile: { - sid: sid || '', - birthdate, - nationalId, - name, - idp, - actor, - subjectType, - delegationType, - locale, - }, - } -} - -/** - * Dynamic hook to get the bff context. + * This hook is used to get the BFF authentication context. */ -export const useDynamicBffHook = (hookName: string): BffContextType => { +export const useBff = () => { const bffContext = useContext(BffContext) if (!bffContext) { - throw new Error(`${hookName} must be used within a BffProvider`) + throw new Error('useBff must be used within a BffProvider') } return bffContext } /** - * This hook is used to get the BFF authentication context. - * It has backward compatibility with AuthContext. + * Dynamic hook to get specific key in the bff context. */ -export const useAuth = () => { - const bffContext = useContext(BffContext) - const authContext = useContext(AuthContext) - - if (bffContext) { - return bffContext - } - - if (authContext) { - return authContext - } - - const errorMsg = (providerStr: string) => - `useAuth must be used within a ${providerStr}` +export const useDynamicBffHook = ( + returnField: Key, +): NonNullable => { + const bffContext = useBff() - if (!authContext) { - throw new Error(errorMsg('AuthProvider')) + if (!isDefined(bffContext[returnField])) { + throw new Error(`The field ${returnField} does not exist in the BffContext`) } - throw new Error(errorMsg('BffProvider')) + return bffContext[returnField] as NonNullable } /** - * This hook is used to get user information. - * It will determine what context to use based on the context that is available. - * We will remove support for AuthContext when other clients transition over to BFF. - * If AuthContext is being used then we will map the user info to the BffUser type. + * This hook is used to get the bff url generator. + * The bff url generator is used to generate urls for the Bff in a conveinent way. */ -export const useUserInfo = (): BffUser => { - const bffContext = useContext(BffContext) - const authContext = useContext(AuthContext) - - const mappedAuthUserInfo = useMemo(() => { - if (authContext.userInfo) { - return mapToBffUser(authContext.userInfo) - } - - return null - }, [authContext.userInfo]) - - if (bffContext?.userInfo) { - return bffContext.userInfo - } else if (mappedAuthUserInfo) { - return mappedAuthUserInfo - } - - throw new Error('User info is not available. Is the user authenticated?') -} +export const useBffUrlGenerator = () => useDynamicBffHook('bffUrlGenerator') +export const useUserInfo = () => useDynamicBffHook('userInfo') /** - * This hook is used to get the bff url generator. - * The bff url generator is used to generate urls for the Bff in a conveinent way. + * Gets the user's birthday from the nationalId in the user profile. + * + * @warning This does not support system nationalId (kerfis kennitala) users. + * This should be read from the parsed id_token in the /user response, when the IDP adds support for it. */ -export const useBffUrlGenerator = () => - useDynamicBffHook(useBffUrlGenerator.name).bffUrlGenerator +export const useUserBirthday = () => { + const userInfo = useUserInfo() + + return useMemo(() => { + const nationalId = userInfo?.profile.nationalId -export const useBff = () => useDynamicBffHook(useBff.name) + return nationalId ? kennitala.info(nationalId)?.birthday : undefined + }, [userInfo?.profile.nationalId]) +} export enum BffBroadcastEvents { NEW_SESSION = 'NEW_SESSION', diff --git a/libs/react-spa/bff/src/lib/bff.mocks.ts b/libs/react-spa/bff/src/lib/bff.mocks.ts index 2af096c1b055..f63d2993b46c 100644 --- a/libs/react-spa/bff/src/lib/bff.mocks.ts +++ b/libs/react-spa/bff/src/lib/bff.mocks.ts @@ -3,6 +3,7 @@ import { LoggedInState } from './bff.state' export const createMockedInitialState = ( user?: Partial, + issuer = 'https://identity-server.dev01.devland.is', ): LoggedInState => ({ userInfo: { profile: { @@ -10,6 +11,7 @@ export const createMockedInitialState = ( locale: 'is', nationalId: '0000000000', ...user?.profile, + iss: issuer, } as BffUser['profile'], scopes: user?.scopes ?? [], }, diff --git a/libs/react-spa/bff/src/lib/bff.state.ts b/libs/react-spa/bff/src/lib/bff.state.ts index d23dfd4b4f30..dbcf3bad3423 100644 --- a/libs/react-spa/bff/src/lib/bff.state.ts +++ b/libs/react-spa/bff/src/lib/bff.state.ts @@ -63,10 +63,8 @@ export type Action = * Helper function to reset user-related state when switching users or logging out */ const resetState = (authState: NonLoggedInAuthState): NonLoggedInState => ({ - userInfo: null, + ...initialState, authState, - isAuthenticated: false, - error: null, }) /** diff --git a/libs/react-spa/bff/src/lib/bff.utils.ts b/libs/react-spa/bff/src/lib/bff.utils.ts index a7af87244515..221b36a895f5 100644 --- a/libs/react-spa/bff/src/lib/bff.utils.ts +++ b/libs/react-spa/bff/src/lib/bff.utils.ts @@ -3,14 +3,20 @@ import { BffUser } from '@island.is/shared/types' /** * Creates a function that can generate a BFF URLs. * + * @param bffGlobalPrefix - The global prefix for the BFF URLs + * * @usage - * const bffBaseUrl = createBffUrlGenerator('/myapplication') + * const bffBaseUrl = createBffUrlGenerator('/myapplication/bff') * const userUrl = bffBaseUrl('/user') // http://localhost:3010/myapplication/bff/user * const userUrlWithParams = bffBaseUrl('/user', { id: '123' }) // http://localhost:3010/myapplication/bff/user?id=123 + * + * With default bffGlobalPrefix prefix: + * const bffBaseUrl = createBffUrlGenerator() + * const userUrl = bffBaseUrl('/user') // http://localhost:3010/bff/user + * const userUrlWithParams = bffBaseUrl('/user', { id: '123' }) // http://localhost:3010/bff/user?id=123 */ -export const createBffUrlGenerator = (basePath: string) => { - const sanitizedBasePath = sanitizePath(basePath) - const baseUrl = `${window.location.origin}/${sanitizedBasePath}/bff` +export const createBffUrlGenerator = (bffGlobalPrefix = 'bff') => { + const baseUrl = `${window.location.origin}/${sanitizePath(bffGlobalPrefix)}` return (relativePath = '', params?: Record) => { const url = `${baseUrl}${relativePath}` diff --git a/libs/shared/components/src/auth/UserMenu/UserDelegations.tsx b/libs/shared/components/src/auth/UserMenu/UserDelegations.tsx index b4db29d4d10f..707a82fa022f 100644 --- a/libs/shared/components/src/auth/UserMenu/UserDelegations.tsx +++ b/libs/shared/components/src/auth/UserMenu/UserDelegations.tsx @@ -1,6 +1,6 @@ import { Box, Stack } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { useAuth, useUserInfo } from '@island.is/react-spa/bff' +import { useBff, useUserInfo } from '@island.is/react-spa/bff' import { userMessages } from '@island.is/shared/translations' import { UserDropdownItem } from './UserDropdownItem' import { UserTopicCard } from './UserTopicCard' @@ -16,7 +16,7 @@ export const UserDelegations = ({ }: UserDelegationsProps) => { const user = useUserInfo() const { formatMessage } = useLocale() - const { switchUser } = useAuth() + const { switchUser } = useBff() const actor = user.profile.actor return ( diff --git a/libs/shared/components/src/auth/UserMenu/UserMenu.spec.tsx b/libs/shared/components/src/auth/UserMenu/UserMenu.spec.tsx index b632a98831b1..172df3c59438 100644 --- a/libs/shared/components/src/auth/UserMenu/UserMenu.spec.tsx +++ b/libs/shared/components/src/auth/UserMenu/UserMenu.spec.tsx @@ -1,27 +1,34 @@ -import React, { FC, ReactNode } from 'react' -import { BrowserRouter } from 'react-router-dom' +import { MockedProvider } from '@apollo/client/testing' +import { LocaleContext, LocaleProvider } from '@island.is/localization' +import { + BffContext, + BffContextType, + createMockedInitialState, +} from '@island.is/react-spa/bff' +import { BffUser } from '@island.is/shared/types' +import '@testing-library/jest-dom' import { - render, - screen, - fireEvent, act, - getByText, + fireEvent, getByRole, + getByText, + render, + screen, } from '@testing-library/react' -import '@testing-library/jest-dom' -import { MockedProvider } from '@apollo/client/testing' -import { LocaleProvider, LocaleContext } from '@island.is/localization' -import { MockedAuthProvider, MockUser } from '@island.is/auth/react' +import React, { FC, ReactNode } from 'react' +import { BrowserRouter } from 'react-router-dom' +import { ActorDelegationsQuery, GetUserProfileQuery } from '../../../gen/schema' import { UserMenu } from './UserMenu' import { ACTOR_DELEGATIONS } from './actorDelegations.graphql' -import { ActorDelegationsQuery } from '../../../gen/schema' import { USER_PROFILE } from './userProfile.graphql' -import { GetUserProfileQuery } from '../../../gen/schema' const delegation = { name: 'Phil', nationalId: '1111111111', } + +const mockedUser = createMockedInitialState().userInfo + const mocks = [ { request: { @@ -74,12 +81,23 @@ describe('UserMenu', () => { const renderAuthenticated = ( ui: ReactNode, - { user }: { user?: MockUser } = {}, + user: { + profile?: Partial + scopes?: Partial + } | null = null, ) => render( - + {ui} - , + , { wrapper, }, @@ -101,7 +119,9 @@ describe('UserMenu', () => { it('shows user menu when authenticated', async () => { // Act renderAuthenticated(, { - user: { profile: { name: 'John' } }, + profile: { + name: 'John', + }, }) // Assert @@ -112,11 +132,9 @@ describe('UserMenu', () => { it('shows delegation name when authenticated with delegations', async () => { // Act renderAuthenticated(, { - user: { - profile: { - name: 'John', - actor: { name: 'Anna', nationalId: '2222222222' }, - }, + profile: { + name: 'John', + actor: { name: 'Anna', nationalId: '2222222222' }, }, }) @@ -128,7 +146,7 @@ describe('UserMenu', () => { it('can open and close user menu', async () => { // Arrange - renderAuthenticated(, { user: { profile: { name: 'John' } } }) + renderAuthenticated(, { profile: { name: 'John' } }) // Act const dialog = await openMenu() @@ -144,7 +162,7 @@ describe('UserMenu', () => { }) it('can log out user', async () => { // Arrange - renderAuthenticated(, { user: {} }) + renderAuthenticated(, mockedUser) await openMenu() // Act @@ -164,7 +182,7 @@ describe('UserMenu', () => { {({ lang }) => Current: {lang}} , - { user: {} }, + mockedUser, ) const dialog = await openMenu() const languageSelector = dialog.querySelector('#language-switcher')! @@ -191,7 +209,7 @@ describe('UserMenu', () => { {({ lang }) => Current: {lang}} , - { user: {} }, + mockedUser, ) const languageSelector = screen.getByTestId('language-switcher-button') expect(languageSelector).not.toBeNull() @@ -205,9 +223,7 @@ describe('UserMenu', () => { it('can switch between delegations', async () => { // Arrange - renderAuthenticated(, { - user: {}, - }) + renderAuthenticated(, mockedUser) const dialog = await openMenu() const delegationButton = getByRole(dialog, 'button', { name: 'Skipta um notanda', @@ -224,7 +240,7 @@ describe('UserMenu', () => { it('hides language switcher', async () => { // Arrange - renderAuthenticated(, { user: {} }) + renderAuthenticated(, mockedUser) // Assert const languageSelector = await screen.queryByTestId( @@ -236,7 +252,7 @@ describe('UserMenu', () => { it('user button shows icon only in mobile and not name', async () => { // Act renderAuthenticated(, { - user: { profile: { name: 'John' } }, + profile: { name: 'John' }, }) // Assert diff --git a/libs/shared/components/src/auth/UserMenu/UserMenu.tsx b/libs/shared/components/src/auth/UserMenu/UserMenu.tsx index 875b16169a25..a2ffae0c76fb 100644 --- a/libs/shared/components/src/auth/UserMenu/UserMenu.tsx +++ b/libs/shared/components/src/auth/UserMenu/UserMenu.tsx @@ -1,5 +1,5 @@ import { Box, Hidden } from '@island.is/island-ui/core' -import { useAuth } from '@island.is/react-spa/bff' +import { useBff } from '@island.is/react-spa/bff' import { useEffect, useState } from 'react' import { UserButton } from './UserButton' import { UserDropdown } from './UserDropdown' @@ -29,7 +29,7 @@ export const UserMenu = ({ const [dropdownState, setDropdownState] = useState<'closed' | 'open'>( 'closed', ) - const { signOut, switchUser, userInfo: user } = useAuth() + const { signOut, switchUser, userInfo: user } = useBff() const handleClick = () => { setDropdownState(dropdownState === 'open' ? 'closed' : 'open') diff --git a/libs/shared/components/src/auth/UserMenu/UserProfileLocale.tsx b/libs/shared/components/src/auth/UserMenu/UserProfileLocale.tsx index 6783d67bcf1d..04b32552c344 100644 --- a/libs/shared/components/src/auth/UserMenu/UserProfileLocale.tsx +++ b/libs/shared/components/src/auth/UserMenu/UserProfileLocale.tsx @@ -1,8 +1,8 @@ -import { useLocale, useNamespaces, isLocale } from '@island.is/localization' -import { useLocation } from 'react-router-dom' +import { isLocale, useLocale, useNamespaces } from '@island.is/localization' +import { useUserInfo } from '@island.is/react-spa/bff' import { useEffect } from 'react' +import { useLocation } from 'react-router-dom' import { useGetUserProfileLocaleLazyQuery } from '../../../gen/schema' -import { useUserInfo } from '@island.is/react-spa/bff' /** * If the user has set a preferred language in his user diff --git a/libs/shared/mocking/src/msw/startMocking.ts b/libs/shared/mocking/src/msw/startMocking.ts index 9c9b225997bb..daeb84c3ee42 100644 --- a/libs/shared/mocking/src/msw/startMocking.ts +++ b/libs/shared/mocking/src/msw/startMocking.ts @@ -3,7 +3,7 @@ import { RequestHandler } from 'msw' // eslint-disable-next-line @typescript-eslint/no-explicit-any export declare type RequestHandlersList = RequestHandler[] -const allowedKeyPaths = ['stjornbord', 'minarsidur'] +const allowedKeyPaths = ['stjornbord', 'minarsidur', 'umsoknir'] const extractUniqueKeyPath = (url: string) => { try { @@ -28,6 +28,25 @@ export const startMocking = (requestHandlers: RequestHandlersList) => { server.listen() return server } else { + const { pathname, search, hash } = location + + // Handle trailing slash redirect only for root paths + // This prevents infinite reloads when accessing root paths without trailing slash + // e.g., /minarsidur will redirect to /minarsidur/ + // but /minarsidur/umsoknir will remain unchanged + // This is necessary because the application expects root paths to have trailing slashes + // for proper routing and service worker functionality. + const isRootPath = allowedKeyPaths.some( + // Exactly matches /minarsidur, /stjornbord, /umsoknir, etc. + (path) => pathname === `/${path}`, + ) + + if (isRootPath) { + location.replace(`${pathname}/${search}${hash}`) + + return + } + // eslint-disable-next-line @typescript-eslint/no-var-requires const { setupWorker } = require('msw') const worker = setupWorker(...requestHandlers) diff --git a/libs/shared/types/src/lib/bff.ts b/libs/shared/types/src/lib/bff.ts index 436aa0ae8907..6c8a64102751 100644 --- a/libs/shared/types/src/lib/bff.ts +++ b/libs/shared/types/src/lib/bff.ts @@ -16,6 +16,9 @@ export interface IdTokenClaims { subjectType: 'person' | 'legalEntity' delegationType?: AuthDelegationType[] locale?: string + iss: string + email?: string + phone_number?: string } export type BffUser = { diff --git a/libs/shared/utils/src/lib/isDelegation.ts b/libs/shared/utils/src/lib/isDelegation.ts index fb36417a6f5d..88a163294c08 100644 --- a/libs/shared/utils/src/lib/isDelegation.ts +++ b/libs/shared/utils/src/lib/isDelegation.ts @@ -1,5 +1,5 @@ -import { BffUser, User } from '@island.is/shared/types' +import { BffUser } from '@island.is/shared/types' -export const checkDelegation = (user: User | BffUser) => { - return Boolean(user?.profile.actor) +export const checkDelegation = (user: BffUser) => { + return Boolean(user.profile?.actor) } diff --git a/package.json b/package.json index 5bb4bf9b3f5a..e78c508c8747 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ "infra": "cd infra/ && yarn infra", "run-proxies": "./scripts/run-proxies.sh", "proxies": "yarn run-proxies", - "hmr-benchmark": "node -r esbuild-register scripts/hmr-benchmark.ts" + "hmr-benchmark": "node -r esbuild-register scripts/hmr-benchmark.ts", + "mock": "nx mock" }, "dependencies": { "@apollo/client": "3.7.15",