Mitra: support Ethereum login

This commit is contained in:
Alex Gleason 2022-02-09 19:47:13 -06:00
parent 872d5135b8
commit 012a7f8d89
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
4 changed files with 61 additions and 15 deletions

View File

@ -213,6 +213,34 @@ export function logIn(intl, username, password) {
}; };
} }
export function ethereumLogin() {
return (dispatch, getState) => {
const { ethereum } = window;
const loginMessage = getState().getIn(['instance', '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: getScopes(getState()),
};
// Note: skips app creation
// TODO: add to quirks.js for Mitra
return dispatch(obtainOAuthToken(params)).then(token => {
dispatch(authLoggedIn(token));
return dispatch(verifyCredentials(token.access_token));
});
});
});
};
}
export function logOut(intl) { export function logOut(intl) {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();

View File

@ -15,11 +15,10 @@ const messages = defineMessages({
const mapStateToProps = state => { const mapStateToProps = state => {
const instance = state.get('instance'); const instance = state.get('instance');
const features = getFeatures(instance);
return { return {
baseURL: getBaseURL(state), baseURL: getBaseURL(state),
hasResetPasswordAPI: features.resetPasswordAPI, features: getFeatures(instance),
}; };
}; };
@ -28,7 +27,7 @@ export default @connect(mapStateToProps)
class LoginForm extends ImmutablePureComponent { class LoginForm extends ImmutablePureComponent {
render() { render() {
const { intl, isLoading, handleSubmit, baseURL, hasResetPasswordAPI } = this.props; const { intl, isLoading, handleSubmit, baseURL, features } = this.props;
return ( return (
<form className='simple_form new_user' method='post' onSubmit={handleSubmit}> <form className='simple_form new_user' method='post' onSubmit={handleSubmit}>
@ -57,12 +56,8 @@ class LoginForm extends ImmutablePureComponent {
autoCapitalize='off' autoCapitalize='off'
required required
/> />
{/* <div className='input password user_password'>
<input
/>
</div> */}
<p className='hint subtle-hint'> <p className='hint subtle-hint'>
{hasResetPasswordAPI ? ( {features.resetPasswordAPI ? (
<Link to='/auth/reset_password'> <Link to='/auth/reset_password'>
<FormattedMessage id='login.reset_password_hint' defaultMessage='Trouble logging in?' /> <FormattedMessage id='login.reset_password_hint' defaultMessage='Trouble logging in?' />
</Link> </Link>

View File

@ -4,18 +4,25 @@ import { injectIntl } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import { ethereumLogin } from 'soapbox/actions/auth';
import { logIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth'; import { logIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth';
import { fetchInstance } from 'soapbox/actions/instance'; import { fetchInstance } from 'soapbox/actions/instance';
import { getFeatures } from 'soapbox/utils/features';
import { isStandalone } from 'soapbox/utils/state'; import { isStandalone } from 'soapbox/utils/state';
import LoginForm from './login_form'; import LoginForm from './login_form';
import OtpAuthForm from './otp_auth_form'; import OtpAuthForm from './otp_auth_form';
const mapStateToProps = state => ({ const mapStateToProps = state => {
me: state.get('me'), const instance = state.get('instance');
isLoading: false,
standalone: isStandalone(state), return {
}); me: state.get('me'),
isLoading: false,
standalone: isStandalone(state),
features: getFeatures(instance),
};
};
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@injectIntl @injectIntl
@ -62,8 +69,18 @@ class LoginPage extends ImmutablePureComponent {
event.preventDefault(); event.preventDefault();
} }
handleEthereumLogin = e => {
const { dispatch } = this.props;
dispatch(ethereumLogin())
.then(() => this.setState({ shouldRedirect: true }))
.catch(console.error);
e.preventDefault();
};
render() { render() {
const { standalone } = this.props; const { standalone, features } = this.props;
const { isLoading, mfa_auth_needed, mfa_token, shouldRedirect } = this.state; const { isLoading, mfa_auth_needed, mfa_token, shouldRedirect } = this.state;
if (standalone) return <Redirect to='/auth/external' />; if (standalone) return <Redirect to='/auth/external' />;
@ -72,7 +89,11 @@ class LoginPage extends ImmutablePureComponent {
if (mfa_auth_needed) return <OtpAuthForm mfa_token={mfa_token} />; if (mfa_auth_needed) return <OtpAuthForm mfa_token={mfa_token} />;
return <LoginForm handleSubmit={this.handleSubmit} isLoading={isLoading} />; if (features.ethereumLogin) {
return <button onClick={this.handleEthereumLogin}>Log in with Ethereum</button>;
} else {
return <LoginForm handleSubmit={this.handleSubmit} isLoading={isLoading} />;
}
} }
} }

View File

@ -9,6 +9,7 @@ const any = arr => arr.some(Boolean);
// For uglification // For uglification
export const MASTODON = 'Mastodon'; export const MASTODON = 'Mastodon';
export const PLEROMA = 'Pleroma'; export const PLEROMA = 'Pleroma';
export const MITRA = 'Mitra';
export const getFeatures = createSelector([ export const getFeatures = createSelector([
instance => parseVersion(instance.get('version')), instance => parseVersion(instance.get('version')),
@ -83,6 +84,7 @@ export const getFeatures = createSelector([
accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'), accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'),
quotePosts: v.software === PLEROMA && gte(v.version, '2.4.50'), quotePosts: v.software === PLEROMA && gte(v.version, '2.4.50'),
birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'), birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'),
ethereumLogin: v.software === MITRA,
}; };
}); });