Mitra: support Ethereum login
This commit is contained in:
parent
872d5135b8
commit
012a7f8d89
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue