mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
Change prettier rules
This commit is contained in:
@@ -1,101 +1,88 @@
|
||||
import request from 'app/services/request';
|
||||
|
||||
export interface UserResponse {
|
||||
elyProfileLink: string;
|
||||
email: string;
|
||||
hasMojangUsernameCollision: boolean;
|
||||
id: number;
|
||||
isActive: boolean;
|
||||
isOtpEnabled: boolean;
|
||||
lang: string;
|
||||
passwordChangedAt: number; // timestamp
|
||||
registeredAt: number; // timestamp
|
||||
shouldAcceptRules: boolean;
|
||||
username: string;
|
||||
uuid: string;
|
||||
elyProfileLink: string;
|
||||
email: string;
|
||||
hasMojangUsernameCollision: boolean;
|
||||
id: number;
|
||||
isActive: boolean;
|
||||
isOtpEnabled: boolean;
|
||||
lang: string;
|
||||
passwordChangedAt: number; // timestamp
|
||||
registeredAt: number; // timestamp
|
||||
shouldAcceptRules: boolean;
|
||||
username: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export function getInfo(id: number, token?: string): Promise<UserResponse> {
|
||||
return request.get(
|
||||
`/api/v1/accounts/${id}`,
|
||||
{},
|
||||
{
|
||||
token,
|
||||
},
|
||||
);
|
||||
return request.get(
|
||||
`/api/v1/accounts/${id}`,
|
||||
{},
|
||||
{
|
||||
token,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function changePassword(
|
||||
id: number,
|
||||
{
|
||||
password = '',
|
||||
newPassword = '',
|
||||
newRePassword = '',
|
||||
logoutAll = true,
|
||||
}: {
|
||||
password?: string;
|
||||
newPassword?: string;
|
||||
newRePassword?: string;
|
||||
logoutAll?: boolean;
|
||||
},
|
||||
id: number,
|
||||
{
|
||||
password = '',
|
||||
newPassword = '',
|
||||
newRePassword = '',
|
||||
logoutAll = true,
|
||||
}: {
|
||||
password?: string;
|
||||
newPassword?: string;
|
||||
newRePassword?: string;
|
||||
logoutAll?: boolean;
|
||||
},
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/password`, {
|
||||
password,
|
||||
newPassword,
|
||||
newRePassword,
|
||||
logoutAll,
|
||||
});
|
||||
return request.post(`/api/v1/accounts/${id}/password`, {
|
||||
password,
|
||||
newPassword,
|
||||
newRePassword,
|
||||
logoutAll,
|
||||
});
|
||||
}
|
||||
|
||||
export function acceptRules(id: number): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/rules`);
|
||||
return request.post(`/api/v1/accounts/${id}/rules`);
|
||||
}
|
||||
|
||||
export function changeUsername(
|
||||
id: number,
|
||||
username: string | void,
|
||||
password: string | void,
|
||||
id: number,
|
||||
username: string | void,
|
||||
password: string | void,
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/username`, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
return request.post(`/api/v1/accounts/${id}/username`, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
}
|
||||
|
||||
export function changeLang(
|
||||
id: number,
|
||||
lang: string,
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/language`, {
|
||||
lang,
|
||||
});
|
||||
export function changeLang(id: number, lang: string): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/language`, {
|
||||
lang,
|
||||
});
|
||||
}
|
||||
|
||||
export function requestEmailChange(
|
||||
id: number,
|
||||
password: string,
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/email-verification`, {
|
||||
password,
|
||||
});
|
||||
export function requestEmailChange(id: number, password: string): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/email-verification`, {
|
||||
password,
|
||||
});
|
||||
}
|
||||
|
||||
export function setNewEmail(
|
||||
id: number,
|
||||
email: string,
|
||||
key: string,
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/new-email-verification`, {
|
||||
email,
|
||||
key,
|
||||
});
|
||||
export function setNewEmail(id: number, email: string, key: string): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/new-email-verification`, {
|
||||
email,
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmNewEmail(
|
||||
id: number,
|
||||
key: string,
|
||||
): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/email`, {
|
||||
key,
|
||||
});
|
||||
export function confirmNewEmail(id: number, key: string): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/v1/accounts/${id}/email`, {
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,75 +5,59 @@ import request from 'app/services/request';
|
||||
import * as signup from 'app/services/api/signup';
|
||||
|
||||
describe('signup api', () => {
|
||||
describe('#register', () => {
|
||||
const params = {
|
||||
email: 'email',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
rePassword: 'rePassword',
|
||||
rulesAgreement: false,
|
||||
lang: 'lang',
|
||||
captcha: 'captcha',
|
||||
};
|
||||
describe('#register', () => {
|
||||
const params = {
|
||||
email: 'email',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
rePassword: 'rePassword',
|
||||
rulesAgreement: false,
|
||||
lang: 'lang',
|
||||
captcha: 'captcha',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should post to register api', () => {
|
||||
signup.register(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/signup', params, {}]);
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
signup.register(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/signup', params, { token: null }]);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
describe('#activate', () => {
|
||||
const key = 'key';
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should post to confirmation api', () => {
|
||||
signup.activate(key);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/signup/confirm', { key }, {}]);
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
signup.activate(key);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/signup/confirm', { key }, { token: null }]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should post to register api', () => {
|
||||
signup.register(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/signup',
|
||||
params,
|
||||
{},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
signup.register(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/signup',
|
||||
params,
|
||||
{ token: null },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#activate', () => {
|
||||
const key = 'key';
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should post to confirmation api', () => {
|
||||
signup.activate(key);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/signup/confirm',
|
||||
{ key },
|
||||
{},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
signup.activate(key);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/signup/confirm',
|
||||
{ key },
|
||||
{ token: null },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,334 +6,261 @@ import * as authentication from 'app/services/api/authentication';
|
||||
import * as accounts from 'app/services/api/accounts';
|
||||
|
||||
describe('authentication api', () => {
|
||||
let server: SinonFakeServer;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create({
|
||||
autoRespond: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
describe('#login', () => {
|
||||
const params = {
|
||||
login: 'foo',
|
||||
password: 'secret',
|
||||
rememberMe: false,
|
||||
};
|
||||
let server: SinonFakeServer;
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
|
||||
(request.post as any).returns(Promise.resolve());
|
||||
server = sinon.fakeServer.create({
|
||||
autoRespond: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
server.restore();
|
||||
});
|
||||
|
||||
it('should post to login api', () => {
|
||||
authentication.login(params);
|
||||
describe('#login', () => {
|
||||
const params = {
|
||||
login: 'foo',
|
||||
password: 'secret',
|
||||
rememberMe: false,
|
||||
};
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/login',
|
||||
params,
|
||||
{},
|
||||
]);
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
|
||||
(request.post as any).returns(Promise.resolve());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should post to login api', () => {
|
||||
authentication.login(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/authentication/login', params, {}]);
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
authentication.login(params);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/authentication/login', params, { token: null }]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable any token', () => {
|
||||
authentication.login(params);
|
||||
describe('#validateToken()', () => {
|
||||
const validToken = 'foo';
|
||||
const validRefreshToken = 'bar';
|
||||
const user = { id: 1 };
|
||||
const validateTokenArgs: [number, string, string] = [user.id, validToken, validRefreshToken];
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/login',
|
||||
params,
|
||||
{ token: null },
|
||||
]);
|
||||
});
|
||||
});
|
||||
beforeEach(() => {
|
||||
sinon.stub(accounts, 'getInfo');
|
||||
(accounts.getInfo as any).returns(Promise.resolve(user));
|
||||
});
|
||||
|
||||
describe('#validateToken()', () => {
|
||||
const validToken = 'foo';
|
||||
const validRefreshToken = 'bar';
|
||||
const user = { id: 1 };
|
||||
const validateTokenArgs: [number, string, string] = [
|
||||
user.id,
|
||||
validToken,
|
||||
validRefreshToken,
|
||||
];
|
||||
afterEach(() => {
|
||||
(accounts.getInfo as any).restore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(accounts, 'getInfo');
|
||||
(accounts.getInfo as any).returns(Promise.resolve(user));
|
||||
it('should request accounts.getInfo', () =>
|
||||
expect(authentication.validateToken(...validateTokenArgs), 'to be fulfilled').then(() => {
|
||||
expect(accounts.getInfo, 'to have a call satisfying', [user.id, validToken]);
|
||||
}));
|
||||
|
||||
it('should resolve with both tokens and user object', () =>
|
||||
expect(authentication.validateToken(...validateTokenArgs), 'to be fulfilled with', {
|
||||
token: validToken,
|
||||
refreshToken: validRefreshToken,
|
||||
user,
|
||||
}));
|
||||
|
||||
it('rejects if token has a bad type', () =>
|
||||
expect(authentication.validateToken(user.id, {} as any), 'to be rejected with', 'token must be a string'));
|
||||
|
||||
it('should allow empty refreshToken', () =>
|
||||
expect(authentication.validateToken(user.id, 'foo', null), 'to be fulfilled'));
|
||||
|
||||
it('rejects if accounts.getInfo request is unexpectedly failed', () => {
|
||||
const error = 'Something wrong';
|
||||
(accounts.getInfo as any).returns(Promise.reject(error));
|
||||
|
||||
return expect(authentication.validateToken(...validateTokenArgs), 'to be rejected with', error);
|
||||
});
|
||||
|
||||
describe('when token is expired', () => {
|
||||
const expiredResponse = {
|
||||
name: 'Unauthorized',
|
||||
message: 'Token expired',
|
||||
code: 0,
|
||||
status: 401,
|
||||
type: 'yii\\web\\UnauthorizedHttpException',
|
||||
};
|
||||
const newToken = 'baz';
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(authentication, 'requestToken');
|
||||
|
||||
(accounts.getInfo as any).onCall(0).returns(Promise.reject(expiredResponse));
|
||||
(authentication.requestToken as any).returns(Promise.resolve(newToken));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(authentication.requestToken as any).restore();
|
||||
});
|
||||
|
||||
it('resolves with new token and user object', async () => {
|
||||
server.respondWith(
|
||||
'POST',
|
||||
'/api/authentication/refresh-token',
|
||||
JSON.stringify({
|
||||
access_token: newToken,
|
||||
refresh_token: validRefreshToken,
|
||||
success: true,
|
||||
expires_in: 50000,
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(authentication.validateToken(...validateTokenArgs), 'to be fulfilled with', {
|
||||
token: newToken,
|
||||
refreshToken: validRefreshToken,
|
||||
user,
|
||||
});
|
||||
|
||||
expect(server.requests[0].requestBody, 'to equal', `refresh_token=${validRefreshToken}`);
|
||||
});
|
||||
|
||||
it('rejects if token request failed', () => {
|
||||
const error = { error: 'Unexpected error example' };
|
||||
server.respondWith('POST', '/api/authentication/refresh-token', [500, [], JSON.stringify(error)]);
|
||||
|
||||
return expect(authentication.validateToken(...validateTokenArgs), 'to be rejected with', error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when token is incorrect', () => {
|
||||
const expiredResponse = {
|
||||
name: 'Unauthorized',
|
||||
message: 'Incorrect token',
|
||||
code: 0,
|
||||
status: 401,
|
||||
type: 'yii\\web\\UnauthorizedHttpException',
|
||||
};
|
||||
const newToken = 'baz';
|
||||
|
||||
beforeEach(() => {
|
||||
(accounts.getInfo as any).onCall(0).returns(Promise.reject(expiredResponse));
|
||||
});
|
||||
|
||||
it('resolves with new token and user object', async () => {
|
||||
server.respondWith(
|
||||
'POST',
|
||||
'/api/authentication/refresh-token',
|
||||
JSON.stringify({
|
||||
access_token: newToken,
|
||||
refresh_token: validRefreshToken,
|
||||
success: true,
|
||||
expires_in: 50000,
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(authentication.validateToken(...validateTokenArgs), 'to be fulfilled with', {
|
||||
token: newToken,
|
||||
refreshToken: validRefreshToken,
|
||||
user,
|
||||
});
|
||||
|
||||
expect(server.requests[0].requestBody, 'to equal', `refresh_token=${validRefreshToken}`);
|
||||
});
|
||||
|
||||
it('rejects if token request failed', () => {
|
||||
const error = { error: 'Unexpected error example' };
|
||||
server.respondWith('POST', '/api/authentication/refresh-token', [500, [], JSON.stringify(error)]);
|
||||
|
||||
return expect(authentication.validateToken(...validateTokenArgs), 'to be rejected with', error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(accounts.getInfo as any).restore();
|
||||
describe('#logout', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should request logout api', () => {
|
||||
authentication.logout();
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/authentication/logout', {}, {}]);
|
||||
});
|
||||
|
||||
it('returns a promise', () => {
|
||||
(request.post as any).returns(Promise.resolve());
|
||||
|
||||
return expect(authentication.logout(), 'to be fulfilled');
|
||||
});
|
||||
|
||||
it('overrides token if provided', () => {
|
||||
const token = 'foo';
|
||||
|
||||
authentication.logout(token);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', ['/api/authentication/logout', {}, { token }]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should request accounts.getInfo', () =>
|
||||
expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be fulfilled',
|
||||
).then(() => {
|
||||
expect(accounts.getInfo, 'to have a call satisfying', [
|
||||
user.id,
|
||||
validToken,
|
||||
]);
|
||||
}));
|
||||
describe('#requestToken', () => {
|
||||
const refreshToken = 'refresh-token';
|
||||
|
||||
it('should resolve with both tokens and user object', () =>
|
||||
expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be fulfilled with',
|
||||
{
|
||||
token: validToken,
|
||||
refreshToken: validRefreshToken,
|
||||
user,
|
||||
},
|
||||
));
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
it('rejects if token has a bad type', () =>
|
||||
expect(
|
||||
authentication.validateToken(user.id, {} as any),
|
||||
'to be rejected with',
|
||||
'token must be a string',
|
||||
));
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should allow empty refreshToken', () =>
|
||||
expect(
|
||||
authentication.validateToken(user.id, 'foo', null),
|
||||
'to be fulfilled',
|
||||
));
|
||||
it('should request refresh-token api', () => {
|
||||
(request.post as any).returns(Promise.resolve({}));
|
||||
|
||||
it('rejects if accounts.getInfo request is unexpectedly failed', () => {
|
||||
const error = 'Something wrong';
|
||||
(accounts.getInfo as any).returns(Promise.reject(error));
|
||||
authentication.requestToken(refreshToken);
|
||||
|
||||
return expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be rejected with',
|
||||
error,
|
||||
);
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken, // eslint-disable-line
|
||||
},
|
||||
{},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should disable bearer auth for request', () => {
|
||||
(request.post as any).returns(Promise.resolve({}));
|
||||
|
||||
authentication.requestToken(refreshToken);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken, // eslint-disable-line
|
||||
},
|
||||
{ token: null },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should resolve with token', () => {
|
||||
const token = 'token';
|
||||
|
||||
(request.post as any).returns(
|
||||
Promise.resolve({
|
||||
access_token: token, // eslint-disable-line
|
||||
}),
|
||||
);
|
||||
|
||||
return expect(authentication.requestToken(refreshToken), 'to be fulfilled with', token);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when token is expired', () => {
|
||||
const expiredResponse = {
|
||||
name: 'Unauthorized',
|
||||
message: 'Token expired',
|
||||
code: 0,
|
||||
status: 401,
|
||||
type: 'yii\\web\\UnauthorizedHttpException',
|
||||
};
|
||||
const newToken = 'baz';
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(authentication, 'requestToken');
|
||||
|
||||
(accounts.getInfo as any)
|
||||
.onCall(0)
|
||||
.returns(Promise.reject(expiredResponse));
|
||||
(authentication.requestToken as any).returns(Promise.resolve(newToken));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(authentication.requestToken as any).restore();
|
||||
});
|
||||
|
||||
it('resolves with new token and user object', async () => {
|
||||
server.respondWith(
|
||||
'POST',
|
||||
'/api/authentication/refresh-token',
|
||||
JSON.stringify({
|
||||
access_token: newToken,
|
||||
refresh_token: validRefreshToken,
|
||||
success: true,
|
||||
expires_in: 50000,
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be fulfilled with',
|
||||
{ token: newToken, refreshToken: validRefreshToken, user },
|
||||
);
|
||||
|
||||
expect(
|
||||
server.requests[0].requestBody,
|
||||
'to equal',
|
||||
`refresh_token=${validRefreshToken}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects if token request failed', () => {
|
||||
const error = { error: 'Unexpected error example' };
|
||||
server.respondWith('POST', '/api/authentication/refresh-token', [
|
||||
500,
|
||||
[],
|
||||
JSON.stringify(error),
|
||||
]);
|
||||
|
||||
return expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be rejected with',
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when token is incorrect', () => {
|
||||
const expiredResponse = {
|
||||
name: 'Unauthorized',
|
||||
message: 'Incorrect token',
|
||||
code: 0,
|
||||
status: 401,
|
||||
type: 'yii\\web\\UnauthorizedHttpException',
|
||||
};
|
||||
const newToken = 'baz';
|
||||
|
||||
beforeEach(() => {
|
||||
(accounts.getInfo as any)
|
||||
.onCall(0)
|
||||
.returns(Promise.reject(expiredResponse));
|
||||
});
|
||||
|
||||
it('resolves with new token and user object', async () => {
|
||||
server.respondWith(
|
||||
'POST',
|
||||
'/api/authentication/refresh-token',
|
||||
JSON.stringify({
|
||||
access_token: newToken,
|
||||
refresh_token: validRefreshToken,
|
||||
success: true,
|
||||
expires_in: 50000,
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be fulfilled with',
|
||||
{ token: newToken, refreshToken: validRefreshToken, user },
|
||||
);
|
||||
|
||||
expect(
|
||||
server.requests[0].requestBody,
|
||||
'to equal',
|
||||
`refresh_token=${validRefreshToken}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects if token request failed', () => {
|
||||
const error = { error: 'Unexpected error example' };
|
||||
server.respondWith('POST', '/api/authentication/refresh-token', [
|
||||
500,
|
||||
[],
|
||||
JSON.stringify(error),
|
||||
]);
|
||||
|
||||
return expect(
|
||||
authentication.validateToken(...validateTokenArgs),
|
||||
'to be rejected with',
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#logout', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should request logout api', () => {
|
||||
authentication.logout();
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/logout',
|
||||
{},
|
||||
{},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns a promise', () => {
|
||||
(request.post as any).returns(Promise.resolve());
|
||||
|
||||
return expect(authentication.logout(), 'to be fulfilled');
|
||||
});
|
||||
|
||||
it('overrides token if provided', () => {
|
||||
const token = 'foo';
|
||||
|
||||
authentication.logout(token);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/logout',
|
||||
{},
|
||||
{ token },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#requestToken', () => {
|
||||
const refreshToken = 'refresh-token';
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(request, 'post').named('request.post');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.post as any).restore();
|
||||
});
|
||||
|
||||
it('should request refresh-token api', () => {
|
||||
(request.post as any).returns(Promise.resolve({}));
|
||||
|
||||
authentication.requestToken(refreshToken);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken, // eslint-disable-line
|
||||
},
|
||||
{},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should disable bearer auth for request', () => {
|
||||
(request.post as any).returns(Promise.resolve({}));
|
||||
|
||||
authentication.requestToken(refreshToken);
|
||||
|
||||
expect(request.post, 'to have a call satisfying', [
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken, // eslint-disable-line
|
||||
},
|
||||
{ token: null },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should resolve with token', () => {
|
||||
const token = 'token';
|
||||
|
||||
(request.post as any).returns(
|
||||
Promise.resolve({
|
||||
access_token: token, // eslint-disable-line
|
||||
}),
|
||||
);
|
||||
|
||||
return expect(
|
||||
authentication.requestToken(refreshToken),
|
||||
'to be fulfilled with',
|
||||
token,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,33 +4,33 @@ import request, { InternalServerError } from 'app/services/request';
|
||||
import { getInfo as getInfoEndpoint } from 'app/services/api/accounts';
|
||||
|
||||
export interface OAuthResponse {
|
||||
access_token: string;
|
||||
refresh_token?: string;
|
||||
expires_in: number; // count seconds before expire
|
||||
success: true;
|
||||
access_token: string;
|
||||
refresh_token?: string;
|
||||
expires_in: number; // count seconds before expire
|
||||
success: true;
|
||||
}
|
||||
|
||||
export function login({
|
||||
login,
|
||||
password,
|
||||
totp,
|
||||
rememberMe = false,
|
||||
login,
|
||||
password,
|
||||
totp,
|
||||
rememberMe = false,
|
||||
}: {
|
||||
login: string;
|
||||
password?: string;
|
||||
totp?: string;
|
||||
rememberMe: boolean;
|
||||
login: string;
|
||||
password?: string;
|
||||
totp?: string;
|
||||
rememberMe: boolean;
|
||||
}): Promise<OAuthResponse> {
|
||||
return request.post(
|
||||
'/api/authentication/login',
|
||||
{
|
||||
login,
|
||||
password,
|
||||
totp,
|
||||
rememberMe,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
return request.post(
|
||||
'/api/authentication/login',
|
||||
{
|
||||
login,
|
||||
password,
|
||||
totp,
|
||||
rememberMe,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,53 +40,49 @@ export function login({
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function logout(token?: string): Promise<{ success: boolean }> {
|
||||
return request.post(
|
||||
'/api/authentication/logout',
|
||||
{},
|
||||
{
|
||||
token,
|
||||
},
|
||||
);
|
||||
return request.post(
|
||||
'/api/authentication/logout',
|
||||
{},
|
||||
{
|
||||
token,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function forgotPassword(
|
||||
login: string,
|
||||
captcha: string,
|
||||
login: string,
|
||||
captcha: string,
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data: {
|
||||
canRepeatIn: number;
|
||||
emailMask: string | void;
|
||||
repeatFrequency: number;
|
||||
};
|
||||
errors: {
|
||||
[key: string]: string;
|
||||
};
|
||||
success: boolean;
|
||||
data: {
|
||||
canRepeatIn: number;
|
||||
emailMask: string | void;
|
||||
repeatFrequency: number;
|
||||
};
|
||||
errors: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}> {
|
||||
return request.post(
|
||||
'/api/authentication/forgot-password',
|
||||
{
|
||||
login,
|
||||
captcha,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
return request.post(
|
||||
'/api/authentication/forgot-password',
|
||||
{
|
||||
login,
|
||||
captcha,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
}
|
||||
|
||||
export function recoverPassword(
|
||||
key: string,
|
||||
newPassword: string,
|
||||
newRePassword: string,
|
||||
): Promise<OAuthResponse> {
|
||||
return request.post(
|
||||
'/api/authentication/recover-password',
|
||||
{
|
||||
key,
|
||||
newPassword,
|
||||
newRePassword,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
export function recoverPassword(key: string, newPassword: string, newRePassword: string): Promise<OAuthResponse> {
|
||||
return request.post(
|
||||
'/api/authentication/recover-password',
|
||||
{
|
||||
key,
|
||||
newPassword,
|
||||
newRePassword,
|
||||
},
|
||||
{ token: null },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,59 +98,52 @@ export function recoverPassword(
|
||||
*
|
||||
*/
|
||||
export async function validateToken(
|
||||
id: number,
|
||||
token: string,
|
||||
refreshToken: string | void | null,
|
||||
id: number,
|
||||
token: string,
|
||||
refreshToken: string | void | null,
|
||||
): Promise<{
|
||||
token: string;
|
||||
refreshToken: string | null;
|
||||
user: UserResponse;
|
||||
token: string;
|
||||
refreshToken: string | null;
|
||||
user: UserResponse;
|
||||
}> {
|
||||
if (typeof token !== 'string') {
|
||||
throw new Error('token must be a string');
|
||||
}
|
||||
|
||||
refreshToken = refreshToken || null;
|
||||
|
||||
let user: UserResponse;
|
||||
try {
|
||||
user = await getInfoEndpoint(id, token);
|
||||
} catch (resp) {
|
||||
token = await handleTokenError(resp, refreshToken);
|
||||
user = await getInfoEndpoint(id, token); // TODO: replace with recursive call
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
refreshToken,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
const recoverableErrors = [
|
||||
'Token expired',
|
||||
'Incorrect token',
|
||||
'You are requesting with an invalid credential.',
|
||||
];
|
||||
|
||||
function handleTokenError(
|
||||
resp: Error | { message: string },
|
||||
refreshToken: string | null,
|
||||
): Promise<string> {
|
||||
if (resp instanceof InternalServerError) {
|
||||
// delegate error recovering to the bsod middleware
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
||||
if (refreshToken) {
|
||||
if (recoverableErrors.includes(resp.message)) {
|
||||
return requestToken(refreshToken);
|
||||
if (typeof token !== 'string') {
|
||||
throw new Error('token must be a string');
|
||||
}
|
||||
|
||||
logger.error('Unexpected error during token validation', { resp });
|
||||
}
|
||||
refreshToken = refreshToken || null;
|
||||
|
||||
return Promise.reject(resp);
|
||||
let user: UserResponse;
|
||||
try {
|
||||
user = await getInfoEndpoint(id, token);
|
||||
} catch (resp) {
|
||||
token = await handleTokenError(resp, refreshToken);
|
||||
user = await getInfoEndpoint(id, token); // TODO: replace with recursive call
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
refreshToken,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
const recoverableErrors = ['Token expired', 'Incorrect token', 'You are requesting with an invalid credential.'];
|
||||
|
||||
function handleTokenError(resp: Error | { message: string }, refreshToken: string | null): Promise<string> {
|
||||
if (resp instanceof InternalServerError) {
|
||||
// delegate error recovering to the bsod middleware
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
||||
if (refreshToken) {
|
||||
if (recoverableErrors.includes(resp.message)) {
|
||||
return requestToken(refreshToken);
|
||||
}
|
||||
|
||||
logger.error('Unexpected error during token validation', { resp });
|
||||
}
|
||||
|
||||
return Promise.reject(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,27 +154,27 @@ function handleTokenError(
|
||||
* @returns {Promise} - resolves to token
|
||||
*/
|
||||
export async function requestToken(refreshToken: string): Promise<string> {
|
||||
try {
|
||||
const response: OAuthResponse = await request.post(
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken,
|
||||
},
|
||||
{
|
||||
token: null,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const response: OAuthResponse = await request.post(
|
||||
'/api/authentication/refresh-token',
|
||||
{
|
||||
refresh_token: refreshToken,
|
||||
},
|
||||
{
|
||||
token: null,
|
||||
},
|
||||
);
|
||||
|
||||
return response.access_token;
|
||||
} catch (resp) {
|
||||
const errors = resp.errors || {};
|
||||
return response.access_token;
|
||||
} catch (resp) {
|
||||
const errors = resp.errors || {};
|
||||
|
||||
if (errors.refresh_token !== 'error.refresh_token_not_exist') {
|
||||
logger.error('Failed refreshing token: unknown error', {
|
||||
resp,
|
||||
});
|
||||
if (errors.refresh_token !== 'error.refresh_token_not_exist') {
|
||||
logger.error('Failed refreshing token: unknown error', {
|
||||
resp,
|
||||
});
|
||||
}
|
||||
|
||||
throw resp;
|
||||
}
|
||||
|
||||
throw resp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import request from 'app/services/request';
|
||||
|
||||
export default {
|
||||
send({ subject = '', email = '', message = '', category = '' }) {
|
||||
return request.post('/api/feedback', {
|
||||
subject,
|
||||
email,
|
||||
message,
|
||||
category,
|
||||
});
|
||||
},
|
||||
send({ subject = '', email = '', message = '', category = '' }) {
|
||||
return request.post('/api/feedback', {
|
||||
subject,
|
||||
email,
|
||||
message,
|
||||
category,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,35 +1,27 @@
|
||||
import request, { Resp } from 'app/services/request';
|
||||
|
||||
export function getSecret(
|
||||
id: number,
|
||||
id: number,
|
||||
): Promise<
|
||||
Resp<{
|
||||
qr: string;
|
||||
secret: string;
|
||||
uri: string;
|
||||
}>
|
||||
Resp<{
|
||||
qr: string;
|
||||
secret: string;
|
||||
uri: string;
|
||||
}>
|
||||
> {
|
||||
return request.get(`/api/v1/accounts/${id}/two-factor-auth`);
|
||||
return request.get(`/api/v1/accounts/${id}/two-factor-auth`);
|
||||
}
|
||||
|
||||
export function enable(
|
||||
id: number,
|
||||
totp: string,
|
||||
password?: string,
|
||||
): Promise<Resp<any>> {
|
||||
return request.post(`/api/v1/accounts/${id}/two-factor-auth`, {
|
||||
totp,
|
||||
password,
|
||||
});
|
||||
export function enable(id: number, totp: string, password?: string): Promise<Resp<any>> {
|
||||
return request.post(`/api/v1/accounts/${id}/two-factor-auth`, {
|
||||
totp,
|
||||
password,
|
||||
});
|
||||
}
|
||||
|
||||
export function disable(
|
||||
id: number,
|
||||
totp: string,
|
||||
password?: string,
|
||||
): Promise<Resp<any>> {
|
||||
return request.delete(`/api/v1/accounts/${id}/two-factor-auth`, {
|
||||
totp,
|
||||
password,
|
||||
});
|
||||
export function disable(id: number, totp: string, password?: string): Promise<Resp<any>> {
|
||||
return request.delete(`/api/v1/accounts/${id}/two-factor-auth`, {
|
||||
totp,
|
||||
password,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,164 +1,147 @@
|
||||
import { ApplicationType } from 'app/components/dev/apps';
|
||||
import request from 'app/services/request';
|
||||
|
||||
export type Scope =
|
||||
| 'minecraft_server_session'
|
||||
| 'offline_access'
|
||||
| 'account_info'
|
||||
| 'account_email';
|
||||
export type Scope = 'minecraft_server_session' | 'offline_access' | 'account_info' | 'account_email';
|
||||
|
||||
export interface Client {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface OauthAppResponse {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
type: ApplicationType;
|
||||
name: string;
|
||||
websiteUrl: string;
|
||||
createdAt: number;
|
||||
// fields for 'application' type
|
||||
countUsers?: number;
|
||||
description?: string;
|
||||
redirectUri?: string;
|
||||
// fields for 'minecraft-server' type
|
||||
minecraftServerIp?: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
type: ApplicationType;
|
||||
name: string;
|
||||
websiteUrl: string;
|
||||
createdAt: number;
|
||||
// fields for 'application' type
|
||||
countUsers?: number;
|
||||
description?: string;
|
||||
redirectUri?: string;
|
||||
// fields for 'minecraft-server' type
|
||||
minecraftServerIp?: string;
|
||||
}
|
||||
|
||||
interface OauthRequestData {
|
||||
client_id: string;
|
||||
redirect_uri: string;
|
||||
response_type: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
prompt: string;
|
||||
login_hint?: string;
|
||||
state?: string;
|
||||
client_id: string;
|
||||
redirect_uri: string;
|
||||
response_type: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
prompt: string;
|
||||
login_hint?: string;
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export interface OauthData {
|
||||
clientId: string;
|
||||
redirectUrl: string;
|
||||
responseType: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
// TODO: why prompt is not nullable?
|
||||
prompt: string; // comma separated list of 'none' | 'consent' | 'select_account';
|
||||
loginHint?: string;
|
||||
state?: string;
|
||||
clientId: string;
|
||||
redirectUrl: string;
|
||||
responseType: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
// TODO: why prompt is not nullable?
|
||||
prompt: string; // comma separated list of 'none' | 'consent' | 'select_account';
|
||||
loginHint?: string;
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export interface OAuthValidateResponse {
|
||||
session: {
|
||||
scopes: Scope[];
|
||||
};
|
||||
client: Client;
|
||||
oAuth: {}; // TODO: improve typing
|
||||
session: {
|
||||
scopes: Scope[];
|
||||
};
|
||||
client: Client;
|
||||
oAuth: {}; // TODO: improve typing
|
||||
}
|
||||
|
||||
interface FormPayloads {
|
||||
name?: string;
|
||||
description?: string;
|
||||
websiteUrl?: string;
|
||||
redirectUri?: string;
|
||||
minecraftServerIp?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
websiteUrl?: string;
|
||||
redirectUri?: string;
|
||||
minecraftServerIp?: string;
|
||||
}
|
||||
|
||||
const api = {
|
||||
validate(oauthData: OauthData) {
|
||||
return request
|
||||
.get<OAuthValidateResponse>(
|
||||
'/api/oauth2/v1/validate',
|
||||
getOAuthRequest(oauthData),
|
||||
)
|
||||
.catch(handleOauthParamsValidation);
|
||||
},
|
||||
validate(oauthData: OauthData) {
|
||||
return request
|
||||
.get<OAuthValidateResponse>('/api/oauth2/v1/validate', getOAuthRequest(oauthData))
|
||||
.catch(handleOauthParamsValidation);
|
||||
},
|
||||
|
||||
complete(
|
||||
oauthData: OauthData,
|
||||
params: { accept?: boolean } = {},
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
redirectUri: string;
|
||||
}> {
|
||||
const query = request.buildQuery(getOAuthRequest(oauthData));
|
||||
|
||||
return request
|
||||
.post<{
|
||||
complete(
|
||||
oauthData: OauthData,
|
||||
params: { accept?: boolean } = {},
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
redirectUri: string;
|
||||
}>(
|
||||
`/api/oauth2/v1/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,
|
||||
originalResponse: resp.originalResponse,
|
||||
};
|
||||
}
|
||||
}> {
|
||||
const query = request.buildQuery(getOAuthRequest(oauthData));
|
||||
|
||||
if (resp.status === 401 && resp.name === 'Unauthorized') {
|
||||
const error: { [key: string]: any } = new Error('Unauthorized');
|
||||
error.unauthorized = true;
|
||||
throw error;
|
||||
}
|
||||
return request
|
||||
.post<{
|
||||
success: boolean;
|
||||
redirectUri: string;
|
||||
}>(
|
||||
`/api/oauth2/v1/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,
|
||||
originalResponse: resp.originalResponse,
|
||||
};
|
||||
}
|
||||
|
||||
if (resp.statusCode === 401 && resp.error === 'accept_required') {
|
||||
const error: { [key: string]: any } = new Error(
|
||||
'Permissions accept required',
|
||||
);
|
||||
error.acceptRequired = true;
|
||||
throw error;
|
||||
}
|
||||
if (resp.status === 401 && resp.name === 'Unauthorized') {
|
||||
const error: { [key: string]: any } = new Error('Unauthorized');
|
||||
error.unauthorized = true;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return handleOauthParamsValidation(resp);
|
||||
});
|
||||
},
|
||||
if (resp.statusCode === 401 && resp.error === 'accept_required') {
|
||||
const error: { [key: string]: any } = new Error('Permissions accept required');
|
||||
error.acceptRequired = true;
|
||||
throw error;
|
||||
}
|
||||
|
||||
create(type: string, formParams: FormPayloads) {
|
||||
return request.post<{ success: boolean; data: OauthAppResponse }>(
|
||||
`/api/v1/oauth2/${type}`,
|
||||
formParams,
|
||||
);
|
||||
},
|
||||
return handleOauthParamsValidation(resp);
|
||||
});
|
||||
},
|
||||
|
||||
update(clientId: string, formParams: FormPayloads) {
|
||||
return request.put<{ success: boolean; data: OauthAppResponse }>(
|
||||
`/api/v1/oauth2/${clientId}`,
|
||||
formParams,
|
||||
);
|
||||
},
|
||||
create(type: string, formParams: FormPayloads) {
|
||||
return request.post<{ success: boolean; data: OauthAppResponse }>(`/api/v1/oauth2/${type}`, formParams);
|
||||
},
|
||||
|
||||
getApp(clientId: string) {
|
||||
return request.get<OauthAppResponse>(`/api/v1/oauth2/${clientId}`);
|
||||
},
|
||||
update(clientId: string, formParams: FormPayloads) {
|
||||
return request.put<{ success: boolean; data: OauthAppResponse }>(`/api/v1/oauth2/${clientId}`, formParams);
|
||||
},
|
||||
|
||||
getAppsByUser(userId: number): Promise<OauthAppResponse[]> {
|
||||
return request.get(`/api/v1/accounts/${userId}/oauth2/clients`);
|
||||
},
|
||||
getApp(clientId: string) {
|
||||
return request.get<OauthAppResponse>(`/api/v1/oauth2/${clientId}`);
|
||||
},
|
||||
|
||||
reset(clientId: string, regenerateSecret: boolean = false) {
|
||||
return request.post<{ success: boolean; data: OauthAppResponse }>(
|
||||
`/api/v1/oauth2/${clientId}/reset${
|
||||
regenerateSecret ? '?regenerateSecret' : ''
|
||||
}`,
|
||||
);
|
||||
},
|
||||
getAppsByUser(userId: number): Promise<OauthAppResponse[]> {
|
||||
return request.get(`/api/v1/accounts/${userId}/oauth2/clients`);
|
||||
},
|
||||
|
||||
delete(clientId: string) {
|
||||
return request.delete<{ success: boolean }>(`/api/v1/oauth2/${clientId}`);
|
||||
},
|
||||
reset(clientId: string, regenerateSecret: boolean = false) {
|
||||
return request.post<{ success: boolean; data: OauthAppResponse }>(
|
||||
`/api/v1/oauth2/${clientId}/reset${regenerateSecret ? '?regenerateSecret' : ''}`,
|
||||
);
|
||||
},
|
||||
|
||||
delete(clientId: string) {
|
||||
return request.delete<{ success: boolean }>(`/api/v1/oauth2/${clientId}`);
|
||||
},
|
||||
};
|
||||
|
||||
if ('Cypress' in window) {
|
||||
(window as any).oauthApi = api;
|
||||
(window as any).oauthApi = api;
|
||||
}
|
||||
|
||||
export default api;
|
||||
@@ -175,48 +158,39 @@ export default api;
|
||||
* @returns {object}
|
||||
*/
|
||||
function getOAuthRequest(oauthData: OauthData): OauthRequestData {
|
||||
return {
|
||||
client_id: oauthData.clientId,
|
||||
redirect_uri: oauthData.redirectUrl,
|
||||
response_type: oauthData.responseType,
|
||||
description: oauthData.description,
|
||||
scope: oauthData.scope,
|
||||
prompt: oauthData.prompt,
|
||||
login_hint: oauthData.loginHint,
|
||||
state: oauthData.state,
|
||||
};
|
||||
return {
|
||||
client_id: oauthData.clientId,
|
||||
redirect_uri: oauthData.redirectUrl,
|
||||
response_type: oauthData.responseType,
|
||||
description: oauthData.description,
|
||||
scope: oauthData.scope,
|
||||
prompt: oauthData.prompt,
|
||||
login_hint: oauthData.loginHint,
|
||||
state: oauthData.state,
|
||||
};
|
||||
}
|
||||
|
||||
function handleOauthParamsValidation(
|
||||
resp:
|
||||
| { [key: string]: any }
|
||||
| {
|
||||
statusCode: number;
|
||||
success: false;
|
||||
error:
|
||||
| 'invalid_request'
|
||||
| 'unsupported_response_type'
|
||||
| 'invalid_scope'
|
||||
| 'invalid_client';
|
||||
parameter: string;
|
||||
} = {},
|
||||
resp:
|
||||
| { [key: string]: any }
|
||||
| {
|
||||
statusCode: number;
|
||||
success: false;
|
||||
error: 'invalid_request' | 'unsupported_response_type' | 'invalid_scope' | 'invalid_client';
|
||||
parameter: string;
|
||||
} = {},
|
||||
) {
|
||||
let userMessage: string | null = null;
|
||||
let userMessage: string | null = null;
|
||||
|
||||
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.';
|
||||
}
|
||||
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.';
|
||||
}
|
||||
|
||||
return userMessage
|
||||
? Promise.reject({ ...resp, userMessage })
|
||||
: Promise.reject(resp);
|
||||
return userMessage ? Promise.reject({ ...resp, userMessage }) : Promise.reject(resp);
|
||||
}
|
||||
|
||||
@@ -4,47 +4,43 @@ import request from 'app/services/request';
|
||||
import options from './options';
|
||||
|
||||
describe('services/api/options', () => {
|
||||
const expectedResp = {
|
||||
foo: 'bar',
|
||||
};
|
||||
const expectedResp = {
|
||||
foo: 'bar',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
sinon
|
||||
.stub(request, 'get')
|
||||
.named('request.get')
|
||||
.returns(
|
||||
Promise.resolve({
|
||||
originalResponse: new Response(),
|
||||
...expectedResp,
|
||||
}),
|
||||
);
|
||||
});
|
||||
beforeEach(() => {
|
||||
sinon
|
||||
.stub(request, 'get')
|
||||
.named('request.get')
|
||||
.returns(
|
||||
Promise.resolve({
|
||||
originalResponse: new Response(),
|
||||
...expectedResp,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(request.get as any).restore();
|
||||
});
|
||||
afterEach(() => {
|
||||
(request.get as any).restore();
|
||||
});
|
||||
|
||||
it('should request options without token', () =>
|
||||
options.get().then((resp) => {
|
||||
expect(resp, 'to satisfy', {
|
||||
...expectedResp,
|
||||
originalResponse: expect.it('to be a', Response),
|
||||
});
|
||||
expect(request.get, 'to have a call satisfying', [
|
||||
'/api/options',
|
||||
{},
|
||||
{ token: null },
|
||||
]);
|
||||
}));
|
||||
it('should request options without token', () =>
|
||||
options.get().then((resp) => {
|
||||
expect(resp, 'to satisfy', {
|
||||
...expectedResp,
|
||||
originalResponse: expect.it('to be a', Response),
|
||||
});
|
||||
expect(request.get, 'to have a call satisfying', ['/api/options', {}, { token: null }]);
|
||||
}));
|
||||
|
||||
it('should cache options', () =>
|
||||
// NOTE: this is bad practice, but we are relying on the state from
|
||||
// the previous test
|
||||
options.get().then((resp) => {
|
||||
expect(resp, 'to satisfy', {
|
||||
...expectedResp,
|
||||
originalResponse: expect.it('to be a', Response),
|
||||
});
|
||||
expect(request.get, 'was not called');
|
||||
}));
|
||||
it('should cache options', () =>
|
||||
// NOTE: this is bad practice, but we are relying on the state from
|
||||
// the previous test
|
||||
options.get().then((resp) => {
|
||||
expect(resp, 'to satisfy', {
|
||||
...expectedResp,
|
||||
originalResponse: expect.it('to be a', Response),
|
||||
});
|
||||
expect(request.get, 'was not called');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -5,19 +5,15 @@ type Options = { reCaptchaPublicKey: string };
|
||||
let options: Resp<Options>;
|
||||
|
||||
export default {
|
||||
async get(): Promise<Resp<Options>> {
|
||||
if (options) {
|
||||
return Promise.resolve(options);
|
||||
}
|
||||
async get(): Promise<Resp<Options>> {
|
||||
if (options) {
|
||||
return Promise.resolve(options);
|
||||
}
|
||||
|
||||
const resp = await request.get<Options>(
|
||||
'/api/options',
|
||||
{},
|
||||
{ token: null },
|
||||
);
|
||||
const resp = await request.get<Options>('/api/options', {}, { token: null });
|
||||
|
||||
options = resp;
|
||||
options = resp;
|
||||
|
||||
return resp;
|
||||
},
|
||||
return resp;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,35 +3,35 @@ import request, { Resp } from 'app/services/request';
|
||||
import { OAuthResponse } from './authentication';
|
||||
|
||||
interface RegisterParams {
|
||||
email?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
rePassword?: string;
|
||||
rulesAgreement?: boolean;
|
||||
lang?: string;
|
||||
captcha?: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
rePassword?: string;
|
||||
rulesAgreement?: boolean;
|
||||
lang?: string;
|
||||
captcha?: string;
|
||||
}
|
||||
|
||||
export function register({
|
||||
email = '',
|
||||
username = '',
|
||||
password = '',
|
||||
rePassword = '',
|
||||
rulesAgreement = false,
|
||||
lang = '',
|
||||
captcha = '',
|
||||
email = '',
|
||||
username = '',
|
||||
password = '',
|
||||
rePassword = '',
|
||||
rulesAgreement = false,
|
||||
lang = '',
|
||||
captcha = '',
|
||||
}: RegisterParams): Promise<Resp<void>> {
|
||||
return request.post(
|
||||
'/api/signup',
|
||||
{ email, username, password, rePassword, rulesAgreement, lang, captcha },
|
||||
{ token: null },
|
||||
);
|
||||
return request.post(
|
||||
'/api/signup',
|
||||
{ email, username, password, rePassword, rulesAgreement, lang, captcha },
|
||||
{ token: null },
|
||||
);
|
||||
}
|
||||
|
||||
export function activate(key: string = ''): Promise<Resp<OAuthResponse>> {
|
||||
return request.post('/api/signup/confirm', { key }, { token: null });
|
||||
return request.post('/api/signup/confirm', { key }, { token: null });
|
||||
}
|
||||
|
||||
export function resendActivation(email: string = '', captcha: string = '') {
|
||||
return request.post('/api/signup/repeat-message', { email, captcha });
|
||||
return request.post('/api/signup/repeat-message', { email, captcha });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user