mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
Auth flow. The next
This commit is contained in:
@@ -13,7 +13,6 @@ import messages from './Activation.messages';
|
||||
class Body extends BaseAuthBody {
|
||||
static propTypes = {
|
||||
...BaseAuthBody.propTypes,
|
||||
activate: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string,
|
||||
login: PropTypes.shape({
|
||||
@@ -48,10 +47,6 @@ class Body extends BaseAuthBody {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.activate(this.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
export default function Activation() {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class AppInfo extends Component {
|
||||
return (
|
||||
<div className={styles.appInfo}>
|
||||
<div className={styles.logoContainer}>
|
||||
<h2 className={styles.logo}>{name}</h2>
|
||||
<h2 className={styles.logo}>{name || 'Ely Accounts'}</h2>
|
||||
</div>
|
||||
<div className={styles.descriptionContainer}>
|
||||
<p className={styles.description}>
|
||||
|
||||
@@ -8,6 +8,8 @@ import AuthError from './AuthError';
|
||||
export default class BaseAuthBody extends Component {
|
||||
static propTypes = {
|
||||
clearErrors: PropTypes.func.isRequired,
|
||||
resolve: PropTypes.func.isRequired,
|
||||
reject: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string
|
||||
})
|
||||
@@ -20,6 +22,10 @@ export default class BaseAuthBody extends Component {
|
||||
;
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.resolve(this.serialize());
|
||||
}
|
||||
|
||||
onClearErrors = this.props.clearErrors;
|
||||
|
||||
form = {};
|
||||
|
||||
@@ -14,7 +14,6 @@ import passwordMessages from './Password.messages';
|
||||
class Body extends BaseAuthBody {
|
||||
static propTypes = {
|
||||
...BaseAuthBody.propTypes,
|
||||
login: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string,
|
||||
login: PropTypes.shape({
|
||||
@@ -37,10 +36,6 @@ class Body extends BaseAuthBody {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.login(this.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { logout } from 'components/auth/actions';
|
||||
|
||||
class Logout extends Component {
|
||||
export class Logout extends Component {
|
||||
static displayName = 'Logout';
|
||||
|
||||
static propTypes = {
|
||||
logout: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.props.logout();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span />;
|
||||
return <span/>;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(null, {
|
||||
logout
|
||||
})(Logout);
|
||||
|
||||
@@ -1,43 +1,9 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { oAuthValidate, oAuthComplete } from 'components/auth/actions';
|
||||
|
||||
class OAuthInit extends Component {
|
||||
export default class OAuthInit extends Component {
|
||||
static displayName = 'OAuthInit';
|
||||
|
||||
static propTypes = {
|
||||
query: PropTypes.shape({
|
||||
client_id: PropTypes.string.isRequired,
|
||||
redirect_uri: PropTypes.string.isRequired,
|
||||
response_type: PropTypes.string.isRequired,
|
||||
scope: PropTypes.string.isRequired,
|
||||
state: PropTypes.string
|
||||
}),
|
||||
validate: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const {query} = this.props;
|
||||
|
||||
this.props.validate({
|
||||
clientId: query.client_id,
|
||||
redirectUrl: query.redirect_uri,
|
||||
responseType: query.response_type,
|
||||
scope: query.scope,
|
||||
state: query.state
|
||||
}).then(this.props.complete);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect((state) => ({
|
||||
query: state.routing.location.query
|
||||
}), {
|
||||
validate: oAuthValidate,
|
||||
complete: oAuthComplete
|
||||
})(OAuthInit);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { routeActions } from 'react-router-redux';
|
||||
import { TransitionMotion, spring } from 'react-motion';
|
||||
import ReactHeight from 'react-height';
|
||||
|
||||
@@ -10,6 +9,7 @@ import { Form } from 'components/ui/Form';
|
||||
import {helpLinks as helpLinksStyles} from 'components/auth/helpLinks.scss';
|
||||
import panelStyles from 'components/ui/panel.scss';
|
||||
import icons from 'components/ui/icons.scss';
|
||||
import authFlow from 'services/authFlow';
|
||||
|
||||
import * as actions from './actions';
|
||||
|
||||
@@ -28,7 +28,6 @@ class PanelTransition extends Component {
|
||||
password: PropTypes.string
|
||||
})
|
||||
}).isRequired,
|
||||
goBack: React.PropTypes.func.isRequired,
|
||||
setError: React.PropTypes.func.isRequired,
|
||||
clearErrors: React.PropTypes.func.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
@@ -211,8 +210,7 @@ class PanelTransition extends Component {
|
||||
onGoBack = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
this.body.onGoBack && this.body.onGoBack();
|
||||
this.props.goBack();
|
||||
authFlow.goBack();
|
||||
};
|
||||
|
||||
getHeader(key, props) {
|
||||
@@ -341,14 +339,10 @@ class PanelTransition extends Component {
|
||||
export default connect((state) => ({
|
||||
user: state.user,
|
||||
auth: state.auth,
|
||||
path: state.routing.location.pathname
|
||||
path: state.routing.location.pathname,
|
||||
resolve: authFlow.resolve.bind(authFlow),
|
||||
reject: authFlow.reject.bind(authFlow)
|
||||
}), {
|
||||
goBack: routeActions.goBack,
|
||||
login: actions.login,
|
||||
logout: actions.logout,
|
||||
register: actions.register,
|
||||
activate: actions.activate,
|
||||
clearErrors: actions.clearErrors,
|
||||
oAuthComplete: actions.oAuthComplete,
|
||||
setError: actions.setError
|
||||
})(PanelTransition);
|
||||
|
||||
@@ -15,8 +15,6 @@ import messages from './Password.messages';
|
||||
class Body extends BaseAuthBody {
|
||||
static propTypes = {
|
||||
...BaseAuthBody.propTypes,
|
||||
login: PropTypes.func.isRequired,
|
||||
logout: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string,
|
||||
login: PropTypes.shape({
|
||||
@@ -56,17 +54,6 @@ class Body extends BaseAuthBody {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.login({
|
||||
...this.serialize(),
|
||||
login: this.props.user.email || this.props.user.username
|
||||
});
|
||||
}
|
||||
|
||||
onGoBack() {
|
||||
this.props.logout();
|
||||
}
|
||||
}
|
||||
|
||||
export default function Password() {
|
||||
|
||||
@@ -15,15 +15,7 @@ import styles from './passwordChange.scss';
|
||||
|
||||
class Body extends BaseAuthBody {
|
||||
static propTypes = {
|
||||
...BaseAuthBody.propTypes/*,
|
||||
// Я так полагаю, это правила валидации?
|
||||
login: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string,
|
||||
login: PropTypes.shape({
|
||||
login: PropTypes.stirng
|
||||
})
|
||||
})*/
|
||||
...BaseAuthBody.propTypes
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -56,10 +48,6 @@ class Body extends BaseAuthBody {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.login(this.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
export default function PasswordChange() {
|
||||
@@ -75,10 +63,14 @@ export default function PasswordChange() {
|
||||
<Message {...passwordChangedMessages.change} />
|
||||
</button>
|
||||
),
|
||||
Links: () => (
|
||||
<Link to="/oauth/permissions">
|
||||
Links: (props) => (
|
||||
<a href="#" onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
props.reject();
|
||||
}}>
|
||||
<Message {...passwordChangedMessages.skipThisStep} />
|
||||
</Link>
|
||||
</a>
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import messages from './Permissions.messages';
|
||||
class Body extends BaseAuthBody {
|
||||
static propTypes = {
|
||||
...BaseAuthBody.propTypes,
|
||||
login: PropTypes.func.isRequired,
|
||||
oAuthComplete: PropTypes.func.isRequired,
|
||||
auth: PropTypes.shape({
|
||||
error: PropTypes.string,
|
||||
scopes: PropTypes.array.isRequired
|
||||
@@ -52,20 +50,14 @@ class Body extends BaseAuthBody {
|
||||
<Message {...messages.theAppNeedsAccess2} />
|
||||
</div>
|
||||
<ul className={styles.permissionsList}>
|
||||
{scopes.map((scope) => (
|
||||
<li>{<Message {...messages[`scope_${scope}`]} />}</li>
|
||||
{scopes.map((scope, key) => (
|
||||
<li key={key}>{<Message {...messages[`scope_${scope}`]} />}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.oAuthComplete({
|
||||
accept: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default function Permissions() {
|
||||
@@ -85,9 +77,7 @@ export default function Permissions() {
|
||||
<a href="#" onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
props.onAuthComplete({
|
||||
accept: false
|
||||
});
|
||||
props.reject();
|
||||
}}>
|
||||
<Message {...messages.decline} />
|
||||
</a>
|
||||
|
||||
@@ -82,10 +82,6 @@ class Body extends BaseAuthBody {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFormSubmit() {
|
||||
this.props.register(this.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
export default function Register() {
|
||||
|
||||
@@ -19,30 +19,26 @@ export function login({login = '', password = '', rememberMe = false}) {
|
||||
token: resp.jwt
|
||||
}));
|
||||
|
||||
dispatch(authenticate(resp.jwt));
|
||||
|
||||
dispatch(redirectToGoal());
|
||||
return dispatch(authenticate(resp.jwt));
|
||||
})
|
||||
.catch((resp) => {
|
||||
if (resp.errors.login === ACTIVATION_REQUIRED) {
|
||||
dispatch(updateUser({
|
||||
return dispatch(updateUser({
|
||||
isActive: false,
|
||||
isGuest: false
|
||||
}));
|
||||
|
||||
dispatch(redirectToGoal());
|
||||
} else if (resp.errors.password === PASSWORD_REQUIRED) {
|
||||
dispatch(updateUser({
|
||||
return dispatch(updateUser({
|
||||
username: login,
|
||||
email: login
|
||||
}));
|
||||
dispatch(routeActions.push('/password'));
|
||||
} else {
|
||||
if (resp.errors.login === LOGIN_REQUIRED && password) {
|
||||
dispatch(logout());
|
||||
}
|
||||
const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
|
||||
dispatch(setError(errorMessage));
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// TODO: log unexpected errors
|
||||
@@ -73,6 +69,7 @@ export function register({
|
||||
.catch((resp) => {
|
||||
const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
|
||||
dispatch(setError(errorMessage));
|
||||
throw new Error(errorMessage);
|
||||
|
||||
// TODO: log unexpected errors
|
||||
})
|
||||
@@ -87,43 +84,22 @@ export function activate({key = ''}) {
|
||||
)
|
||||
.then((resp) => {
|
||||
dispatch(updateUser({
|
||||
isGuest: false,
|
||||
isActive: true
|
||||
}));
|
||||
|
||||
dispatch(authenticate(resp.jwt));
|
||||
|
||||
dispatch(redirectToGoal());
|
||||
return dispatch(authenticate(resp.jwt));
|
||||
})
|
||||
.catch((resp) => {
|
||||
const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
|
||||
dispatch(setError(errorMessage));
|
||||
throw new Error(errorMessage);
|
||||
|
||||
// TODO: log unexpected errors
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
function redirectToGoal() {
|
||||
return (dispatch, getState) => {
|
||||
const {user} = getState();
|
||||
|
||||
switch (user.goal) {
|
||||
case 'oauth':
|
||||
dispatch(routeActions.push('/oauth/permissions'));
|
||||
break;
|
||||
|
||||
case 'account':
|
||||
default:
|
||||
dispatch(routeActions.push('/'));
|
||||
break;
|
||||
}
|
||||
|
||||
// dispatch(updateUser({ // TODO: mb create action resetGoal?
|
||||
// goal: null
|
||||
// }));
|
||||
};
|
||||
}
|
||||
|
||||
export const ERROR = 'error';
|
||||
export function setError(error) {
|
||||
return {
|
||||
@@ -138,10 +114,7 @@ export function clearErrors() {
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return (dispatch) => {
|
||||
dispatch(logoutUser());
|
||||
dispatch(routeActions.push('/login'));
|
||||
};
|
||||
return logoutUser();
|
||||
}
|
||||
|
||||
// TODO: move to oAuth actions?
|
||||
@@ -174,28 +147,26 @@ export function oAuthComplete(params = {}) {
|
||||
`/api/oauth/complete?${query}`,
|
||||
typeof params.accept === 'undefined' ? {} : {accept: params.accept}
|
||||
)
|
||||
.then((resp) => {
|
||||
if (resp.status === 401 && resp.name === 'Unauthorized') {
|
||||
// TODO: temporary solution for oauth init by guest
|
||||
// TODO: request serivce should handle http status codes
|
||||
dispatch(routeActions.push('/oauth/permissions'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (resp.redirectUri) {
|
||||
location.href = resp.redirectUri;
|
||||
}
|
||||
})
|
||||
.catch((resp = {}) => { // TODO
|
||||
handleOauthParamsValidation(resp);
|
||||
|
||||
if (resp.statusCode === 401 && resp.error === 'accept_required') {
|
||||
dispatch(routeActions.push('/oauth/permissions'));
|
||||
}
|
||||
|
||||
if (resp.statusCode === 401 && resp.error === 'access_denied') {
|
||||
// user declined permissions
|
||||
location.href = resp.redirectUri;
|
||||
return {
|
||||
redirectUri: resp.redirectUri
|
||||
};
|
||||
}
|
||||
|
||||
handleOauthParamsValidation(resp);
|
||||
|
||||
if (resp.status === 401 && resp.name === 'Unauthorized') {
|
||||
const error = new Error('Unauthorized');
|
||||
error.unauthorized = true;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (resp.statusCode === 401 && resp.error === 'accept_required') {
|
||||
const error = new Error('Permissions accept required');
|
||||
error.acceptRequired = true;
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -212,17 +183,22 @@ function getOAuthRequest(oauth) {
|
||||
}
|
||||
|
||||
function handleOauthParamsValidation(resp = {}) {
|
||||
const error = new Error('Error completing request');
|
||||
if (resp.statusCode === 400 && resp.error === 'invalid_request') {
|
||||
alert(`Invalid request (${resp.parameter} required).`);
|
||||
throw error;
|
||||
}
|
||||
if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') {
|
||||
alert(`Invalid response type '${resp.parameter}'.`);
|
||||
throw error;
|
||||
}
|
||||
if (resp.statusCode === 400 && resp.error === 'invalid_scope') {
|
||||
alert(`Invalid scope '${resp.parameter}'.`);
|
||||
throw error;
|
||||
}
|
||||
if (resp.statusCode === 401 && resp.error === 'invalid_client') {
|
||||
alert('Can not find application you are trying to authorize.');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,6 @@ export function authenticate(token) {
|
||||
|
||||
return (dispatch) => {
|
||||
request.setAuthToken(token);
|
||||
dispatch(fetchUserData());
|
||||
return dispatch(fetchUserData());
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user