oauth/complete

This commit is contained in:
SleepWalker 2016-02-27 12:53:58 +02:00
parent 31d94ae770
commit e44360a20b
7 changed files with 119 additions and 32 deletions

View File

@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { oAuthValidate } from 'components/auth/actions'; import { oAuthValidate, oAuthComplete } from 'components/auth/actions';
class OAuthInit extends Component { class OAuthInit extends Component {
static displayName = 'OAuthInit'; static displayName = 'OAuthInit';
@ -27,7 +27,7 @@ class OAuthInit extends Component {
responseType: query.response_type, responseType: query.response_type,
scope: query.scope, scope: query.scope,
state: query.state state: query.state
}); }).then(this.props.complete);
} }
render() { render() {
@ -38,5 +38,6 @@ class OAuthInit extends Component {
export default connect((state) => ({ export default connect((state) => ({
query: state.routing.location.query query: state.routing.location.query
}), { }), {
validate: oAuthValidate validate: oAuthValidate,
complete: oAuthComplete
})(OAuthInit); })(OAuthInit);

View File

@ -345,5 +345,6 @@ export default connect((state) => ({
register: actions.register, register: actions.register,
activate: actions.activate, activate: actions.activate,
clearErrors: actions.clearErrors, clearErrors: actions.clearErrors,
oAuthComplete: actions.oAuthComplete,
setError: actions.setError setError: actions.setError
})(PanelTransition); })(PanelTransition);

View File

@ -15,6 +15,7 @@ class Body extends BaseAuthBody {
static propTypes = { static propTypes = {
...BaseAuthBody.propTypes, ...BaseAuthBody.propTypes,
login: PropTypes.func.isRequired, login: PropTypes.func.isRequired,
oAuthComplete: PropTypes.func.isRequired,
auth: PropTypes.shape({ auth: PropTypes.shape({
error: PropTypes.string, error: PropTypes.string,
login: PropTypes.shape({ login: PropTypes.shape({
@ -24,6 +25,8 @@ class Body extends BaseAuthBody {
}; };
render() { render() {
const {user} = this.props;
return ( return (
<div> <div>
{this.renderErrors()} {this.renderErrors()}
@ -31,14 +34,16 @@ class Body extends BaseAuthBody {
<PanelBodyHeader> <PanelBodyHeader>
<div className={styles.authInfo}> <div className={styles.authInfo}>
<div className={styles.authInfoAvatar}> <div className={styles.authInfoAvatar}>
{/*<img src="//lorempixel.com/g/90/90" />*/} {user.avatar
<span className={icons.user} /> ? <img src={user.avatar} />
: <span className={icons.user} />
}
</div> </div>
<div className={styles.authInfoTitle}> <div className={styles.authInfoTitle}>
<Message {...messages.youAuthorizedAs} /> <Message {...messages.youAuthorizedAs} />
</div> </div>
<div className={styles.authInfoEmail}> <div className={styles.authInfoEmail}>
{'erickskrauch@yandex.ru'} {user.email}
</div> </div>
</div> </div>
</PanelBodyHeader> </PanelBodyHeader>
@ -59,7 +64,9 @@ class Body extends BaseAuthBody {
} }
onFormSubmit() { onFormSubmit() {
// TODO this.props.oAuthComplete({
accept: true
});
} }
} }

View File

@ -138,38 +138,78 @@ export function logout() {
// TODO: move to oAuth actions? // 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 // 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) => return (dispatch) =>
request.get( request.get(
'/api/oauth/validate', '/api/oauth/validate',
{ getOAuthRequest(oauth)
client_id: clientId,
redirect_uri: redirectUrl,
response_type: responseType,
scope,
state
}
) )
.then((resp) => { .then((resp) => {
dispatch(setClient(resp.client)); dispatch(setClient(resp.client));
dispatch(routeActions.push('/oauth/permissions')); dispatch(setOAuthRequest(resp.oAuth));
}) })
.catch((resp = {}) => { // TODO .catch((resp = {}) => { // TODO
if (resp.statusCode === 400 && resp.error === 'invalid_request') { handleOauthParamsValidation(resp);
alert(`Invalid request (${resp.parameter} required).`); if (resp.statusCode === 401 && resp.error === 'accept_required') {
} alert('Accept 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}'.`);
} }
}); });
} }
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 const SET_CLIENT = 'set_client';
export function setClient({id, name, description}) { export function setClient({id, name, description}) {
return { return {
@ -177,3 +217,17 @@ export function setClient({id, name, description}) {
payload: {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
}
};
}

View File

@ -1,10 +1,11 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { ERROR, SET_CLIENT } from './actions'; import { ERROR, SET_CLIENT, SET_OAUTH } from './actions';
export default combineReducers({ export default combineReducers({
error, error,
client client,
oauth
}); });
function error( function error(
@ -39,3 +40,22 @@ function client(
return state; 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;
}
}

View File

@ -37,6 +37,8 @@ export default function routesFactory(store) {
} }
} }
// TODO: validate that we have all required data on premissions page
if (forcePath && pathname !== forcePath) { if (forcePath && pathname !== forcePath) {
switch (pathname) { switch (pathname) {
case '/': case '/':

View File

@ -1,4 +1,4 @@
function serialize(data) { function buildQuery(data) {
return Object.keys(data) return Object.keys(data)
.map( .map(
(keyName) => (keyName) =>
@ -33,7 +33,7 @@ export default {
...getDefaultHeaders(), ...getDefaultHeaders(),
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}, },
body: serialize(data) body: buildQuery(data)
}) })
.then(toJSON) .then(toJSON)
.then(handleResponse) .then(handleResponse)
@ -43,7 +43,7 @@ export default {
get(url, data) { get(url, data) {
if (typeof data === 'object') { if (typeof data === 'object') {
const separator = url.indexOf('?') === -1 ? '?' : '&'; const separator = url.indexOf('?') === -1 ? '?' : '&';
url += separator + serialize(data); url += separator + buildQuery(data);
} }
return fetch(url, { return fetch(url, {
@ -54,6 +54,8 @@ export default {
; ;
}, },
buildQuery,
setAuthToken(tkn) { setAuthToken(tkn) {
authToken = tkn; authToken = tkn;
} }