Skip to content

Commit 94a89e1

Browse files
committed
Feat: Added MFA support and version bump and lint issue fix
1 parent fddae88 commit 94a89e1

File tree

7 files changed

+362
-29
lines changed

7 files changed

+362
-29
lines changed

.talismanrc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fileignoreconfig:
99
ignore_detectors:
1010
- filecontent
1111
- filename: package-lock.json
12-
checksum: b043facad4b4aca7a013730746bdb9cb9e9dfca1e5d6faf11c068fc2525569c0
12+
checksum: c37956b0e6ccd5267dac7fd6f4347fbb5ad41defe1c42d39aaaeaf64d2cf8605
1313
- filename: .husky/pre-commit
1414
checksum: 52a664f536cf5d1be0bea19cb6031ca6e8107b45b6314fe7d47b7fad7d800632
1515
- filename: test/sanity-check/api/user-test.js
@@ -26,4 +26,8 @@ fileignoreconfig:
2626
checksum: e8a32ffbbbdba2a15f3d327273f0a5b4eb33cf84cd346562596ab697125bbbc6
2727
- filename: test/sanity-check/api/bulkOperation-test.js
2828
checksum: f40a14c84ab9a194aaf830ca68e14afde2ef83496a07d4a6393d7e0bed15fb0e
29-
version: "1.0"
29+
- filename: lib/contentstackClient.js
30+
checksum: b61fcf4fea88b8328ffa9d29a1ed88fe23ee9c9ef0cbd458721dcb7c82d2432b
31+
- filename: test/unit/ContentstackClient-test.js
32+
checksum: 210e75d6620fcae59ca9db6ecab6101736e8227f5a557a111ac72176056a5769
33+
version: "1.0"

lib/contentstackClient.js

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cloneDeep from 'lodash/cloneDeep'
77
import { User } from './user/index'
88
import error from './core/contentstackError'
99
import OAuthHandler from './core/oauthHandler'
10+
import { authenticator } from 'otplib'
1011

1112
export default function contentstackClient ({ http }) {
1213
/**
@@ -16,7 +17,8 @@ export default function contentstackClient ({ http }) {
1617
* @param {Object} parameters - login parameters
1718
* @prop {string} parameters.email - email id for user to login
1819
* @prop {string} parameters.password - password for user to login
19-
* @prop {string} parameters.token - token for user to login
20+
* @prop {string} parameters.token - token for user to login (2FA token)
21+
* @prop {string} parameters.tfa_token - tfa token for user to login (2FA token)
2022
* @returns {Promise}
2123
* @example
2224
* import * as contentstack from '@contentstack/management'
@@ -26,9 +28,28 @@ export default function contentstackClient ({ http }) {
2628
* .then(() => console.log('Logged in successfully'))
2729
*
2830
*/
29-
function login (requestBody, params = {}) {
31+
/**
32+
* Login function that supports both regular login and TOTP-based 2FA
33+
* @param {Object} requestBody - Login credentials
34+
* @param {string} requestBody.email - Email address
35+
* @param {string} requestBody.password - Password
36+
* @param {string} [requestBody.token] - 2FA token
37+
* @param {string} [requestBody.tfa_token] - Alternative 2FA token
38+
* @param {string} [requestBody.mfaSecret] - TOTP secret key for generating 2FA token
39+
* @param {Object} params - Additional parameters
40+
* @returns {Promise}
41+
*/
42+
function login (requestBody = {}, params = {}) {
3043
http.defaults.versioningStrategy = 'path'
3144

45+
const { mfaSecret, ...credentials } = requestBody
46+
requestBody = credentials
47+
48+
if (requestBody?.tfa_token) {
49+
// tfa_token is already in credentials, no need to do anything
50+
} else if (mfaSecret) {
51+
requestBody.tfa_token = authenticator.generate(mfaSecret)
52+
}
3253
return http.post('/user-session', { user: requestBody }, { params: params })
3354
.then((response) => {
3455
if (response.data.user != null && response.data.user.authtoken != null) {
@@ -55,10 +76,9 @@ export default function contentstackClient ({ http }) {
5576
*/
5677
function getUser (params = {}) {
5778
http.defaults.versioningStrategy = 'path'
58-
return http.get('/user', { params: params })
59-
.then((response) => {
60-
return new User(http, response.data)
61-
}, error)
79+
return http.get('/user', { params: params }).then((response) => {
80+
return new User(http, response.data)
81+
}, error)
6282
}
6383
/**
6484
* @description Get Stack instance. A stack is a space that stores the content of a project.
@@ -127,13 +147,16 @@ export default function contentstackClient ({ http }) {
127147
*/
128148
function organization (uid = null) {
129149
http.defaults.versioningStrategy = 'path'
130-
return new Organization(http, uid !== null ? { organization: { uid: uid } } : null)
150+
return new Organization(
151+
http,
152+
uid !== null ? { organization: { uid: uid } } : null
153+
)
131154
}
132155

133156
/**
134157
* @description The Log out of your account call is used to sign out the user of Contentstack account.
135158
* @memberof ContentstackClient
136-
* @param {String} authtoken - Authtoken to logout from.
159+
* @param {String} authtoken - Authtoken to logout from.
137160
* @func logout
138161
* @returns {Object} Response object.
139162
*
@@ -152,25 +175,25 @@ export default function contentstackClient ({ http }) {
152175
function logout (authtoken) {
153176
http.defaults.versioningStrategy = 'path'
154177
if (authtoken !== undefined) {
155-
return http.delete('/user-session', {
156-
headers: {
157-
authtoken: authtoken
158-
}
159-
})
178+
return http
179+
.delete('/user-session', {
180+
headers: {
181+
authtoken: authtoken
182+
}
183+
})
160184
.then((response) => {
161185
return response.data
162186
}, error)
163187
}
164-
return http.delete('/user-session')
165-
.then((response) => {
166-
if (http.defaults.headers.common) {
167-
delete http.defaults.headers.common.authtoken
168-
}
169-
delete http.defaults.headers.authtoken
170-
delete http.httpClientParams.authtoken
171-
delete http.httpClientParams.headers.authtoken
172-
return response.data
173-
}, error)
188+
return http.delete('/user-session').then((response) => {
189+
if (http.defaults.headers.common) {
190+
delete http.defaults.headers.common.authtoken
191+
}
192+
delete http.defaults.headers.authtoken
193+
delete http.httpClientParams.authtoken
194+
delete http.httpClientParams.headers.authtoken
195+
return response.data
196+
}, error)
174197
}
175198

176199
/**
@@ -201,7 +224,15 @@ export default function contentstackClient ({ http }) {
201224
const responseType = params.responseType || 'code'
202225
const scope = params.scope
203226
const clientSecret = params.clientSecret
204-
return new OAuthHandler(http, appId, clientId, redirectUri, clientSecret, responseType, scope)
227+
return new OAuthHandler(
228+
http,
229+
appId,
230+
clientId,
231+
redirectUri,
232+
clientSecret,
233+
responseType,
234+
scope
235+
)
205236
}
206237

207238
return {

lib/core/contentstackError.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default function error (errorResponse) {
3434
errorDetails.errorCode = data.error_code || 0
3535
errorDetails.errors = data.errors || {}
3636
errorDetails.error = data.error || ''
37+
errorDetails.tfa_type = data.tfa_type
3738
}
3839

3940
var error = new Error()

package-lock.json

Lines changed: 69 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/management",
3-
"version": "1.23.2",
3+
"version": "1.24.0",
44
"description": "The Content Management API is used to manage the content of your Contentstack account",
55
"main": "./dist/node/contentstack-management.js",
66
"browser": "./dist/web/contentstack-management.js",
@@ -58,6 +58,7 @@
5858
"form-data": "^4.0.4",
5959
"husky": "^9.1.7",
6060
"lodash": "^4.17.21",
61+
"otplib": "^12.0.1",
6162
"qs": "^6.14.0",
6263
"stream-browserify": "^3.0.0"
6364
},

0 commit comments

Comments
 (0)