Skip to content

Commit

Permalink
Merge pull request #1753 from Agoric/mfig/agoric-wallet-button
Browse files Browse the repository at this point in the history
feat: provide a button to activate the wallet from the bridge
  • Loading branch information
michaelfig authored Sep 15, 2020
2 parents 0efa57f + 8b43307 commit 1c8aa00
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 18 deletions.
1 change: 1 addition & 0 deletions packages/agoric-cli/lib/open.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default async function walletMain(progname, rawArgs, powers, opts) {
case 'no':
case 'false':
case false:
case undefined:
suffix = '/wallet';
break;
case 'only':
Expand Down
4 changes: 4 additions & 0 deletions packages/cosmic-swingset/lib/ag-solo/html/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ let inpBackground;
const accessTokenParams = `?${window.location.hash.slice(1)}`;
const accessTokenHash = window.location.hash;
window.location.hash = '';
window.addEventListener('hashchange', _ev => {
// Keep it clear.
window.location.hash = '';
});
const hasAccessToken = new URLSearchParams(accessTokenParams).has(
'accessToken',
);
Expand Down
65 changes: 63 additions & 2 deletions packages/cosmic-swingset/lib/ag-solo/html/wallet-bridge.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,57 @@
<title>Agoric Wallet Bridge</title>
</head>
<body>
<p>This bridge is only for dApps to reach your wallet. It contains no user-servicable parts.</p>
<style>
html, body {
height: 100%;
padding: 0;
margin: 0;
}

* {
box-sizing: border-box;
padding: 0;
margin: 0;
}

#launchWallet {
height: 100%;
width: 100%;
background-color: white;
}

.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
background-color: white;
}
50% {
background-color: #ab2328;
}
100% {
background-color: white;
}
}
</style>

<button id="launchWallet">Agoric Wallet</button>
<script type="text/javascript">
launchWallet.addEventListener('click', ev => {
launchWallet.classList.remove('pulse');
alert(`\
Use the:
agoric open
command-line request to open the Agoric wallet.`);
});

const popupWallet = () => {
launchWallet.classList.add('pulse');
};

const walletPublicURL = new URL('/private/wallet-bridge', window.origin.replace(/^http/, 'ws')).href;
const ws = new WebSocket(walletPublicURL);
const wsQueue = [];
Expand All @@ -22,6 +70,10 @@
if (!obj || typeof obj.type !== 'string') {
return;
}
if (obj.type === 'walletNeedDappApproval') {
// Let them approve.
popupWallet();
}
if (origin === undefined) {
dappQueue.push(obj);
return;
Expand All @@ -37,7 +89,12 @@
}
while (wsQueue.length) {
// The queued messages are raw objects, so JSONify.
ws.send(JSON.stringify(wsQueue.shift()));
const obj = wsQueue.shift();
ws.send(JSON.stringify(obj));
if (obj.type === 'walletAddOffer') {
// Just pop up our corresponding UI.
popupWallet();
}
}
});

Expand Down Expand Up @@ -74,6 +131,10 @@
} else {
// console.log('sending', obj);
ws.send(JSON.stringify(obj));
if (obj.type === 'walletAddOffer') {
// Just pop up our corresponding UI.
popupWallet();
}
}
});
</script>
Expand Down
45 changes: 32 additions & 13 deletions packages/cosmic-swingset/lib/ag-solo/web.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ const send = (ws, msg) => {
}
};

const verifyToken = (actual, expected) => {
// TODO: This should be a constant-time operation so that
// the caller cannot tell the difference between initial characters
// that match vs. ones that don't.
return actual === expected;
};

export async function makeHTTPListener(basedir, port, host, rawInboundCommand) {
// Enrich the inbound command with some metadata.
const inboundCommand = (
Expand Down Expand Up @@ -69,6 +76,15 @@ export async function makeHTTPListener(basedir, port, host, rawInboundCommand) {
log(`Serving static files from ${htmldir}`);
app.use(express.static(htmldir));

// The rules for validation:
//
// path outside /private: always accept
//
// all paths within /private: origin-based access control: reject anything except
// chrome-extension:, moz-extension:, and http:/https: localhost/127.0.0.1
//
// path in /private but not /private/wallet-bridge: also require correct
// accessToken= in query params
const validateOriginAndAccessToken = async req => {
const { origin } = req.headers;
const id = `${req.socket.remoteAddress}:${req.socket.remotePort}:`;
Expand All @@ -78,20 +94,23 @@ export async function makeHTTPListener(basedir, port, host, rawInboundCommand) {
return true;
}

// Validate the private accessToken.
const accessToken = await getAccessToken(port);
const reqToken = new URL(`http://localhost${req.url}`).searchParams.get(
'accessToken',
);

if (reqToken !== accessToken) {
log.error(
id,
`Invalid access token ${JSON.stringify(
reqToken,
)}; try running "agoric open"`,
// Bypass accessToken just for the wallet bridge.
if (req.url !== '/private/wallet-bridge') {
// Validate the private accessToken.
const accessToken = await getAccessToken(port);
const reqToken = new URL(`http://localhost${req.url}`).searchParams.get(
'accessToken',
);
return false;

if (!verifyToken(reqToken, accessToken)) {
log.error(
id,
`Invalid access token ${JSON.stringify(
reqToken,
)}; try running "agoric open"`,
);
return false;
}
}

if (!origin) {
Expand Down
10 changes: 9 additions & 1 deletion packages/dapp-svelte-wallet/api/src/lib-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,11 @@ export async function makeWallet({
dappsUpdater.updateState([...dappOrigins.values()]);
}

async function waitForDappApproval(suggestedPetname, origin) {
async function waitForDappApproval(
suggestedPetname,
origin,
notYetEnabled = () => {},
) {
let dappRecord;
if (dappOrigins.has(origin)) {
dappRecord = dappOrigins.get(origin);
Expand All @@ -674,6 +678,7 @@ export async function makeWallet({
petname: suggestedPetname,
origin,
approvalP,
enable: false,
actions: {
setPetname(petname) {
if (dappRecord.petname === petname) {
Expand Down Expand Up @@ -730,6 +735,9 @@ export async function makeWallet({
dappRecord.actions.disable();
}

if (!dappRecord.enable) {
notYetEnabled();
}
await dappRecord.approvalP;
// AWAIT
// Refetch the origin record.
Expand Down
5 changes: 4 additions & 1 deletion packages/dapp-svelte-wallet/api/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@
* @property {Promise<void>=} approvalP
* @property {Petname} suggestedPetname
* @property {Petname} petname
* @property {boolean} enable
* @property {string} origin
* @property {DappActions} actions
*
*/

/**
* @typedef {Object} DappActions
* @property {(petname: Petname) => DappActions} setPetname
* @property {() => DappActions} enable
Expand Down
20 changes: 19 additions & 1 deletion packages/dapp-svelte-wallet/api/src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,25 @@ export function buildRootObject(_vatPowers) {
dappOrigin = meta.origin,
suggestedDappPetname = obj.dappOrigin || meta.origin,
} = obj;
await wallet.waitForDappApproval(suggestedDappPetname, dappOrigin);

// When we haven't been enabled, tell our caller.
const notYetEnabled = () =>
E(http).send(
{
type: 'walletNeedDappApproval',
data: {
dappOrigin,
suggestedDappPetname,
},
},
[meta.channelHandle],
);

await wallet.waitForDappApproval(
suggestedDappPetname,
dappOrigin,
notYetEnabled,
);

switch (type) {
case 'walletGetPurses':
Expand Down
4 changes: 4 additions & 0 deletions packages/dapp-svelte-wallet/ui/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { makeCapTPConnection } from './captp';
const accessTokenParams = `?${window.location.hash.slice(1)}`;
// Now that we've captured it, clear out the access token from the URL bar.
window.location.hash = '';
window.addEventListener('hashchange', _ev => {
// Keep it clear.
window.location.hash = '';
});
const hasAccessToken = new URLSearchParams(accessTokenParams).has(
'accessToken',
);
Expand Down

0 comments on commit 1c8aa00

Please sign in to comment.