From 4c3a16774db0d03ada6e83b7882c8e69bf885d2a Mon Sep 17 00:00:00 2001
From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Date: Tue, 26 Mar 2024 10:11:56 +0100
Subject: [PATCH] add handlers to get configuration of a mfa provider
---
.../mocks/handlers/user/current.handlers.ts | 21 ++++
.../mfa-login/mfa-provider-default.element.ts | 107 ++++++++++++++++--
2 files changed, 116 insertions(+), 12 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/current.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/current.handlers.ts
index 4ab1ee22b7..b59917d21e 100644
--- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/current.handlers.ts
+++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/current.handlers.ts
@@ -12,6 +12,27 @@ export const handlers = [
const mfaLoginProviders = umbUserMockDb.getMfaLoginProviders();
return res(ctx.status(200), ctx.json(mfaLoginProviders));
}),
+ rest.get(umbracoPath(`${UMB_SLUG}/current/2fa/:providerName`), (req, res, ctx) => {
+ if (!req.params.providerName) {
+ return res(ctx.status(400));
+ }
+
+ const mfaProviders = umbUserMockDb.getMfaLoginProviders();
+ const mfaProvider = mfaProviders.find((p) => p.providerName === req.params.providerName.toString());
+
+ if (!mfaProvider) {
+ return res(ctx.status(404));
+ }
+
+ return res(
+ ctx.status(200),
+ ctx.json({
+ $type: 'TwoFactorAuthInfo',
+ qrCodeSetupImageUrl: 'https://placekitten.com/200/200',
+ secret: '8b713fc7-8f17-4f5d-b2ac-b53879c75953',
+ }),
+ );
+ }),
rest.post<{ code: string; secret: string }>(
umbracoPath(`${UMB_SLUG}/current/2fa/:providerName`),
async (req, res, ctx) => {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-provider-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-provider-default.element.ts
index 7e3160c8a6..8b3e6665ec 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-provider-default.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/mfa-provider-default.element.ts
@@ -1,6 +1,10 @@
import type { UmbMfaProviderConfigurationElementProps } from '../types.js';
-import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
+import { UserResource } from '@umbraco-cms/backoffice/external/backend-api';
+import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
+import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
+import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification';
+import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
@customElement('umb-mfa-provider-default')
export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMfaProviderConfigurationElementProps {
@@ -16,24 +20,103 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf
@property({ attribute: false })
onClose = () => {};
+ @state()
+ _loading = true;
+
+ @state()
+ _secret = '';
+
+ @state()
+ _qrCodeSetupImageUrl = '';
+
+ #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE;
+
+ constructor() {
+ super();
+
+ this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => {
+ this.#notificationContext = context;
+ });
+ }
+
+ async firstUpdated() {
+ await this.#load();
+ this._loading = false;
+ }
+
+ async #load() {
+ if (!this.providerName) {
+ this.#peek('Provider name is required');
+ throw new Error('Provider name is required');
+ }
+ const { data } = await tryExecuteAndNotify(
+ this,
+ UserResource.getUserCurrent2FaByProviderName({ providerName: this.providerName }),
+ );
+
+ if (!data) {
+ this.#peek('No data returned');
+ throw new Error('No data returned');
+ }
+
+ // Verify that there is a secret
+ if (!data.secret) {
+ this.#peek('The provider did not return a secret.');
+ throw new Error('No secret returned');
+ }
+
+ this._secret = data.secret;
+ this._qrCodeSetupImageUrl = data.qrCodeSetupImageUrl;
+ }
+
render() {
+ if (this._loading) {
+ return html`