From abcd55752da73fe8cd3d3f9d18cad23dee973fb6 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 10 Feb 2022 19:34:23 -0600 Subject: [PATCH] Support only external Ethereum auth for now --- app/soapbox/actions/auth.js | 30 ------------------------ app/soapbox/actions/external_auth.js | 34 +++++++++++++++++++++++----- app/soapbox/reducers/instance.js | 4 ++-- app/soapbox/utils/ethereum.js | 26 +++++++++++++++++++++ app/soapbox/utils/quirks.js | 3 ++- 5 files changed, 58 insertions(+), 39 deletions(-) create mode 100644 app/soapbox/utils/ethereum.js diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js index 5da8d579e..5274a98cf 100644 --- a/app/soapbox/actions/auth.js +++ b/app/soapbox/actions/auth.js @@ -213,36 +213,6 @@ export function logIn(intl, username, password) { }; } -export function ethereumLogin(instance, baseURL) { - return (dispatch, getState) => { - instance = (instance || getState().get('instance')); - - const { ethereum } = window; - const { scopes } = getFeatures(instance); - const loginMessage = instance.get('login_message'); - - return ethereum.request({ method: 'eth_requestAccounts' }).then(walletAddresses => { - const [walletAddress] = walletAddresses; - - return ethereum.request({ method: 'personal_sign', params: [loginMessage, walletAddress] }).then(signature => { - const params = { - grant_type: 'ethereum', - wallet_address: walletAddress.toLowerCase(), - password: signature, - redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', - scope: scopes, - }; - - // Note: skips app creation - // TODO: add to quirks.js for Mitra - return dispatch(obtainOAuthToken(params, baseURL)) - .then(token => dispatch(authLoggedIn(token))) - .then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL))); - }); - }); - }; -} - export function logOut(intl) { return (dispatch, getState) => { const state = getState(); diff --git a/app/soapbox/actions/external_auth.js b/app/soapbox/actions/external_auth.js index 35af6699e..c6fc5d340 100644 --- a/app/soapbox/actions/external_auth.js +++ b/app/soapbox/actions/external_auth.js @@ -9,10 +9,11 @@ import { Map as ImmutableMap, fromJS } from 'immutable'; import { createApp } from 'soapbox/actions/apps'; -import { authLoggedIn, verifyCredentials, switchAccount, ethereumLogin } from 'soapbox/actions/auth'; +import { authLoggedIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth'; import { obtainOAuthToken } from 'soapbox/actions/oauth'; import { parseBaseURL } from 'soapbox/utils/auth'; import sourceCode from 'soapbox/utils/code'; +import { getWalletAndSign } from 'soapbox/utils/ethereum'; import { getFeatures } from 'soapbox/utils/features'; import { getQuirks } from 'soapbox/utils/quirks'; @@ -35,6 +36,9 @@ const fetchExternalInstance = baseURL => { function createExternalApp(instance, baseURL) { return (dispatch, getState) => { + // Mitra: skip creating the auth app + if (getQuirks(instance).noApps) return new Promise(f => f({})); + const { scopes } = getFeatures(instance); const params = { @@ -71,11 +75,29 @@ function externalAuthorize(instance, baseURL) { }; } -function externalEthereumLogin(instance, baseURL) { +export function externalEthereumLogin(instance, baseURL) { return (dispatch, getState) => { - return dispatch(ethereumLogin(instance, baseURL)) - .then(account => dispatch(switchAccount(account.id))) - .then(() => window.location.href = '/'); + const loginMessage = instance.get('login_message'); + + return getWalletAndSign(loginMessage).then(({ wallet, signature }) => { + return dispatch(createExternalApp(instance, baseURL)).then(app => { + const params = { + grant_type: 'ethereum', + wallet_address: wallet.toLowerCase(), + client_id: app.client_id, + client_secret: app.client_secret, + password: signature, + redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', + scope: getFeatures(instance).scopes, + }; + + return dispatch(obtainOAuthToken(params, baseURL)) + .then(token => dispatch(authLoggedIn(token))) + .then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL))) + .then(account => dispatch(switchAccount(account.id))) + .then(() => window.location.href = '/'); + }); + }); }; } @@ -87,7 +109,7 @@ export function externalLogin(host) { const features = getFeatures(instance); const quirks = getQuirks(instance); - if (features.ethereumLogin && quirks.ethereumLoginOnly) { + if (features.ethereumLogin && quirks.noOAuthForm) { return dispatch(externalEthereumLogin(instance, baseURL)); } else { return dispatch(externalAuthorize(instance, baseURL)); diff --git a/app/soapbox/reducers/instance.js b/app/soapbox/reducers/instance.js index 25a04562e..b0dd8594f 100644 --- a/app/soapbox/reducers/instance.js +++ b/app/soapbox/reducers/instance.js @@ -50,7 +50,7 @@ const initialState = ImmutableMap({ // Build Mastodon configuration from Pleroma instance const pleromaToMastodonConfig = instance => { - return { + return ImmutableMap({ statuses: ImmutableMap({ max_characters: instance.get('max_toot_chars'), }), @@ -60,7 +60,7 @@ const pleromaToMastodonConfig = instance => { min_expiration: instance.getIn(['poll_limits', 'min_expiration']), max_expiration: instance.getIn(['poll_limits', 'max_expiration']), }), - }; + }); }; // Use new value only if old value is undefined diff --git a/app/soapbox/utils/ethereum.js b/app/soapbox/utils/ethereum.js new file mode 100644 index 000000000..3bd8db7b1 --- /dev/null +++ b/app/soapbox/utils/ethereum.js @@ -0,0 +1,26 @@ +export const ethereum = () => window.ethereum; + +export const hasEthereum = () => Boolean(ethereum()); + +// Requests an Ethereum wallet from the browser +// Returns a Promise containing the Ethereum wallet address (string). +export const getWallet = () => { + return ethereum().request({ method: 'eth_requestAccounts' }) + .then(wallets => wallets[0]); +}; + +// Asks the browser to sign a message with Ethereum. +// Returns a Promise containing the signature (string). +export const signMessage = (wallet, message) => { + return ethereum().request({ method: 'personal_sign', params: [message, wallet] }); +}; + +// Combines the above functions. +// Returns an object with the `wallet` and `signature` +export const getWalletAndSign = message => { + return getWallet().then(wallet => { + return signMessage(wallet, message).then(signature => { + return { wallet, signature }; + }); + }); +}; diff --git a/app/soapbox/utils/quirks.js b/app/soapbox/utils/quirks.js index 864f1effd..c64631078 100644 --- a/app/soapbox/utils/quirks.js +++ b/app/soapbox/utils/quirks.js @@ -8,7 +8,8 @@ export const getQuirks = createSelector([ ], (v) => { return { invertedPagination: v.software === PLEROMA, - ethereumLoginOnly: v.software === MITRA, + noApps: v.software === MITRA, + noOAuthForm: v.software === MITRA, }; });