Skip to content

Commit

Permalink
Merge pull request #93 from aesirxio/modal
Browse files Browse the repository at this point in the history
Modal
  • Loading branch information
lunguyenhat authored Jul 25, 2023
2 parents a24923d + ebb0526 commit 5a42322
Show file tree
Hide file tree
Showing 18 changed files with 858 additions and 56 deletions.
26 changes: 19 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
"author": "AesirX",
"repository": "https://github.com/aesirxio/sso",
"main": "build/lib/index.js",
"module": "build/lib/index.js",
"exports": {
"require": "./build/lib/index.js",
"import": "./build/lib/index.js"
},
"types": "build/lib/types/index.d.ts",
"source": "src/index.js",
"dependencies": {
"@concordium/react-components": "^0.3.0",
"@wagmi/core": "^1.3.8",
"@web3modal/ethereum": "^2.7.0",
"@web3modal/react": "^2.7.0",
"axios": "^1.4.0",
"bootstrap": "^5.3.0",
"cross-env": "^7.0.3",
"dotenv": "^16.0.3",
"ethers": "^6.6.5",
"react": "^18.2.0",
"react-spinners": "^0.13.6"
"react-secure-storage": "^1.2.2",
"react-spinners": "^0.13.6",
"react-toastify": "^9.1.3",
"reactstrap": "^9.2.0",
"viem": "^1.0.0",
"wagmi": "^1.3.2"
},
"tsup": {
"entry": [
Expand All @@ -27,6 +34,7 @@
"clean": true
},
"scripts": {
"dev": "NODE_ENV=development tsup --watch --onSuccess 'yalc push --no-scripts'",
"build": "tsup",
"lint": "eslint --fix \"src/**/\"",
"lint:check": "eslint \"src/**/\"",
Expand Down Expand Up @@ -60,11 +68,15 @@
"@babel/plugin-transform-runtime": "^7.21.4",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@types/bootstrap": "^5.2.6",
"@types/node": "^20.1.4",
"@types/react": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7",
"@walletconnect/types": "^2.9.1",
"babel-plugin-module-resolver": "^5.0.0",
"esbuild-plugin-inline-image": "^0.0.9",
"esbuild-sass-plugin": "^2.10.0",
"eslint": "^8.35",
"eslint-plugin-react": "^7.31.10",
"prettier": "^2.8.7",
Expand All @@ -75,4 +87,4 @@
"build",
"src"
]
}
}
68 changes: 68 additions & 0 deletions src/Hooks/useWallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { toast } from 'react-toastify';
import axios from 'axios';
import { getClientApp } from '../utils';

const useWallet = (wallet: any, publicAddress: string) => {
const { endpoint } = getClientApp();

const getWalletNonce = async () => {
try {
const reqAuthFormData = {
publicAddress: publicAddress,
wallet: wallet,
};

const config = {
method: 'post',
url: `${endpoint}/index.php?webserviceClient=site&webserviceVersion=1.0.0&option=member&task=getWalletNonce&api=hal`,
headers: {
'Content-Type': 'application/json',
},
data: reqAuthFormData,
};
const { data } = await axios(config);

if (data.result) {
return data.result;
}
throw false;
} catch (error) {
toast('Wrong authentication', { closeOnClick: false });
return false;
}
};

const verifySignature = async (wallet: any, publicAddress: string, signature: any) => {
try {
const reqAuthFormData = {
wallet: wallet,
publicAddress: publicAddress,
signature: signature,
};

const config = {
method: 'post',
url: `${endpoint}/index.php?webserviceClient=site&webserviceVersion=1.0.0&option=member&task=walletLogin&api=hal`,
headers: {
'Content-Type': 'application/json',
},
data: reqAuthFormData,
};

const { data } = await axios(config);
if (data?.result) {
return data?.result;
} else {
throw false;
}
} catch (error) {
console.log(error);
toast('Wrong authentication', { closeOnClick: false });
return false;
}
};

return { getWalletNonce, verifySignature };
};

export default useWallet;
23 changes: 23 additions & 0 deletions src/SSOButton/Providers/Concordium/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
BrowserWalletConnector,
CONCORDIUM_WALLET_CONNECT_PROJECT_ID,
WalletConnectConnector,
ephemeralConnectorType,
} from '@concordium/react-components';

import { SignClientTypes } from '@walletconnect/types';

const WALLET_CONNECT_OPTS: SignClientTypes.Options = {
projectId: CONCORDIUM_WALLET_CONNECT_PROJECT_ID,
metadata: {
name: 'AesirX SSO',
description: 'AesirX SSO is our Open Source Single Sign On service.',
url: '#',
icons: ['https://walletconnect.com/walletconnect-logo.png'],
},
};

export const BROWSER_WALLET = ephemeralConnectorType(BrowserWalletConnector.create);
export const WALLET_CONNECT = ephemeralConnectorType(
WalletConnectConnector.create.bind(this, WALLET_CONNECT_OPTS)
);
48 changes: 48 additions & 0 deletions src/SSOButton/Providers/Concordium/connect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { BROWSER_WALLET, WALLET_CONNECT } from './config';
import React from 'react';

const ConnectConcordium = ({
isConnecting,
handleOnConnect,
activeConnectorError,
activeConnectorType,
activeConnector,
}: any) => {
return (
<>
<button
className="btn btn-secondary"
onClick={() => handleOnConnect(BROWSER_WALLET, 'browser')}
>
{isConnecting ? (
<>
<span
className="spinner-border spinner-border-sm me-1"
role="status"
aria-hidden="true"
></span>
Connecting
</>
) : (
<>Browser Wallet</>
)}
</button>
<button
className="btn btn-secondary ms-1"
onClick={() => handleOnConnect(WALLET_CONNECT, 'walletconnect')}
>
{!activeConnectorError && activeConnectorType && !activeConnector ? (
<span
className="spinner-border spinner-border-sm me-1"
role="status"
aria-hidden="true"
></span>
) : (
'QR Code'
)}
</button>
</>
);
};

export default ConnectConcordium;
125 changes: 125 additions & 0 deletions src/SSOButton/Providers/Concordium/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { useEffect, useState } from 'react';

import { toast } from 'react-toastify';
import {
TESTNET,
MAINNET,
WithWalletConnector,
useConnection,
useConnect,
WalletConnectionProps,
withJsonRpcClient,
ConnectorType,
} from '@concordium/react-components';
import ConnectConcordium from './connect';
import SignMessageConcordium from './sign';
import secureLocalStorage from 'react-secure-storage';

const SSOConcordiumProvider = () => {
return (
<WithWalletConnector network={process.env.NODE_ENV === 'development' ? TESTNET : MAINNET}>
{(props) => <ConcordiumApp {...props} />}
</WithWalletConnector>
);
};

const ConcordiumApp = (props: WalletConnectionProps) => {
const {
activeConnectorType,
activeConnector,
activeConnectorError,
network,
connectedAccounts,
genesisHashes,
setActiveConnectorType,
} = props;

const { connection, setConnection, account, genesisHash } = useConnection(
connectedAccounts,
genesisHashes
);

const { connect, connectError, isConnecting } = useConnect(activeConnector, setConnection);

const [rpcGenesisHash, setRpcGenesisHash] = useState();
const [rpcError, setRpcError] = useState('');

useEffect(() => {
if (connection) {
setRpcGenesisHash(undefined);
withJsonRpcClient(connection, async (rpc) => {
const status = await rpc.getConsensusStatus();
return status.genesisBlock;
})
.then((hash: any) => {
console.log('NODE_ENV', process.env.NODE_ENV);

if (process.env.NODE_ENV === 'development' && hash !== TESTNET.genesisHash) {
throw new Error(`Please change the network to Testnet in Wallet`);
}

if (process.env.NODE_ENV === 'production' && hash !== MAINNET.genesisHash) {
throw new Error(`Please change the network to Mainnet in Wallet`);
}
setRpcGenesisHash(hash);
setRpcError('');
})
.catch((err) => {
setRpcGenesisHash(undefined);
toast(err.message);
setRpcError(err.message);
});
}
}, [connection, genesisHash, network]);

useEffect(() => {
if (activeConnector) {
connect();
}
}, [activeConnector]);

useEffect(() => {
if (connectError) {
toast(connectError);
}
}, [connectError]);

const handleOnConnect = async (connectorType: ConnectorType, name: string) => {
secureLocalStorage.setItem('concordium', name);
await setActiveConnectorType(connectorType);
};
return (
<>
<div className="pb-2">Concordium wallets</div>
{activeConnectorError && <div>Connector error: {activeConnectorError}.</div>}

{!account || rpcError ? (
<ConnectConcordium
isConnecting={isConnecting}
handleOnConnect={handleOnConnect}
activeConnectorError={activeConnectorError}
activeConnectorType={activeConnectorType}
activeConnector={activeConnector}
/>
) : (
<>
{rpcGenesisHash ? (
<SignMessageConcordium account={account} connection={connection} />
) : (
<button className="btn btn-secondary">
<span
className="spinner-border spinner-border-sm me-1"
role="status"
aria-hidden="true"
></span>
</button>
)}
</>
)}

{rpcError && <div className="mt-1">RPC error: {rpcError}</div>}
</>
);
};

export default SSOConcordiumProvider;
65 changes: 65 additions & 0 deletions src/SSOButton/Providers/Concordium/sign.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useContext, useState } from 'react';
import useWallet from '../../../Hooks/useWallet';

import { toast } from 'react-toastify';
import { shortenString } from '../../../utils';
import { stringMessage } from '@concordium/react-components';
import { SSOModalContext } from '../../modal';

const SignMessageConcordium = ({ account, connection }: any) => {
const [status, setStatus] = useState('');
const wallet = 'concordium';
const { handleOnData } = useContext(SSOModalContext);

const { getWalletNonce, verifySignature } = useWallet(wallet, account);

const handleConnect = async () => {
setStatus('connect');
const nonce = await getWalletNonce();

if (nonce) {
try {
setStatus('sign');
const signature = await connection.signMessage(account, stringMessage(`${nonce}`));
const convertedSignature =
typeof signature === 'object' && signature !== null ? signature : JSON.parse(signature);

if (signature) {
const data = await verifySignature(wallet, account, convertedSignature);
handleOnData(data);
}
} catch (error) {
toast(error.message);
}
}
setStatus('');
};
return (
<>
<p className="text-break">
<span className="fw-semibold">Wallet:</span>
<span className="ms-1">{account && shortenString(account)}</span>
</p>
<button className="btn btn-secondary" onClick={handleConnect}>
{status ? (
<div className="d-flex align-items-center">
<span
className="spinner-border spinner-border-sm me-1"
role="status"
aria-hidden="true"
></span>
<span className="ms-1">
{status === 'sign'
? 'Please sign message on the wallet'
: `Please wait to connect...`}
</span>
</div>
) : (
<>Sign in via Concordium</>
)}
</button>
</>
);
};

export default SignMessageConcordium;
Loading

0 comments on commit 5a42322

Please sign in to comment.