Skip to content

Commit d26eed6

Browse files
Merge pull request #8586 from BitGo/WCI-186
refactor(express): split login endpoint into V1 and V2
2 parents 1eeb320 + de9a2ca commit d26eed6

6 files changed

Lines changed: 87 additions & 24 deletions

File tree

modules/express/src/clientRoutes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function handlePingExpress(req: ExpressApiRouteRequest<'express.v1.pingexpress'
8989
};
9090
}
9191

92-
function handleLogin(req: ExpressApiRouteRequest<'express.login', 'post'>) {
92+
function handleLogin(req: ExpressApiRouteRequest<'express.v1.login' | 'express.login', 'post'>) {
9393
const username = req.decoded.username || req.decoded.email;
9494
const body = req.body;
9595
body.username = username;
@@ -1700,6 +1700,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
17001700
router.get('express.pingexpress', [typedPromiseWrapper(handlePingExpress)]);
17011701

17021702
// auth
1703+
router.post('express.v1.login', [prepareBitGo(config), typedPromiseWrapper(handleLogin)]);
17031704
router.post('express.login', [prepareBitGo(config), typedPromiseWrapper(handleLogin)]);
17041705

17051706
router.post('express.v1.decrypt', [prepareBitGo(config), typedPromiseWrapper(handleDecrypt)]);

modules/express/src/typedRoutes/api/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { GetV1Ping } from './v1/ping';
66
import { GetV2Ping } from './v2/ping';
77
import { GetV1PingExpress } from './v1/pingExpress';
88
import { GetV2PingExpress } from './v2/pingExpress';
9-
import { PostLogin } from './common/login';
9+
import { PostV1Login } from './v1/login';
10+
import { PostV2Login } from './v2/login';
1011
import { PostV1Decrypt } from './v1/decrypt';
1112
import { PostV2Decrypt } from './v2/decrypt';
1213
import { PostV1Encrypt } from './v1/encrypt';
@@ -86,8 +87,11 @@ export const ExpressPingExpressApiSpec = apiSpec({
8687
});
8788

8889
export const ExpressLoginApiSpec = apiSpec({
90+
'express.v1.login': {
91+
post: PostV1Login,
92+
},
8993
'express.login': {
90-
post: PostLogin,
94+
post: PostV2Login,
9195
},
9296
});
9397

modules/express/src/typedRoutes/api/common/login.ts renamed to modules/express/src/typedRoutes/api/v1/login.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@ export const LoginRequest = {
3939
};
4040

4141
/**
42-
* Login
42+
* Login (Express v1)
4343
*
44-
* @operationId express.login
45-
* @tag express
44+
* Authenticate a user and retrieve their session details using BitGo Express.
45+
*
46+
* @operationId express.v1.login
47+
* @tag Express
48+
* @private
4649
*/
47-
export const PostLogin = httpRoute({
48-
path: '/api/v[12]/user/login',
50+
export const PostV1Login = httpRoute({
51+
path: '/api/v1/user/login',
4952
method: 'POST',
5053
request: httpRequest({
5154
body: LoginRequest,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as t from 'io-ts';
2+
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
3+
import { BitgoExpressError } from '../../schemas/error';
4+
import { LoginRequest } from '../v1/login';
5+
6+
/**
7+
* Login (Express)
8+
*
9+
* Authenticate a user and retrieve their session details using BitGo Express.
10+
*
11+
* @operationId express.login
12+
* @tag Express
13+
* @public
14+
*/
15+
export const PostV2Login = httpRoute({
16+
path: '/api/v2/user/login',
17+
method: 'POST',
18+
request: httpRequest({
19+
body: LoginRequest,
20+
}),
21+
response: {
22+
200: t.type({
23+
email: t.string,
24+
password: t.string,
25+
forceSMS: t.boolean,
26+
otp: optional(t.string),
27+
trust: optional(t.number),
28+
extensible: optional(t.boolean),
29+
extensionAddress: optional(t.string),
30+
forceV1Auth: optional(t.boolean),
31+
forReset2FA: optional(t.boolean),
32+
initialHash: optional(t.string),
33+
fingerprintHash: optional(t.string),
34+
}),
35+
404: BitgoExpressError,
36+
},
37+
});

modules/express/test/unit/typedRoutes/decode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as assert from 'assert';
22
import * as t from 'io-ts';
33
import { DecryptRequestBody } from '../../../src/typedRoutes/api/v1/decrypt';
44
import { EncryptRequestBody } from '../../../src/typedRoutes/api/v1/encrypt';
5-
import { LoginRequest } from '../../../src/typedRoutes/api/common/login';
5+
import { LoginRequest } from '../../../src/typedRoutes/api/v1/login';
66
import { VerifyAddressBody } from '../../../src/typedRoutes/api/v1/verifyAddress';
77
import { VerifyAddressV2Body, VerifyAddressV2Params } from '../../../src/typedRoutes/api/v2/verifyCoinAddress';
88
import { SimpleCreateRequestBody } from '../../../src/typedRoutes/api/v1/simpleCreate';

modules/express/test/unit/typedRoutes/userLogin.ts

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as assert from 'assert';
22
import * as t from 'io-ts';
3-
import { LoginRequest, PostLogin } from '../../../src/typedRoutes/api/common/login';
3+
import { LoginRequest, PostV1Login } from '../../../src/typedRoutes/api/v1/login';
4+
import { PostV2Login } from '../../../src/typedRoutes/api/v2/login';
45
import { assertDecode } from './common';
56
import 'should';
67
import 'should-http';
@@ -130,7 +131,7 @@ describe('Login codec tests', function () {
130131
});
131132

132133
describe('LoginResponse', function () {
133-
const LoginResponse = PostLogin.response[200];
134+
const LoginResponse = PostV1Login.response[200];
134135

135136
it('should validate response with all required fields', function () {
136137
const validResponse = {
@@ -245,24 +246,41 @@ describe('Login codec tests', function () {
245246
});
246247
});
247248

248-
describe('PostLogin route definition', function () {
249+
describe('PostV1Login route definition', function () {
249250
it('should have the correct path', function () {
250-
assert.strictEqual(PostLogin.path, '/api/v[12]/user/login');
251+
assert.strictEqual(PostV1Login.path, '/api/v1/user/login');
251252
});
252253

253254
it('should have the correct HTTP method', function () {
254-
assert.strictEqual(PostLogin.method, 'POST');
255+
assert.strictEqual(PostV1Login.method, 'POST');
255256
});
256257

257258
it('should have the correct request configuration', function () {
258-
// Verify the route is configured with a request property
259-
assert.ok(PostLogin.request);
259+
assert.ok(PostV1Login.request);
260260
});
261261

262262
it('should have the correct response types', function () {
263-
// Check that the response object has the expected status codes
264-
assert.ok(PostLogin.response[200]);
265-
assert.ok(PostLogin.response[404]);
263+
assert.ok(PostV1Login.response[200]);
264+
assert.ok(PostV1Login.response[404]);
265+
});
266+
});
267+
268+
describe('PostV2Login route definition', function () {
269+
it('should have the correct path', function () {
270+
assert.strictEqual(PostV2Login.path, '/api/v2/user/login');
271+
});
272+
273+
it('should have the correct HTTP method', function () {
274+
assert.strictEqual(PostV2Login.method, 'POST');
275+
});
276+
277+
it('should have the correct request configuration', function () {
278+
assert.ok(PostV2Login.request);
279+
});
280+
281+
it('should have the correct response types', function () {
282+
assert.ok(PostV2Login.response[200]);
283+
assert.ok(PostV2Login.response[404]);
266284
});
267285
});
268286

@@ -304,7 +322,7 @@ describe('Login codec tests', function () {
304322
assert.strictEqual(result.body.email, mockLoginResponse.email);
305323
assert.strictEqual(result.body.forceSMS, mockLoginResponse.forceSMS);
306324

307-
const decodedResponse = assertDecode(PostLogin.response[200], result.body);
325+
const decodedResponse = assertDecode(PostV1Login.response[200], result.body);
308326
assert.strictEqual(decodedResponse.email, mockLoginResponse.email);
309327
});
310328

@@ -329,7 +347,7 @@ describe('Login codec tests', function () {
329347
assert.strictEqual(result.body.email, mockLoginResponse.email);
330348
assert.strictEqual(result.body.forceSMS, mockLoginResponse.forceSMS);
331349

332-
const decodedResponse = assertDecode(PostLogin.response[200], result.body);
350+
const decodedResponse = assertDecode(PostV2Login.response[200], result.body);
333351
assert.strictEqual(decodedResponse.email, mockLoginResponse.email);
334352
});
335353

@@ -375,7 +393,7 @@ describe('Login codec tests', function () {
375393
result.body.should.have.property('forceSMS');
376394
result.body.should.have.property('extensible');
377395

378-
const decodedResponse = assertDecode(PostLogin.response[200], result.body);
396+
const decodedResponse = assertDecode(PostV1Login.response[200], result.body);
379397
assert.strictEqual(decodedResponse.email, mockFullResponse.email);
380398
assert.strictEqual(decodedResponse.extensible, mockFullResponse.extensible);
381399
assert.strictEqual(decodedResponse.initialHash, mockFullResponse.initialHash);
@@ -423,7 +441,7 @@ describe('Login codec tests', function () {
423441
result.body.should.have.property('forceSMS');
424442
result.body.should.have.property('extensible');
425443

426-
const decodedResponse = assertDecode(PostLogin.response[200], result.body);
444+
const decodedResponse = assertDecode(PostV2Login.response[200], result.body);
427445
assert.strictEqual(decodedResponse.email, mockFullResponse.email);
428446
assert.strictEqual(decodedResponse.extensible, mockFullResponse.extensible);
429447
assert.strictEqual(decodedResponse.initialHash, mockFullResponse.initialHash);
@@ -447,7 +465,7 @@ describe('Login codec tests', function () {
447465
result.body.should.have.property('email');
448466
result.body.should.have.property('forceSMS');
449467

450-
const decodedResponse = assertDecode(PostLogin.response[200], result.body);
468+
const decodedResponse = assertDecode(PostV2Login.response[200], result.body);
451469
assert.ok(decodedResponse);
452470
});
453471
});

0 commit comments

Comments
 (0)