From 21c9cbbbf3ff8160da2feb965d744ed7242e5f48 Mon Sep 17 00:00:00 2001
From: mcstoer <49734282+mcstoer@users.noreply.github.com>
Date: Thu, 21 Nov 2024 17:39:05 -0800
Subject: [PATCH 01/12] Check if provisioning is possible and change the text
on the login screen to indicate it.
---
src/components/LoginConsent/Login/Login.js | 29 ++++++++++++++++++-
.../LoginConsent/Login/Login.render.js | 3 +-
2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/src/components/LoginConsent/Login/Login.js b/src/components/LoginConsent/Login/Login.js
index fcef6bc..ad12232 100644
--- a/src/components/LoginConsent/Login/Login.js
+++ b/src/components/LoginConsent/Login/Login.js
@@ -8,7 +8,11 @@ import { CONSENT_TO_SCOPE, EXTERNAL_ACTION, EXTERNAL_CHAIN_START, REDIRECT } fro
import { checkAndUpdateIdentities, setActiveVerusId } from '../../../redux/reducers/identity/identity.actions';
import { signResponse } from '../../../rpc/calls/signResponse';
import { setError } from '../../../redux/reducers/error/error.actions';
-import { LoginConsentDecision, LoginConsentResponse } from 'verus-typescript-primitives';
+import {
+ ID_ADDRESS_VDXF_KEY,
+ LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY,
+ LoginConsentDecision, LoginConsentResponse
+} from 'verus-typescript-primitives';
import BigNumber from 'bignumber.js';
class Login extends React.Component {
@@ -19,6 +23,29 @@ class Login extends React.Component {
loading: false
}
+ // Check to see if provisioning is an option.
+ const { request } = this.props.loginConsentRequest;
+
+ // See if the webhook exists.
+ let canProvision = request.challenge.provisioning_info && request.challenge.provisioning_info.some(x => {
+ return (
+ x.vdxfkey === LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY.vdxfid
+ );
+ });
+
+ // Provisioning is not an option if the subject is specified to be one of the identities that the user owns.
+ if (this.props.identities.length > 0) {
+ const identitySubjects =
+ request.challenge.subject.filter(item => item.vdxfkey === ID_ADDRESS_VDXF_KEY.vdxfid).map(id => id.data);
+
+ const identitySubjectMatches = this.props.identities.filter(id => identitySubjects.includes(id.identity.identityaddress));
+
+ if (identitySubjectMatches.length > 0) {
+ canProvision = false;
+ }
+ }
+
+ this.canProvision = canProvision;
this.tryLogin = this.tryLogin.bind(this);
this.selectId = this.selectId.bind(this);
this.cancel = this.cancel.bind(this);
diff --git a/src/components/LoginConsent/Login/Login.render.js b/src/components/LoginConsent/Login/Login.render.js
index 3c01752..b4c8ee8 100644
--- a/src/components/LoginConsent/Login/Login.render.js
+++ b/src/components/LoginConsent/Login/Login.render.js
@@ -44,7 +44,8 @@ export const LoginRender = function () {
padding: 8,
}}
>
- {`Select an Identity`}
+ {`Select an Identity` +
+ (this.canProvision ? " or Request an Identity" : "")}
Date: Fri, 22 Nov 2024 15:43:53 -0800
Subject: [PATCH 02/12] Add screens for each step of provisioning with
navigation between them.
---
src/components/LoginConsent/Login/Login.js | 13 ++-
.../LoginConsent/Login/Login.render.js | 24 ++++
.../LoginConsent/LoginConsent.render.js | 26 ++++-
.../ProvisionIdentityConfirm.js | 43 +++++++
.../ProvisionIdentityConfirm.render.js | 107 ++++++++++++++++++
.../ProvisionIdentityForm.js | 43 +++++++
.../ProvisionIdentityForm.render.js | 107 ++++++++++++++++++
.../ProvisionIdentityResult.js | 43 +++++++
.../ProvisionIdentityResult.render.js | 107 ++++++++++++++++++
src/utils/constants.js | 3 +
10 files changed, 514 insertions(+), 2 deletions(-)
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.render.js
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.render.js
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityResult/ProvisionIdentityResult.js
create mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityResult/ProvisionIdentityResult.render.js
diff --git a/src/components/LoginConsent/Login/Login.js b/src/components/LoginConsent/Login/Login.js
index ad12232..76baa1a 100644
--- a/src/components/LoginConsent/Login/Login.js
+++ b/src/components/LoginConsent/Login/Login.js
@@ -4,7 +4,13 @@ import { setExternalAction, setNavigationPath } from '../../../redux/reducers/na
import {
LoginRender
} from './Login.render';
-import { CONSENT_TO_SCOPE, EXTERNAL_ACTION, EXTERNAL_CHAIN_START, REDIRECT } from '../../../utils/constants'
+import {
+ CONSENT_TO_SCOPE,
+ EXTERNAL_ACTION,
+ EXTERNAL_CHAIN_START,
+ REDIRECT,
+ PROVISIONING_FORM
+} from '../../../utils/constants'
import { checkAndUpdateIdentities, setActiveVerusId } from '../../../redux/reducers/identity/identity.actions';
import { signResponse } from '../../../rpc/calls/signResponse';
import { setError } from '../../../redux/reducers/error/error.actions';
@@ -47,6 +53,7 @@ class Login extends React.Component {
this.canProvision = canProvision;
this.tryLogin = this.tryLogin.bind(this);
+ this.tryProvision = this.tryProvision.bind(this);
this.selectId = this.selectId.bind(this);
this.cancel = this.cancel.bind(this);
}
@@ -89,6 +96,10 @@ class Login extends React.Component {
})
}
+ tryProvision() {
+ this.props.dispatch(setNavigationPath(PROVISIONING_FORM));
+ }
+
cancel() {
this.props.dispatch(setNavigationPath(CONSENT_TO_SCOPE));
}
diff --git a/src/components/LoginConsent/Login/Login.render.js b/src/components/LoginConsent/Login/Login.render.js
index b4c8ee8..33431b5 100644
--- a/src/components/LoginConsent/Login/Login.render.js
+++ b/src/components/LoginConsent/Login/Login.render.js
@@ -56,6 +56,7 @@ export const LoginRender = function () {
alignItems: "flex-start",
justifyContent: "center",
flex: 1,
+ paddingTop: 2,
}}
>
@@ -89,6 +90,29 @@ export const LoginRender = function () {
+
Date: Fri, 29 Nov 2024 15:53:16 -0800
Subject: [PATCH 04/12] Finish implementing the provisioning form by remaking
the component as a functional component and using a modified implementation
of the mobile app's component.
---
.../ProvisionIdentityConfirm.js | 6 +-
.../ProvisionIdentityForm.js | 369 ++++++++++++++++--
.../ProvisionIdentityForm.render.js | 132 -------
.../reducers/provision/provision.actions.js | 22 ++
.../reducers/provision/provision.reducer.js | 27 ++
.../reducers/provision/provision.types.js | 2 +
src/redux/rootReducer.js | 4 +-
src/rpc/calls/getAddresses.js | 27 ++
src/rpc/calls/getIdentity.js | 3 +-
src/utils/constants.js | 1 +
10 files changed, 429 insertions(+), 164 deletions(-)
delete mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.render.js
create mode 100644 src/redux/reducers/provision/provision.actions.js
create mode 100644 src/redux/reducers/provision/provision.reducer.js
create mode 100644 src/redux/reducers/provision/provision.types.js
create mode 100644 src/rpc/calls/getAddresses.js
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
index b314dc5..5ef6fe2 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
@@ -12,6 +12,9 @@ class ProvisionIdentityConfirm extends React.Component {
loading: false
}
+ console.log(props.loginConsentRequest)
+ console.log(props.provisioningInfo)
+
this.submitData = this.submitData.bind(this);
this.cancel = this.cancel.bind(this);
}
@@ -36,7 +39,8 @@ const mapStateToProps = (state) => {
loginConsentRequest: state.rpc.loginConsentRequest,
identities: state.identity.identities,
activeIdentity: state.identity.activeIdentity,
- originApp: state.origin.originApp
+ originApp: state.origin.originApp,
+ provisioningInfo: state.provision.provisioningInfo
};
};
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
index e3e4a34..e58cf34 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
@@ -1,6 +1,5 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { ProvisionIdentityFormRender } from './ProvisionIdentityForm.render';
+import React, { useEffect, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
import { PROVISIONING_CONFIRM, SELECT_LOGIN_ID } from '../../../../utils/constants';
import { setNavigationPath } from '../../../../redux/reducers/navigation/navigation.actions';
import {
@@ -9,44 +8,356 @@ import {
ID_FULLYQUALIFIEDNAME_VDXF_KEY,
ID_PARENT_VDXF_KEY,
LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY,
+ fromBase58Check,
} from 'verus-typescript-primitives';
+import Button from '@mui/material/Button';
+import TextField from '@mui/material/TextField';
+import { VerusIdLogo } from "../../../../images";
+import { getIdentity } from '../../../../rpc/calls/getIdentity';
+import { InputAdornment } from '@mui/material';
+import { setIdentityToProvisionField, setProvisioningInfo } from '../../../../redux/reducers/provision/provision.actions';
+import { getAddresses } from '../../../../rpc/calls/getAddresses';
-class ProvisionIdentityForm extends React.Component {
- constructor(props) {
- super(props);
+const ProvisionIdentityForm = (props) => {
+ const dispatch = useDispatch();
+ const { request } = useSelector((state) => state.rpc.loginConsentRequest);
+ const identityToProvisionField = useSelector((state) => state.provision.identityToProvisionField);
- const { request } = props.loginConsentRequest;
+ const hasProvisioningInfo = request != null && request.challenge.provisioning_info != null;
- const hasProvisioningInfo = request != null && request.challenge.provisioning_info != null;
+ const [state, setState] = useState({
+ friendlyNameMap: {},
+ provisioningInfo: hasProvisioningInfo
+ ? request.challenge.provisioning_info
+ : [],
+ provAddress: null,
+ provSystemId: null,
+ provFqn: null,
+ provParent: null,
+ provWebhook: null,
+ assignedIdentity: null,
+ loading: false,
+ parentname: '',
+ addresses: {},
+ });
- this.state = {
- loading: false,
- assignedIdentity: null,
- hasProvisioningInfo: hasProvisioningInfo,
+ const [formError, setFormError] = useState({
+ error: false,
+ description: ''
+ });
+
+ useEffect(() => {
+
+ // Extract the provisioning info from the request.
+ const updateProvisioningInfoProcessedData = async () => {
+ if (!hasProvisioningInfo) return;
+
+ const findProvisioningInfo = (key) =>
+ request.challenge.provisioning_info.find(
+ (x) => x.vdxfkey === key.vdxfid
+ );
+
+ const provAddress = findProvisioningInfo(ID_ADDRESS_VDXF_KEY);
+ const provSystemId = findProvisioningInfo(ID_SYSTEMID_VDXF_KEY);
+ const provFqn = findProvisioningInfo(ID_FULLYQUALIFIEDNAME_VDXF_KEY);
+ const provParent = findProvisioningInfo(ID_PARENT_VDXF_KEY);
+ const provWebhook = findProvisioningInfo(LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY);
+
+ setState((currentState) => ({
+ ...currentState,
+ provAddress,
+ provSystemId,
+ provFqn,
+ provParent,
+ provWebhook,
+ }));
};
- this.submitData = this.submitData.bind(this);
- this.cancel = this.cancel.bind(this);
- }
+ const initializeState = async () => {
+ await updateProvisioningInfoProcessedData();
- componentDidMount() {
- const { request } = this.props.loginConsentRequest;
- const provInfo = updateProvisioningInfoProcessedData(request);
- this.setState(provInfo, () => console.log(this.state));
- const {provAddress, provSystemId, provFqn, provParent, provWebhook} = provInfo;
- }
+ // Get the addresses of the wallet so the identity can be provisioned to one of them.
+ const addresses = await getAddresses(request.chainTicker, true, false);
+ setState((currentState) => ({
+ ...currentState,
+ addresses
+ }));
+
+ setState((currentState) => {
+ const provIdKey = currentState.provAddress || currentState.provFqn || null;
+
+ const identitykeys = provIdKey == null ? [] : [provIdKey];
+ if (currentState.provParent) identitykeys.push(currentState.provParent);
+ if (currentState.provSystemId) identitykeys.push(currentState.provSystemId);
+
+ const fetchIdentities = async () => {
+ let friendlyNameMap = currentState.friendlyNameMap;
+ let assignedIdentity = null;
+ let parentname = '';
- submitData() {
- this.props.dispatch(setNavigationPath(PROVISIONING_CONFIRM));
- }
+ for (const idKey of identitykeys) {
+ if (idKey != null) {
+ const identity = await getIdentity(request.chainTicker, idKey.data);
+
+ if (identity) {
+ friendlyNameMap[identity.identity.identityaddress] =
+ identity.identity.name;
+
+ if (provIdKey != null && idKey.data === provIdKey.data) {
+ assignedIdentity = identity.identity.identityaddress;
+ dispatch(setIdentityToProvisionField(identity.identity.name));
+ }
+ if (idKey.vdxfkey === ID_PARENT_VDXF_KEY.vdxfid) {
+ parentname = `.${identity.fullyqualifiedname}`
+ }
+ }
+ }
+ }
+
+ return { friendlyNameMap, assignedIdentity, parentname };
+ };
+
+ fetchIdentities().then(({ parentname, friendlyNameMap, assignedIdentity }) => {
+ setState({ ...currentState, friendlyNameMap, assignedIdentity, loading: false, parentname });
+ });
+
+ return { ...currentState, loading: true };
+ });
+ };
+
+ initializeState();
+ }, []);
+
+ const formHasError = () => {
+ const identity = identityToProvisionField?.trim() || '';
+
+ if (!identity) {
+ setFormError({
+ error: true,
+ description: 'Identity is a required field.'
+ });
+ return true;
+ }
+
+ try {
+ fromBase58Check(identity);
+ if (state.parentname) {
+ setFormError({
+ error: true,
+ description: 'i-Address cannot have a parent name.'
+ });
+ return true;
+ }
+ } catch (e) {
+ const formattedId = state.parentname ? `${identity}${state.parentname}` : `${identity}@`;
+ if (!formattedId.endsWith('@')) {
+ setFormError({
+ error: true,
+ description: 'Identity not a valid identity handle or iAddress.'
+ });
+ return true;
+ }
+ }
+
+ // Clear any old errors.
+ setFormError({
+ error: false,
+ description: ''
+ });
+
+ return false;
+ };
+
+ const submitData = async () => {
+ if (formHasError()) return;
- cancel() {
- this.props.dispatch(setNavigationPath(SELECT_LOGIN_ID));
+ setState((currentState) => {return { ...currentState, loading: true}});
+
+ const identity = identityToProvisionField;
+
+ let formattedId;
+
+ try {
+ fromBase58Check(identity);
+ formattedId = identity;
+ } catch(e) {
+ formattedId = state.parentname ? `${identity}${state.parentname}` : `${identity}.${request.chainName}@`;
+ }
+
+ let identityError = false;
+
+ try {
+ await getIdentity(request.chainTicker, formattedId);
+
+ // If we get a result back, that means the identity must already exist.
+ identityError = true;
+ setFormError({
+ error: true,
+ description: 'Identity name taken, please select a different name.'
+ });
+
+ } catch (e) {
+ // Check for an invalid identity, otherwise the identity is valid since it does not already exist
+ // and it is using valid characters.
+ if (e.message.includes("Identity parameter must be valid friendly name or identity address")) {
+ identityError = true;
+ setFormError({
+ error: true,
+ description: 'Identity name must not include / : * ? " < > | @ .'
+ });
+ }
+ }
+
+ setState((currentState) => {return { ...currentState, loading: false}});
+
+ if (!identityError) {
+ // Find a public address to provision the identity to.
+ const publicAddresses = state.addresses.public.filter((address) => address.tag === "public");
+
+ dispatch(setProvisioningInfo({
+ primaryAddress: publicAddresses[0],
+ provAddress: state.provAddress,
+ provSystemId: state.provSystemId,
+ provFqn: state.provFqn,
+ provParent: state.provParent,
+ provWebhook: state.provWebhook,
+ friendlyNameMap: state.friendlyNameMap
+ }));
+ dispatch(setNavigationPath(PROVISIONING_CONFIRM));
+ }
}
- render() {
- return ProvisionIdentityFormRender.call(this);
+ const cancel = () => {
+ dispatch(setNavigationPath(SELECT_LOGIN_ID));
}
+
+ return (
+
+
+
+
+
+ {`Request VerusID`}
+
+
+
+
+ {
+ const text = event.target.value;
+ if (state.assignedIdentity == null && !text.endsWith("@")) {
+ dispatch(setIdentityToProvisionField(text));
+ }
+ }}
+ InputProps={{
+ endAdornment: {state.parentname ? state.parentname : ``} ,
+ }}
+ >
+
+
+
+
+
+ cancel()}
+ style={{
+ width: 120,
+ marginRight: 32,
+ padding: 8,
+ }}
+ >
+ {"Back"}
+
+ submitData()}
+ style={{
+ width: 120,
+ padding: 8,
+ }}
+ >
+ {"Continue"}
+
+
+
+
+
+ );
}
const hasProvisioningInfo = (request) => {
@@ -87,4 +398,4 @@ const mapStateToProps = (state) => {
};
};
-export default connect(mapStateToProps)(ProvisionIdentityForm);
\ No newline at end of file
+export default ProvisionIdentityForm;
\ No newline at end of file
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.render.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.render.js
deleted file mode 100644
index a9caa1f..0000000
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.render.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import React, { useState } from "react";
-import Button from '@mui/material/Button';
-import TextField from '@mui/material/TextField';
-import { VerusIdLogo } from "../../../../images";
-
-export const ProvisionIdentityFormRender = function () {
- const { loading } = this.state
-
- return (
-
-
-
-
-
- {`Request VerusID`}
-
-
-
-
- {
- const text = event.target.value
- console.log("text", text);
- if (this.state.assignedIdentity == null && !text.endsWith("@")) {
- this.setState({
- assignedIdentity: text
- });
- }
- }}
- >
-
-
-
-
-
- this.cancel()}
- style={{
- width: 120,
- marginRight: 32,
- padding: 8,
- }}
- >
- {"Back"}
-
- this.submitData()}
- style={{
- width: 120,
- padding: 8,
- }}
- >
- {"Continue"}
-
-
-
-
-
- );
-};
\ No newline at end of file
diff --git a/src/redux/reducers/provision/provision.actions.js b/src/redux/reducers/provision/provision.actions.js
new file mode 100644
index 0000000..b9df906
--- /dev/null
+++ b/src/redux/reducers/provision/provision.actions.js
@@ -0,0 +1,22 @@
+import {
+ SET_IDENTITY_TO_PROVISION_FIELD,
+ SET_PROVISONING_INFO
+} from "./provision.types"
+
+export const setIdentityToProvisionField = (idToProvisionField) => {
+ return {
+ type: SET_IDENTITY_TO_PROVISION_FIELD,
+ payload: {
+ idToProvisionField
+ }
+ }
+}
+
+export const setProvisioningInfo= (provisioningInfo) => {
+ return {
+ type: SET_PROVISONING_INFO,
+ payload: {
+ provisioningInfo
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/redux/reducers/provision/provision.reducer.js b/src/redux/reducers/provision/provision.reducer.js
new file mode 100644
index 0000000..ecc493b
--- /dev/null
+++ b/src/redux/reducers/provision/provision.reducer.js
@@ -0,0 +1,27 @@
+/*
+ This reducer contains the information about provisioning.
+*/
+import {
+ SET_IDENTITY_TO_PROVISION_FIELD,
+ SET_PROVISONING_INFO
+} from "./provision.types";
+
+export const provision = (state = {
+ identityToProvisionField: "",
+ provisioningInfo: {},
+}, action) => {
+ switch (action.type) {
+ case SET_IDENTITY_TO_PROVISION_FIELD:
+ return {
+ ...state,
+ identityToProvisionField: action.payload.idToProvisionField
+ }
+ case SET_PROVISONING_INFO:
+ return {
+ ...state,
+ provisioningInfo: action.payload.provisioningInfo
+ }
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/src/redux/reducers/provision/provision.types.js b/src/redux/reducers/provision/provision.types.js
new file mode 100644
index 0000000..164e22e
--- /dev/null
+++ b/src/redux/reducers/provision/provision.types.js
@@ -0,0 +1,2 @@
+export const SET_IDENTITY_TO_PROVISION_FIELD = "SET_IDENTITY_TO_PROVISION_FIELD"
+export const SET_PROVISONING_INFO = "SET_PROVISONING_INFO"
\ No newline at end of file
diff --git a/src/redux/rootReducer.js b/src/redux/rootReducer.js
index fd41f5e..81009ad 100644
--- a/src/redux/rootReducer.js
+++ b/src/redux/rootReducer.js
@@ -4,13 +4,15 @@ import { rpc } from './reducers/rpc/rpc.reducer';
import { identity } from './reducers/identity/identity.reducer';
import { origin } from './reducers/origin/origin.reducer';
import { error } from './reducers/error/error.reducer';
+import { provision } from './reducers/provision/provision.reducer';
const rootReducer = combineReducers({
navigation,
rpc,
identity,
origin,
- error
+ error,
+ provision
});
export default rootReducer;
\ No newline at end of file
diff --git a/src/rpc/calls/getAddresses.js b/src/rpc/calls/getAddresses.js
new file mode 100644
index 0000000..88060ae
--- /dev/null
+++ b/src/rpc/calls/getAddresses.js
@@ -0,0 +1,27 @@
+import { API_GET_ADDRESSES, NATIVE, POST } from "../../utils/constants"
+import { getApiData } from "../callCreator"
+
+/**
+ * Gets addresses of the current wallet with the options to include private addresses
+ * and/or include private balances.
+ */
+export const getAddresses = async (chainId, includePrivateAddresses, includePrivateBalances) => {
+ try {
+ const res = await getApiData(
+ NATIVE,
+ API_GET_ADDRESSES,
+ {
+ chainTicker: chainId,
+ includePrivateAddresses: includePrivateAddresses,
+ includePrivateBalances, includePrivateBalances
+ },
+ POST,
+ true
+ );
+ if (res.msg !== "success") throw new Error(res.result);
+ else return res.result;
+ } catch (e) {
+ console.error(e.message);
+ throw new Error(e.message);
+ }
+}
\ No newline at end of file
diff --git a/src/rpc/calls/getIdentity.js b/src/rpc/calls/getIdentity.js
index 7172a17..d64317b 100644
--- a/src/rpc/calls/getIdentity.js
+++ b/src/rpc/calls/getIdentity.js
@@ -19,7 +19,8 @@ export const getIdentity = async (chainId, id) => {
if (res.msg !== "success") throw new Error(res.result);
else return res.result;
} catch (e) {
- console.error(e.message);
+ // Don't console log the error since the error can be used to determine if
+ // an identity already exists when provisioning.
throw new Error(e.message);
}
}
\ No newline at end of file
diff --git a/src/utils/constants.js b/src/utils/constants.js
index de5275f..aa6b6ce 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -20,6 +20,7 @@ export const API_CLOSE_PLUGIN = 'plugin/close'
export const API_ACTIVATE_COIN = 'coins/activate'
export const API_CHECK_ZCASH_PARAMS = 'zcashparamsexist'
export const API_DL_ZCASH_PARAMS = 'zcparamsdl'
+export const API_GET_ADDRESSES = "get_addresses"
export const API_GET_BLOCK = 'get_block'
export const API_GET_CURRENCY = 'get_currency'
export const API_GET_IDENTITIES = 'get_identities'
From 84d5a4d9db0b277c71bbea3b588a3c2023b83e9b Mon Sep 17 00:00:00 2001
From: mcstoer <49734282+mcstoer@users.noreply.github.com>
Date: Tue, 3 Dec 2024 10:00:12 -0800
Subject: [PATCH 05/12] Add WIP version of the confirmation screen for
provisioning.
---
.../ProvisionIdentityConfirm.js | 359 ++++++++++++++++--
.../ProvisionIdentityConfirm.render.js | 107 ------
.../ProvisionIdentityForm.js | 38 --
3 files changed, 325 insertions(+), 179 deletions(-)
delete mode 100644 src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.render.js
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
index 5ef6fe2..7e30a7b 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
@@ -1,47 +1,338 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { ProvisionIdentityConfirmRender } from './ProvisionIdentityConfirm.render';
+import React, { useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
import { PROVISIONING_FORM, PROVISIONING_RESULT } from '../../../../utils/constants';
import { setNavigationPath } from '../../../../redux/reducers/navigation/navigation.actions';
+import Button from '@mui/material/Button';
+import { VerusIdLogo } from "../../../../images";
+import Card from '@mui/material/Card';
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemText from '@mui/material/ListItemText';
+import Divider from '@mui/material/Divider';
+import Box from '@mui/material/Box'
+import {
+ LOGIN_CONSENT_CONTEXT_VDXF_KEY,
+ LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY,
+ LoginConsentProvisioningRequest,
+ LoginConsentProvisioningChallenge,
+ LoginConsentRequest
+} from 'verus-typescript-primitives';
+import { getIdentity } from '../../../../rpc/calls/getIdentity';
-class ProvisionIdentityConfirm extends React.Component {
- constructor(props) {
- super(props);
+const ProvisionIdentityConfirm = (props) => {
+ const dispatch = useDispatch();
+ const { request } = useSelector((state) => state.rpc.loginConsentRequest);
+ const provisioningInfo = useSelector((state) => state.provision.provisioningInfo);
+ const identityToProvisionField = useSelector((state) => state.provision.identityToProvisionField);
- this.state = {
- loading: false
- }
+ console.log(provisioningInfo);
- console.log(props.loginConsentRequest)
- console.log(props.provisioningInfo)
+ const {
+ primaryAddress,
+ provAddress,
+ provSystemId,
+ provFqn,
+ provParent,
+ provWebhook,
+ friendlyNameMap,
+ } = provisioningInfo;
- this.submitData = this.submitData.bind(this);
- this.cancel = this.cancel.bind(this);
- }
+ const displayIdentity = provFqn
+ ? provFqn.data
+ : friendlyNameMap[
+ identityToProvisionField
+ ]
+ ? `${
+ friendlyNameMap[
+ identityToProvisionField
+ ]
+ }@`
+ : identityToProvisionField;
- submitData() {
- this.props.dispatch(setNavigationPath(PROVISIONING_RESULT));
- }
+ const displayParent = provParent != null && friendlyNameMap[provParent.data]
+ ? friendlyNameMap[provParent.data]
+ : null;
+
+ const displaySystemid = provSystemId != null && friendlyNameMap[provSystemId.data]
+ ? friendlyNameMap[provSystemId.data]
+ : null;
+
+ const [state, setState] = useState({
+ loading: false,
+ });
- cancel() {
- this.props.dispatch(setNavigationPath(PROVISIONING_FORM));
+ const cancel = () => {
+ dispatch(setNavigationPath(PROVISIONING_FORM));
}
- render() {
- return ProvisionIdentityConfirmRender.call(this);
- }
-}
+ const submitData = async () => {
+ setState({ loading: false});
+
+ const submissionSuccess = (response, requestedFqn) => {
+ setState({ loading: false});
+ /*
+ this.props.navigation.navigate(SEND_MODAL_FORM_STEP_RESULT, {
+ response: response,
+ fullyQualifiedName: requestedFqn,
+ success: true
+ });
+ */
+ }
+
+ const submissionError = (msg) => {
+ console.error('Error', msg);
+
+ setState({ loading: false});
+ }
+
+ try {
+ const loginRequest = new LoginConsentRequest(request)
+
+ const webhookSubject = loginRequest.challenge.provisioning_info ? loginRequest.challenge.provisioning_info.find(x => {
+ return x.vdxfkey === LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY.vdxfid
+ }) : null
+
+ if (webhookSubject == null) throw new Error("No endpoint for ID provisioning")
+
+ const webhookUrl = webhookSubject.data
-// TODO: Figure out if this should be different
-const mapStateToProps = (state) => {
- return {
- path: state.navigation.path,
- loginConsentRequest: state.rpc.loginConsentRequest,
- identities: state.identity.identities,
- activeIdentity: state.identity.activeIdentity,
- originApp: state.origin.originApp,
- provisioningInfo: state.provision.provisioningInfo
+ const identity =
+ identityToProvisionField != null
+ ? identityToProvisionField.trim()
+ : '';
+
+ let identityName;
+ let isIAddress;
+ let parent;
+ let systemid;
+ let nameId;
+ let requestedFqn;
+
+ try {
+ fromBase58Check(identity);
+ isIAddress = true;
+ } catch (e) {
+ isIAddress = false;
+ }
+
+ if (isIAddress) {
+ const identityObj = await getIdentity(request.chainTicker, identity);
+
+ // if (identityObj.error) throw new Error(identityObj.error.message)
+
+ identityName = identityObj.identity.name;
+ parent = identityObj.identity.parent;
+ systemid = identityObj.identity.systemid;
+ nameId = identity;
+ requestedFqn = identityObj.fullyqualifiedname;
+
+ } else {
+ identityName = identity.split("@")[0];
+ parent = provParent ? provParent.data : null;
+ systemid = provSystemId ? provSystemId.data : null;
+ const parentObj = await getIdentity(request.chainTicker, parent ? parent : loginRequest.system_id);
+
+ requestedFqn = `${identityName.split(".")[0]}.${parentObj.fullyqualifiedname}`;
+ // TODO: Implement getVdxfId
+ nameId = (await getVdxfId(coinObj.system_id, requestedFqn)).result.vdxfid;
+ }
+
+ const provisionRequest = new LoginConsentProvisioningRequest({
+ signing_address: primaryAddress,
+
+ challenge: LoginConsentProvisioningChallenge({
+ challenge_id: loginRequest.challenge.challenge_id,
+ created_at: Number((Date.now() / 1000).toFixed(0)),
+ name: identityName,
+ system_id: systemid,
+ parent: parent
+ }),
+ });
+
+ const res = provisionRequest;
+ console.log(res)
+
+ /*
+ const signedRequest = await signIdProvisioningRequest(coinObj, provisionRequest);
+
+ const res = await axios.post(
+ webhookUrl,
+ signedRequest
+ );
+
+ const provisioningName = (await getIdentity(coinObj.system_id, loginRequest.signing_id)).result.identity.name;
+ const newLoadingNotification = new LoadingNotification();
+
+ await handleProvisioningResponse(coinObj, res.data, loginRequest.toBuffer().toString('base64'),
+ this.props.sendModal.data.fromService, provisioningName, newLoadingNotification.uid, nameId, requestedFqn, async () => {
+
+ newLoadingNotification.body = "";
+ let formattedName = ''
+ const lastDotIndex = requestedFqn.lastIndexOf('.');
+ if (lastDotIndex === -1) formattedName = requestedFqn; // return the original string if there's no dot
+ else formattedName = requestedFqn.substring(0, lastDotIndex);
+
+ newLoadingNotification.title = [`${formattedName}@`, ` is being provisioned by `, `${provisioningName}@`]
+ newLoadingNotification.acchash = this.props.activeAccount.accountHash;
+ newLoadingNotification.icon = NOTIFICATION_ICON_VERUSID;
+
+ dispatchAddNotification(newLoadingNotification);
+ });
+ */
+ submissionSuccess(res.data, requestedFqn)
+ } catch (e) {
+ submissionError(e.message)
+ }
};
-};
+
+
+ return (
+
+
+
+
+
+ {`Review Provisioning Request`}
+
+
+
+
+
+
+
+
+
+ {provAddress &&
+
+
+
+
+
+
+
+ }
+
+
+
+
+
+ {displayParent &&
+
+
+
+
+
+
+
+ }
+ {provFqn &&
+
+
+
+
+
+
+
+ }
+ {displaySystemid &&
+
+
+
+
+
+
+
+ }
+
+
+
+
+
+
+ cancel()}
+ style={{
+ width: 120,
+ marginRight: 32,
+ padding: 8,
+ }}
+ >
+ {"Back"}
+
+ submitData()}
+ style={{
+ width: 120,
+ padding: 8,
+ }}
+ >
+ {"Continue"}
+
+
+
+
+
+ );
+}
-export default connect(mapStateToProps)(ProvisionIdentityConfirm);
\ No newline at end of file
+export default ProvisionIdentityConfirm;
\ No newline at end of file
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.render.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.render.js
deleted file mode 100644
index 2899b89..0000000
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.render.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from "react";
-import Button from '@mui/material/Button';
-import { VerusIdLogo } from "../../../../images";
-
-export const ProvisionIdentityConfirmRender = function () {
- const { loading } = this.state
-
- return (
-
-
-
-
-
- {`Provisioning Confirm`}
-
-
-
-
-
-
-
- this.cancel()}
- style={{
- width: 120,
- marginRight: 32,
- padding: 8,
- }}
- >
- {"Back"}
-
- this.submitData()}
- style={{
- width: 120,
- padding: 8,
- }}
- >
- {"Continue"}
-
-
-
-
-
- );
-};
\ No newline at end of file
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
index e58cf34..6eb201f 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
@@ -360,42 +360,4 @@ const ProvisionIdentityForm = (props) => {
);
}
-const hasProvisioningInfo = (request) => {
- return request != null && request.challenge.provisioning_info != null;
-}
-
-const updateProvisioningInfoProcessedData = (request) => {
- if (!hasProvisioningInfo(request)) return;
-
- const findProvisioningInfo = (key) =>
- request.challenge.provisioning_info.find(
- (x) => x.vdxfkey === key.vdxfid
- );
-
- const provAddress = findProvisioningInfo(ID_ADDRESS_VDXF_KEY);
- const provSystemId = findProvisioningInfo(ID_SYSTEMID_VDXF_KEY);
- const provFqn = findProvisioningInfo(ID_FULLYQUALIFIEDNAME_VDXF_KEY);
- const provParent = findProvisioningInfo(ID_PARENT_VDXF_KEY);
- const provWebhook = findProvisioningInfo(LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY);
-
- return {
- provAddress: provAddress,
- provSystemId: provSystemId,
- provFqn: provFqn,
- provParent: provParent,
- provWebhook: provWebhook,
- };
-};
-
-// TODO: Figure out if this should be different
-const mapStateToProps = (state) => {
- return {
- path: state.navigation.path,
- loginConsentRequest: state.rpc.loginConsentRequest,
- identities: state.identity.identities,
- activeIdentity: state.identity.activeIdentity,
- originApp: state.origin.originApp
- };
-};
-
export default ProvisionIdentityForm;
\ No newline at end of file
From cd1a498f657c38b8d55838ea6b85448d41a576fa Mon Sep 17 00:00:00 2001
From: mcstoer <49734282+mcstoer@users.noreply.github.com>
Date: Tue, 17 Dec 2024 14:52:25 -0800
Subject: [PATCH 06/12] Add first working version of provisioning.
---
package.json | 6 +
.../ProvisionIdentityConfirm.js | 203 +--
.../ProvisionIdentityForm.js | 95 +-
.../ProvisionIdentityResult.js | 198 ++-
src/containers/SnackbarAlert.js | 17 +
src/env.js | 2 +-
.../reducers/provision/provision.actions.js | 44 +-
.../reducers/provision/provision.reducer.js | 49 +-
.../reducers/provision/provision.types.js | 7 +-
src/rpc/calls/getVdxfId.js | 26 +
src/rpc/calls/signIdProvisioningRequest.js | 26 +
src/rpc/calls/verifyIdProvisioningResponse.js | 21 +
src/themes/main.js | 1 -
src/utils/constants.js | 3 +
yarn.lock | 1229 ++++++++++++++++-
15 files changed, 1737 insertions(+), 190 deletions(-)
create mode 100644 src/containers/SnackbarAlert.js
create mode 100644 src/rpc/calls/getVdxfId.js
create mode 100644 src/rpc/calls/signIdProvisioningRequest.js
create mode 100644 src/rpc/calls/verifyIdProvisioningResponse.js
diff --git a/package.json b/package.json
index b9a1ec7..bc7ca8e 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"@testing-library/jest-dom": "5.11.4",
"@testing-library/react": "11.1.0",
"@testing-library/user-event": "12.1.10",
+ "axios": "1.7.7",
"base64url": "https://github.com/VerusCoin/base64url.git",
"bignumber.js": "9.1.2",
"blake2b": "https://github.com/VerusCoin/blake2b.git",
@@ -68,8 +69,13 @@
"@babel/polyfill": "7.0.0",
"@babel/preset-env": "7.0.0",
"@babel/preset-react": "7.0.0",
+ "@eslint/js": "9.16.0",
+ "@stylistic/eslint-plugin-js": "2.11.0",
"babel-loader": "8.3.0",
"css-loader": "4.3.0",
+ "eslint": "9.16.0",
+ "eslint-plugin-react": "7.37.2",
+ "globals": "15.13.0",
"html-webpack-plugin": "4.5.2",
"mini-css-extract-plugin": "1.6.2",
"node-sass": "9.0.0",
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
index 7e30a7b..e131815 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityConfirm/ProvisionIdentityConfirm.js
@@ -9,24 +9,32 @@ import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Divider from '@mui/material/Divider';
-import Box from '@mui/material/Box'
+import Box from '@mui/material/Box';
import {
- LOGIN_CONSENT_CONTEXT_VDXF_KEY,
LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY,
LoginConsentProvisioningRequest,
LoginConsentProvisioningChallenge,
- LoginConsentRequest
+ LoginConsentRequest,
+ fromBase58Check,
+ LoginConsentProvisioningResponse,
+ LOGIN_CONSENT_PROVISIONING_RESULT_STATE_FAILED,
+ LOGIN_CONSENT_PROVISIONING_RESULT_STATE_PENDINGAPPROVAL,
+ LOGIN_CONSENT_PROVISIONING_RESULT_STATE_COMPLETE,
} from 'verus-typescript-primitives';
import { getIdentity } from '../../../../rpc/calls/getIdentity';
+import { getVdxfId } from '../../../../rpc/calls/getVdxfId';
+import { signIdProvisioningRequest } from '../../../../rpc/calls/signIdProvisioningRequest';
+import axios from 'axios';
+import { SnackbarAlert } from '../../../../containers/SnackbarAlert';
+import { verifyIdProvisioningResponse } from '../../../../rpc/calls/verifyIdProvisioningResponse';
+import { setProvisioningName, setProvisioningResponse, setRequestedFqn } from '../../../../redux/reducers/provision/provision.actions';
-const ProvisionIdentityConfirm = (props) => {
+const ProvisionIdentityConfirm = () => {
const dispatch = useDispatch();
const { request } = useSelector((state) => state.rpc.loginConsentRequest);
const provisioningInfo = useSelector((state) => state.provision.provisioningInfo);
const identityToProvisionField = useSelector((state) => state.provision.identityToProvisionField);
- console.log(provisioningInfo);
-
const {
primaryAddress,
provAddress,
@@ -37,64 +45,118 @@ const ProvisionIdentityConfirm = (props) => {
friendlyNameMap,
} = provisioningInfo;
- const displayIdentity = provFqn
- ? provFqn.data
- : friendlyNameMap[
- identityToProvisionField
- ]
- ? `${
- friendlyNameMap[
- identityToProvisionField
- ]
- }@`
- : identityToProvisionField;
-
- const displayParent = provParent != null && friendlyNameMap[provParent.data]
- ? friendlyNameMap[provParent.data]
- : null;
+ let displayIdentity;
- const displaySystemid = provSystemId != null && friendlyNameMap[provSystemId.data]
- ? friendlyNameMap[provSystemId.data]
- : null;
+ if (provFqn) {
+ if (provFqn.data) {
+ displayIdentity = friendlyNameMap[identityToProvisionField];
+ } else {
+ displayIdentity = `${friendlyNameMap[identityToProvisionField]}@`;
+ }
+ } else {
+ displayIdentity = identityToProvisionField;
+ }
+
+ let displayParent;
+
+ if (provParent != null) {
+ if (friendlyNameMap[provParent.data]) {
+ displayParent = friendlyNameMap[provParent.data];
+ } else {
+ displayParent = provParent.data;
+ }
+ } else {
+ displayParent = null;
+ }
+
+ let displaySystemid;
+
+ if (provSystemId != null) {
+ if (friendlyNameMap[provSystemId.data]) {
+ displaySystemid = friendlyNameMap[provSystemId.data];
+ } else {
+ displaySystemid = provSystemId.data;
+ }
+ } else {
+ displaySystemid = null;
+ }
const [state, setState] = useState({
loading: false,
});
+ const [submissionError, setSubmissionError] = useState({
+ showError: false,
+ description: ''
+ });
+
+ const handleProvisioningResponse = async (response, requestedId, requestedFqn) => {
+ const res = new LoginConsentProvisioningResponse(response);
+
+ // Check the response to see if it is valid and if there are errors.
+ const verified = await verifyIdProvisioningResponse(res);
+
+ if (!verified) throw new Error('Failed to verify response from service');
+
+ const {decision} = res;
+ const {result} = decision;
+ const {
+ error_desc,
+ state,
+ } = result;
+
+ if (state === LOGIN_CONSENT_PROVISIONING_RESULT_STATE_FAILED.vdxfid) {
+ throw new Error(error_desc);
+ } else if (state === LOGIN_CONSENT_PROVISIONING_RESULT_STATE_PENDINGAPPROVAL.vdxfid ||
+ state === LOGIN_CONSENT_PROVISIONING_RESULT_STATE_COMPLETE.vdxfid) {
+
+ if (!result.identity_address && !result.fully_qualified_name) {
+ throw new Error('Provisioning response did not contain an identity or fully qualified name');
+ }
+
+ if (result.identity_address && result.identity_address !== requestedId) {
+ throw new Error(`Provisioning response identity [${result.identity_address}] address does not match requested identity address[${requestedId}]`);
+ }
+
+ if (result.fully_qualified_name && result.fully_qualified_name.toLowerCase() !== requestedFqn.toLowerCase()) {
+ throw new Error(`Provisioning response fully qualified name [${result.fully_qualified_name.toLowerCase()}] does not match requested fully qualified name[${requestedFqn.toLowerCase()}]`);
+ }
+ }
+ };
+
const cancel = () => {
dispatch(setNavigationPath(PROVISIONING_FORM));
- }
+ };
const submitData = async () => {
setState({ loading: false});
- const submissionSuccess = (response, requestedFqn) => {
+ const submissionSuccess = (response, requestedFqn, provisioningName) => {
setState({ loading: false});
- /*
- this.props.navigation.navigate(SEND_MODAL_FORM_STEP_RESULT, {
- response: response,
- fullyQualifiedName: requestedFqn,
- success: true
- });
- */
- }
+ dispatch(setProvisioningResponse(response));
+ dispatch(setRequestedFqn(requestedFqn));
+ dispatch(setProvisioningName(provisioningName));
+ dispatch(setNavigationPath(PROVISIONING_RESULT));
+ };
const submissionError = (msg) => {
- console.error('Error', msg);
-
+ setSubmissionError({
+ showError: true,
+ description: msg,
+ });
setState({ loading: false});
- }
+ };
try {
- const loginRequest = new LoginConsentRequest(request)
+ const loginRequest = new LoginConsentRequest(request);
const webhookSubject = loginRequest.challenge.provisioning_info ? loginRequest.challenge.provisioning_info.find(x => {
- return x.vdxfkey === LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY.vdxfid
- }) : null
+ return x.vdxfkey === LOGIN_CONSENT_ID_PROVISIONING_WEBHOOK_VDXF_KEY.vdxfid;
+ }) : null;
- if (webhookSubject == null) throw new Error("No endpoint for ID provisioning")
+ if (webhookSubject == null) throw new Error("No endpoint for ID provisioning");
- const webhookUrl = webhookSubject.data
+ const webhookUrl = webhookSubject.data;
const identity =
identityToProvisionField != null
@@ -111,14 +173,12 @@ const ProvisionIdentityConfirm = (props) => {
try {
fromBase58Check(identity);
isIAddress = true;
- } catch (e) {
+ } catch {
isIAddress = false;
}
if (isIAddress) {
const identityObj = await getIdentity(request.chainTicker, identity);
-
- // if (identityObj.error) throw new Error(identityObj.error.message)
identityName = identityObj.identity.name;
parent = identityObj.identity.parent;
@@ -133,14 +193,13 @@ const ProvisionIdentityConfirm = (props) => {
const parentObj = await getIdentity(request.chainTicker, parent ? parent : loginRequest.system_id);
requestedFqn = `${identityName.split(".")[0]}.${parentObj.fullyqualifiedname}`;
- // TODO: Implement getVdxfId
- nameId = (await getVdxfId(coinObj.system_id, requestedFqn)).result.vdxfid;
+ nameId = (await getVdxfId(request.chainTicker, requestedFqn)).vdxfid;
}
const provisionRequest = new LoginConsentProvisioningRequest({
signing_address: primaryAddress,
- challenge: LoginConsentProvisioningChallenge({
+ challenge: new LoginConsentProvisioningChallenge({
challenge_id: loginRequest.challenge.challenge_id,
created_at: Number((Date.now() / 1000).toFixed(0)),
name: identityName,
@@ -148,40 +207,24 @@ const ProvisionIdentityConfirm = (props) => {
parent: parent
}),
});
-
- const res = provisionRequest;
- console.log(res)
- /*
- const signedRequest = await signIdProvisioningRequest(coinObj, provisionRequest);
-
+ const signedRequest = await signIdProvisioningRequest(request.chainTicker, provisionRequest, primaryAddress);
+
+ // The responding server should include the error within the response instead of
+ // using an error code.
const res = await axios.post(
webhookUrl,
signedRequest
);
- const provisioningName = (await getIdentity(coinObj.system_id, loginRequest.signing_id)).result.identity.name;
- const newLoadingNotification = new LoadingNotification();
-
- await handleProvisioningResponse(coinObj, res.data, loginRequest.toBuffer().toString('base64'),
- this.props.sendModal.data.fromService, provisioningName, newLoadingNotification.uid, nameId, requestedFqn, async () => {
-
- newLoadingNotification.body = "";
- let formattedName = ''
- const lastDotIndex = requestedFqn.lastIndexOf('.');
- if (lastDotIndex === -1) formattedName = requestedFqn; // return the original string if there's no dot
- else formattedName = requestedFqn.substring(0, lastDotIndex);
-
- newLoadingNotification.title = [`${formattedName}@`, ` is being provisioned by `, `${provisioningName}@`]
- newLoadingNotification.acchash = this.props.activeAccount.accountHash;
- newLoadingNotification.icon = NOTIFICATION_ICON_VERUSID;
+ const provisionResponse = res.data;
+ await handleProvisioningResponse(provisionResponse, nameId, requestedFqn);
- dispatchAddNotification(newLoadingNotification);
- });
- */
- submissionSuccess(res.data, requestedFqn)
+ const provisioningName = (await getIdentity(request.chainTicker, loginRequest.signing_id)).identity.name;
+
+ submissionSuccess(res.data, requestedFqn, provisioningName);
} catch (e) {
- submissionError(e.message)
+ submissionError(e.message);
}
};
@@ -253,7 +296,7 @@ const ProvisionIdentityConfirm = (props) => {
-
+
{displayParent &&
@@ -285,7 +328,11 @@ const ProvisionIdentityConfirm = (props) => {
-
+
{setSubmissionError(false, '');}}
+ >
{
submitData()}
style={{
@@ -326,7 +373,7 @@ const ProvisionIdentityConfirm = (props) => {
padding: 8,
}}
>
- {"Continue"}
+ {"Request"}
diff --git a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
index 6eb201f..7274589 100644
--- a/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
+++ b/src/components/LoginConsent/ProvisionIdentity/ProvisionIdentityForm/ProvisionIdentityForm.js
@@ -17,8 +17,10 @@ import { getIdentity } from '../../../../rpc/calls/getIdentity';
import { InputAdornment } from '@mui/material';
import { setIdentityToProvisionField, setProvisioningInfo } from '../../../../redux/reducers/provision/provision.actions';
import { getAddresses } from '../../../../rpc/calls/getAddresses';
+import Box from '@mui/material/Box';
+import CircularProgress from '@mui/material/CircularProgress';
-const ProvisionIdentityForm = (props) => {
+const ProvisionIdentityForm = () => {
const dispatch = useDispatch();
const { request } = useSelector((state) => state.rpc.loginConsentRequest);
const identityToProvisionField = useSelector((state) => state.provision.identityToProvisionField);
@@ -41,7 +43,7 @@ const ProvisionIdentityForm = (props) => {
addresses: {},
});
- const [formError, setFormError] = useState({
+ const [formError, setFormError] = useState({
error: false,
description: ''
});
@@ -74,6 +76,11 @@ const ProvisionIdentityForm = (props) => {
};
const initializeState = async () => {
+ setState((currentState) => ({
+ ...currentState,
+ loading: true,
+ }));
+
await updateProvisioningInfoProcessedData();
// Get the addresses of the wallet so the identity can be provisioned to one of them.
@@ -108,7 +115,7 @@ const ProvisionIdentityForm = (props) => {
dispatch(setIdentityToProvisionField(identity.identity.name));
}
if (idKey.vdxfkey === ID_PARENT_VDXF_KEY.vdxfid) {
- parentname = `.${identity.fullyqualifiedname}`
+ parentname = `.${identity.fullyqualifiedname}`;
}
}
}
@@ -129,7 +136,7 @@ const ProvisionIdentityForm = (props) => {
}, []);
const formHasError = () => {
- const identity = identityToProvisionField?.trim() || '';
+ const identity = identityToProvisionField ? identityToProvisionField.trim() : '';
if (!identity) {
setFormError({
@@ -148,7 +155,7 @@ const ProvisionIdentityForm = (props) => {
});
return true;
}
- } catch (e) {
+ } catch {
const formattedId = state.parentname ? `${identity}${state.parentname}` : `${identity}@`;
if (!formattedId.endsWith('@')) {
setFormError({
@@ -171,7 +178,7 @@ const ProvisionIdentityForm = (props) => {
const submitData = async () => {
if (formHasError()) return;
- setState((currentState) => {return { ...currentState, loading: true}});
+ setState((currentState) => {return { ...currentState, loading: true};});
const identity = identityToProvisionField;
@@ -180,7 +187,7 @@ const ProvisionIdentityForm = (props) => {
try {
fromBase58Check(identity);
formattedId = identity;
- } catch(e) {
+ } catch {
formattedId = state.parentname ? `${identity}${state.parentname}` : `${identity}.${request.chainName}@`;
}
@@ -208,14 +215,14 @@ const ProvisionIdentityForm = (props) => {
}
}
- setState((currentState) => {return { ...currentState, loading: false}});
+ setState((currentState) => {return { ...currentState, loading: false};});
if (!identityError) {
// Find a public address to provision the identity to.
const publicAddresses = state.addresses.public.filter((address) => address.tag === "public");
dispatch(setProvisioningInfo({
- primaryAddress: publicAddresses[0],
+ primaryAddress: publicAddresses[0].address,
provAddress: state.provAddress,
provSystemId: state.provSystemId,
provFqn: state.provFqn,
@@ -225,11 +232,11 @@ const ProvisionIdentityForm = (props) => {
}));
dispatch(setNavigationPath(PROVISIONING_CONFIRM));
}
- }
+ };
const cancel = () => {
dispatch(setNavigationPath(SELECT_LOGIN_ID));
- }
+ };
return (