mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-27 07:20:23 +05:30
Decouple oauth api calls into separate module. Simple tests for oauth
actions
This commit is contained in:
parent
eb3d436843
commit
1b8333b006
@ -3,6 +3,7 @@ import { routeActions } from 'react-router-redux';
|
||||
import { updateUser, logout as logoutUser, changePassword as changeUserPassword, authenticate } from 'components/user/actions';
|
||||
import request from 'services/request';
|
||||
import authentication from 'services/api/authentication';
|
||||
import oauth from 'services/api/oauth';
|
||||
|
||||
export function login({login = '', password = '', rememberMe = false}) {
|
||||
const PASSWORD_REQUIRED = 'error.password_required';
|
||||
@ -146,100 +147,50 @@ 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(oauth) {
|
||||
export function oAuthValidate(oauthData) {
|
||||
return wrapInLoader((dispatch) =>
|
||||
request.get(
|
||||
'/api/oauth/validate',
|
||||
getOAuthRequest(oauth)
|
||||
)
|
||||
.then((resp) => {
|
||||
dispatch(setClient(resp.client));
|
||||
dispatch(setOAuthRequest(resp.oAuth));
|
||||
dispatch(setScopes(resp.session.scopes));
|
||||
})
|
||||
.catch((resp = {}) => { // TODO
|
||||
handleOauthParamsValidation(resp);
|
||||
})
|
||||
oauth.validate(oauthData)
|
||||
.then((resp) => {
|
||||
dispatch(setClient(resp.client));
|
||||
dispatch(setOAuthRequest(resp.oAuth));
|
||||
dispatch(setScopes(resp.session.scopes));
|
||||
})
|
||||
.catch(handleOauthParamsValidation)
|
||||
);
|
||||
}
|
||||
|
||||
export function oAuthComplete(params = {}) {
|
||||
return wrapInLoader((dispatch, getState) => {
|
||||
const oauth = getState().auth.oauth;
|
||||
const query = request.buildQuery(getOAuthRequest(oauth));
|
||||
return wrapInLoader((dispatch, getState) =>
|
||||
oauth.complete(getState().auth.oauth, params)
|
||||
.then((resp) => {
|
||||
if (resp.redirectUri.startsWith('static_page')) {
|
||||
resp.code = resp.redirectUri.match(/code=(.+)&/)[1];
|
||||
resp.redirectUri = resp.redirectUri.match(/^(.+)\?/)[1];
|
||||
resp.displayCode = resp.redirectUri === 'static_page_with_code';
|
||||
|
||||
return request.post(
|
||||
`/api/oauth/complete?${query}`,
|
||||
typeof params.accept === 'undefined' ? {} : {accept: params.accept}
|
||||
)
|
||||
.catch((resp = {}) => { // TODO
|
||||
if (resp.statusCode === 401 && resp.error === 'access_denied') {
|
||||
// user declined permissions
|
||||
return {
|
||||
success: false,
|
||||
redirectUri: resp.redirectUri
|
||||
};
|
||||
}
|
||||
dispatch(setOAuthCode({
|
||||
success: resp.success,
|
||||
code: resp.code,
|
||||
displayCode: resp.displayCode
|
||||
}));
|
||||
}
|
||||
|
||||
handleOauthParamsValidation(resp);
|
||||
return resp;
|
||||
}, (resp) => {
|
||||
if (resp.acceptRequired) {
|
||||
dispatch(requirePermissionsAccept());
|
||||
}
|
||||
|
||||
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;
|
||||
dispatch(requirePermissionsAccept());
|
||||
throw error;
|
||||
}
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.redirectUri.startsWith('static_page')) {
|
||||
resp.code = resp.redirectUri.match(/code=(.+)&/)[1];
|
||||
resp.redirectUri = resp.redirectUri.match(/^(.+)\?/)[1];
|
||||
resp.displayCode = resp.redirectUri === 'static_page_with_code';
|
||||
dispatch(setOAuthCode({
|
||||
success: resp.success,
|
||||
code: resp.code,
|
||||
displayCode: resp.displayCode
|
||||
}));
|
||||
}
|
||||
|
||||
return resp;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getOAuthRequest(oauth) {
|
||||
return {
|
||||
client_id: oauth.clientId,
|
||||
redirect_uri: oauth.redirectUrl,
|
||||
response_type: oauth.responseType,
|
||||
scope: oauth.scope,
|
||||
state: oauth.state
|
||||
};
|
||||
return handleOauthParamsValidation(resp);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function handleOauthParamsValidation(resp = {}) {
|
||||
let userMessage;
|
||||
if (resp.statusCode === 400 && resp.error === 'invalid_request') {
|
||||
userMessage = `Invalid request (${resp.parameter} required).`;
|
||||
} else if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') {
|
||||
userMessage = `Invalid response type '${resp.parameter}'.`;
|
||||
} else if (resp.statusCode === 400 && resp.error === 'invalid_scope') {
|
||||
userMessage = `Invalid scope '${resp.parameter}'.`;
|
||||
} else if (resp.statusCode === 401 && resp.error === 'invalid_client') {
|
||||
userMessage = 'Can not find application you are trying to authorize.';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/* eslint no-alert: "off" */
|
||||
alert(userMessage);
|
||||
throw new Error('Error completing request');
|
||||
resp.userMessage && alert(resp.userMessage);
|
||||
|
||||
return Promise.reject(resp);
|
||||
}
|
||||
|
||||
export const SET_CLIENT = 'set_client';
|
||||
|
69
src/services/api/oauth.js
Normal file
69
src/services/api/oauth.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from 'services/request';
|
||||
|
||||
export default {
|
||||
validate(oauthData) {
|
||||
return request.get(
|
||||
'/api/oauth/validate',
|
||||
getOAuthRequest(oauthData)
|
||||
).catch(handleOauthParamsValidation);
|
||||
},
|
||||
|
||||
complete(oauthData, params = {}) {
|
||||
const query = request.buildQuery(getOAuthRequest(oauthData));
|
||||
|
||||
return request.post(
|
||||
`/api/oauth/complete?${query}`,
|
||||
typeof params.accept === 'undefined' ? {} : {accept: params.accept}
|
||||
).catch((resp = {}) => {
|
||||
if (resp.statusCode === 401 && resp.error === 'access_denied') {
|
||||
// user declined permissions
|
||||
return {
|
||||
success: false,
|
||||
redirectUri: resp.redirectUri
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return handleOauthParamsValidation(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getOAuthRequest(oauthData) {
|
||||
return {
|
||||
client_id: oauthData.clientId,
|
||||
redirect_uri: oauthData.redirectUrl,
|
||||
response_type: oauthData.responseType,
|
||||
scope: oauthData.scope,
|
||||
state: oauthData.state
|
||||
};
|
||||
}
|
||||
|
||||
function handleOauthParamsValidation(resp = {}) {
|
||||
let userMessage;
|
||||
|
||||
if (resp.statusCode === 400 && resp.error === 'invalid_request') {
|
||||
userMessage = `Invalid request (${resp.parameter} required).`;
|
||||
} else if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') {
|
||||
userMessage = `Invalid response type '${resp.parameter}'.`;
|
||||
} else if (resp.statusCode === 400 && resp.error === 'invalid_scope') {
|
||||
userMessage = `Invalid scope '${resp.parameter}'.`;
|
||||
} else if (resp.statusCode === 401 && resp.error === 'invalid_client') {
|
||||
userMessage = 'Can not find application you are trying to authorize.';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.reject(userMessage);
|
||||
}
|
126
tests/components/auth/actions.test.js
Normal file
126
tests/components/auth/actions.test.js
Normal file
@ -0,0 +1,126 @@
|
||||
import request from 'services/request';
|
||||
|
||||
import {
|
||||
oAuthValidate,
|
||||
oAuthComplete,
|
||||
setClient,
|
||||
setOAuthRequest,
|
||||
setScopes,
|
||||
setOAuthCode,
|
||||
requirePermissionsAccept
|
||||
} from 'components/auth/actions';
|
||||
|
||||
const oauthData = {
|
||||
clientId: '',
|
||||
redirectUrl: '',
|
||||
responseType: '',
|
||||
scope: '',
|
||||
state: ''
|
||||
};
|
||||
|
||||
describe('components/auth/actions', () => {
|
||||
const dispatch = sinon.stub();
|
||||
const getState = sinon.stub();
|
||||
|
||||
const callThunk = function(fn, ...args) {
|
||||
const thunk = fn(...args);
|
||||
|
||||
return thunk(dispatch, getState);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dispatch.reset();
|
||||
getState.reset();
|
||||
getState.returns({});
|
||||
sinon.stub(request, 'get');
|
||||
sinon.stub(request, 'post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
request.get.restore();
|
||||
request.post.restore();
|
||||
});
|
||||
|
||||
describe('#oAuthValidate()', () => {
|
||||
it('should dispatch setClient, setOAuthRequest and setScopes', () => {
|
||||
// TODO: the assertions may be splitted up to one per test
|
||||
|
||||
const resp = {
|
||||
client: {id: 123},
|
||||
oAuth: {state: 123},
|
||||
session: {
|
||||
scopes: ['scopes']
|
||||
}
|
||||
};
|
||||
|
||||
request.get.returns(Promise.resolve(resp));
|
||||
|
||||
return callThunk(oAuthValidate, oauthData).then(() => {
|
||||
sinon.assert.calledWith(request.get, '/api/oauth/validate');
|
||||
sinon.assert.calledWith(dispatch, setClient(resp.client));
|
||||
sinon.assert.calledWith(dispatch, setOAuthRequest(resp.oAuth));
|
||||
sinon.assert.calledWith(dispatch, setScopes(resp.session.scopes));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#oAuthComplete()', () => {
|
||||
beforeEach(() => {
|
||||
getState.returns({
|
||||
auth: {
|
||||
oauth: oauthData
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch setOAuthCode for static_page redirect', () => {
|
||||
// TODO: it may be split on separate url and dispatch tests
|
||||
const resp = {
|
||||
success: true,
|
||||
redirectUri: 'static_page?code=123&state='
|
||||
};
|
||||
|
||||
request.post.returns(Promise.resolve(resp));
|
||||
|
||||
return callThunk(oAuthComplete).then(() => {
|
||||
sinon.assert.calledWithMatch(request.post, /\/api\/oauth\/complete/);
|
||||
sinon.assert.calledWith(dispatch, setOAuthCode({
|
||||
success: true,
|
||||
code: '123',
|
||||
displayCode: false
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve to with success false and redirectUri for access_denied', () => {
|
||||
const resp = {
|
||||
statusCode: 401,
|
||||
error: 'access_denied',
|
||||
redirectUri: 'redirectUri'
|
||||
};
|
||||
|
||||
request.post.returns(Promise.reject(resp));
|
||||
|
||||
return callThunk(oAuthComplete).then((resp) => {
|
||||
expect(resp).to.be.deep.equal({
|
||||
success: false,
|
||||
redirectUri: 'redirectUri'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch requirePermissionsAccept if accept_required', () => {
|
||||
const resp = {
|
||||
statusCode: 401,
|
||||
error: 'accept_required'
|
||||
};
|
||||
|
||||
request.post.returns(Promise.reject(resp));
|
||||
|
||||
return callThunk(oAuthComplete).catch((resp) => {
|
||||
expect(resp.acceptRequired).to.be.true;
|
||||
sinon.assert.calledWith(dispatch, requirePermissionsAccept());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user