diff --git a/src/components/auth/OAuthInit.jsx b/src/components/auth/OAuthInit.jsx index 48e9e07..858e327 100644 --- a/src/components/auth/OAuthInit.jsx +++ b/src/components/auth/OAuthInit.jsx @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { oAuthValidate } from 'components/auth/actions'; +import { oAuthValidate, oAuthComplete } from 'components/auth/actions'; class OAuthInit extends Component { static displayName = 'OAuthInit'; @@ -27,7 +27,7 @@ class OAuthInit extends Component { responseType: query.response_type, scope: query.scope, state: query.state - }); + }).then(this.props.complete); } render() { @@ -38,5 +38,6 @@ class OAuthInit extends Component { export default connect((state) => ({ query: state.routing.location.query }), { - validate: oAuthValidate + validate: oAuthValidate, + complete: oAuthComplete })(OAuthInit); diff --git a/src/components/auth/PanelTransition.jsx b/src/components/auth/PanelTransition.jsx index 6ac854b..34f1749 100644 --- a/src/components/auth/PanelTransition.jsx +++ b/src/components/auth/PanelTransition.jsx @@ -345,5 +345,6 @@ export default connect((state) => ({ register: actions.register, activate: actions.activate, clearErrors: actions.clearErrors, + oAuthComplete: actions.oAuthComplete, setError: actions.setError })(PanelTransition); diff --git a/src/components/auth/Permissions.jsx b/src/components/auth/Permissions.jsx index 102b782..7cc8f11 100644 --- a/src/components/auth/Permissions.jsx +++ b/src/components/auth/Permissions.jsx @@ -15,6 +15,7 @@ class Body extends BaseAuthBody { static propTypes = { ...BaseAuthBody.propTypes, login: PropTypes.func.isRequired, + oAuthComplete: PropTypes.func.isRequired, auth: PropTypes.shape({ error: PropTypes.string, login: PropTypes.shape({ @@ -24,6 +25,8 @@ class Body extends BaseAuthBody { }; render() { + const {user} = this.props; + return (
{this.renderErrors()} @@ -31,14 +34,16 @@ class Body extends BaseAuthBody {
- {/**/} - + {user.avatar + ? + : + }
- {'erickskrauch@yandex.ru'} + {user.email}
@@ -59,7 +64,9 @@ class Body extends BaseAuthBody { } onFormSubmit() { - // TODO + this.props.oAuthComplete({ + accept: true + }); } } diff --git a/src/components/auth/actions.js b/src/components/auth/actions.js index 6ed4d3a..c07ab9e 100644 --- a/src/components/auth/actions.js +++ b/src/components/auth/actions.js @@ -138,38 +138,78 @@ export function logout() { // TODO: move to oAuth actions? // test request: /oauth?client_id=ely&redirect_uri=http%3A%2F%2Fely.by&response_type=code&scope=minecraft_server_session -export function oAuthValidate({clientId, redirectUrl, responseType, scope, state}) { +export function oAuthValidate(oauth) { return (dispatch) => request.get( '/api/oauth/validate', - { - client_id: clientId, - redirect_uri: redirectUrl, - response_type: responseType, - scope, - state - } + getOAuthRequest(oauth) ) .then((resp) => { dispatch(setClient(resp.client)); - dispatch(routeActions.push('/oauth/permissions')); + dispatch(setOAuthRequest(resp.oAuth)); }) .catch((resp = {}) => { // TODO - if (resp.statusCode === 400 && resp.error === 'invalid_request') { - alert(`Invalid request (${resp.parameter} required).`); - } - if (resp.statusCode === 401 && resp.error === 'invalid_client') { - alert('Can not find application you are trying to authorize.'); - } - if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') { - alert(`Invalid response type '${resp.parameter}'.`); - } - if (resp.statusCode === 400 && resp.error === 'invalid_scope') { - alert(`Invalid scope '${resp.parameter}'.`); + handleOauthParamsValidation(resp); + if (resp.statusCode === 401 && resp.error === 'accept_required') { + alert('Accept required.'); } }); } +export function oAuthComplete(params = {}) { + return (dispatch, getState) => { + const oauth = getState().auth.oauth; + const query = request.buildQuery(getOAuthRequest(oauth)); + + return request.post( + `/api/oauth/complete?${query}`, + typeof params.accept === 'undefined' ? {} : {accept: params.accept} + ) + .then((resp) => { + 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; + } + }); + }; +} + +function getOAuthRequest(oauth) { + return { + client_id: oauth.clientId, + redirect_uri: oauth.redirectUrl, + response_type: oauth.responseType, + scope: oauth.scope, + state: oauth.state + }; +} + +function handleOauthParamsValidation(resp = {}) { + if (resp.statusCode === 400 && resp.error === 'invalid_request') { + alert(`Invalid request (${resp.parameter} required).`); + } + if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') { + alert(`Invalid response type '${resp.parameter}'.`); + } + if (resp.statusCode === 400 && resp.error === 'invalid_scope') { + alert(`Invalid scope '${resp.parameter}'.`); + } + if (resp.statusCode === 401 && resp.error === 'invalid_client') { + alert('Can not find application you are trying to authorize.'); + } +} + export const SET_CLIENT = 'set_client'; export function setClient({id, name, description}) { return { @@ -177,3 +217,17 @@ export function setClient({id, name, description}) { payload: {id, name, description} }; } + +export const SET_OAUTH = 'set_oauth'; +export function setOAuthRequest(oauth) { + return { + type: SET_OAUTH, + payload: { + clientId: oauth.client_id, + redirectUrl: oauth.redirect_uri, + responseType: oauth.response_type, + scope: oauth.scope, + state: oauth.state + } + }; +} diff --git a/src/components/auth/reducer.js b/src/components/auth/reducer.js index da79a42..55a6a42 100644 --- a/src/components/auth/reducer.js +++ b/src/components/auth/reducer.js @@ -1,10 +1,11 @@ import { combineReducers } from 'redux'; -import { ERROR, SET_CLIENT } from './actions'; +import { ERROR, SET_CLIENT, SET_OAUTH } from './actions'; export default combineReducers({ error, - client + client, + oauth }); function error( @@ -39,3 +40,22 @@ function client( return state; } } + +function oauth( + state = null, + {type, payload = {}} +) { + switch (type) { + case SET_OAUTH: + return { + clientId: payload.clientId, + redirectUrl: payload.redirectUrl, + responseType: payload.responseType, + scope: payload.scope, + state: payload.state + }; + + default: + return state; + } +} diff --git a/src/routes.js b/src/routes.js index 69db693..27d9888 100644 --- a/src/routes.js +++ b/src/routes.js @@ -37,6 +37,8 @@ export default function routesFactory(store) { } } + // TODO: validate that we have all required data on premissions page + if (forcePath && pathname !== forcePath) { switch (pathname) { case '/': diff --git a/src/services/request.js b/src/services/request.js index af3255c..511c023 100644 --- a/src/services/request.js +++ b/src/services/request.js @@ -1,4 +1,4 @@ -function serialize(data) { +function buildQuery(data) { return Object.keys(data) .map( (keyName) => @@ -33,7 +33,7 @@ export default { ...getDefaultHeaders(), 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: serialize(data) + body: buildQuery(data) }) .then(toJSON) .then(handleResponse) @@ -43,7 +43,7 @@ export default { get(url, data) { if (typeof data === 'object') { const separator = url.indexOf('?') === -1 ? '?' : '&'; - url += separator + serialize(data); + url += separator + buildQuery(data); } return fetch(url, { @@ -54,6 +54,8 @@ export default { ; }, + buildQuery, + setAuthToken(tkn) { authToken = tkn; }