Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: b043facad4b4aca7a013730746bdb9cb9e9dfca1e5d6faf11c068fc2525569c0
checksum: c37956b0e6ccd5267dac7fd6f4347fbb5ad41defe1c42d39aaaeaf64d2cf8605
- filename: .husky/pre-commit
checksum: 52a664f536cf5d1be0bea19cb6031ca6e8107b45b6314fe7d47b7fad7d800632
- filename: test/sanity-check/api/user-test.js
Expand All @@ -26,4 +26,8 @@ fileignoreconfig:
checksum: e8a32ffbbbdba2a15f3d327273f0a5b4eb33cf84cd346562596ab697125bbbc6
- filename: test/sanity-check/api/bulkOperation-test.js
checksum: f40a14c84ab9a194aaf830ca68e14afde2ef83496a07d4a6393d7e0bed15fb0e
version: "1.0"
- filename: lib/contentstackClient.js
checksum: b61fcf4fea88b8328ffa9d29a1ed88fe23ee9c9ef0cbd458721dcb7c82d2432b
- filename: test/unit/ContentstackClient-test.js
checksum: e712b74f37ea15afd2ac095fc2154f8c9f2ffe955659e77651da4a0d7079ec88
version: "1.0"
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## [v1.23.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.24.0) (2025-07-28)
## [v1.24.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.24.0) (2025-08-18)
- Feat
- Added Support for MFA

## [v1.23.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.23.1) (2025-07-28)
- Fix
- Add asset types support in bulk operations

Expand Down
79 changes: 55 additions & 24 deletions lib/contentstackClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cloneDeep from 'lodash/cloneDeep'
import { User } from './user/index'
import error from './core/contentstackError'
import OAuthHandler from './core/oauthHandler'
import { authenticator } from 'otplib'

export default function contentstackClient ({ http }) {
/**
Expand All @@ -16,7 +17,8 @@ export default function contentstackClient ({ http }) {
* @param {Object} parameters - login parameters
* @prop {string} parameters.email - email id for user to login
* @prop {string} parameters.password - password for user to login
* @prop {string} parameters.token - token for user to login
* @prop {string} parameters.token - token for user to login (2FA token)
* @prop {string} parameters.tfa_token - tfa token for user to login (2FA token)
* @returns {Promise}
* @example
* import * as contentstack from '@contentstack/management'
Expand All @@ -26,9 +28,28 @@ export default function contentstackClient ({ http }) {
* .then(() => console.log('Logged in successfully'))
*
*/
function login (requestBody, params = {}) {
/**
* Login function that supports both regular login and TOTP-based 2FA
* @param {Object} requestBody - Login credentials
* @param {string} requestBody.email - Email address
* @param {string} requestBody.password - Password
* @param {string} [requestBody.token] - 2FA token
* @param {string} [requestBody.tfa_token] - Alternative 2FA token
* @param {string} [requestBody.mfaSecret] - TOTP secret key for generating 2FA token
* @param {Object} params - Additional parameters
* @returns {Promise}
*/
function login (requestBody = {}, params = {}) {
http.defaults.versioningStrategy = 'path'

const { mfaSecret, ...credentials } = requestBody
requestBody = credentials

if (requestBody?.tfa_token) {
// tfa_token is already in credentials, no need to do anything
} else if (mfaSecret) {
requestBody.tfa_token = authenticator.generate(mfaSecret)
}
return http.post('/user-session', { user: requestBody }, { params: params })
.then((response) => {
if (response.data.user != null && response.data.user.authtoken != null) {
Expand All @@ -55,10 +76,9 @@ export default function contentstackClient ({ http }) {
*/
function getUser (params = {}) {
http.defaults.versioningStrategy = 'path'
return http.get('/user', { params: params })
.then((response) => {
return new User(http, response.data)
}, error)
return http.get('/user', { params: params }).then((response) => {
return new User(http, response.data)
}, error)
}
/**
* @description Get Stack instance. A stack is a space that stores the content of a project.
Expand Down Expand Up @@ -127,13 +147,16 @@ export default function contentstackClient ({ http }) {
*/
function organization (uid = null) {
http.defaults.versioningStrategy = 'path'
return new Organization(http, uid !== null ? { organization: { uid: uid } } : null)
return new Organization(
http,
uid !== null ? { organization: { uid: uid } } : null
)
}

/**
* @description The Log out of your account call is used to sign out the user of Contentstack account.
* @memberof ContentstackClient
* @param {String} authtoken - Authtoken to logout from.
* @param {String} authtoken - Authtoken to logout from.
* @func logout
* @returns {Object} Response object.
*
Expand All @@ -152,25 +175,25 @@ export default function contentstackClient ({ http }) {
function logout (authtoken) {
http.defaults.versioningStrategy = 'path'
if (authtoken !== undefined) {
return http.delete('/user-session', {
headers: {
authtoken: authtoken
}
})
return http
.delete('/user-session', {
headers: {
authtoken: authtoken
}
})
.then((response) => {
return response.data
}, error)
}
return http.delete('/user-session')
.then((response) => {
if (http.defaults.headers.common) {
delete http.defaults.headers.common.authtoken
}
delete http.defaults.headers.authtoken
delete http.httpClientParams.authtoken
delete http.httpClientParams.headers.authtoken
return response.data
}, error)
return http.delete('/user-session').then((response) => {
if (http.defaults.headers.common) {
delete http.defaults.headers.common.authtoken
}
delete http.defaults.headers.authtoken
delete http.httpClientParams.authtoken
delete http.httpClientParams.headers.authtoken
return response.data
}, error)
}

/**
Expand Down Expand Up @@ -201,7 +224,15 @@ export default function contentstackClient ({ http }) {
const responseType = params.responseType || 'code'
const scope = params.scope
const clientSecret = params.clientSecret
return new OAuthHandler(http, appId, clientId, redirectUri, clientSecret, responseType, scope)
return new OAuthHandler(
http,
appId,
clientId,
redirectUri,
clientSecret,
responseType,
scope
)
}

return {
Expand Down
1 change: 1 addition & 0 deletions lib/core/contentstackError.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function error (errorResponse) {
errorDetails.errorCode = data.error_code || 0
errorDetails.errors = data.errors || {}
errorDetails.error = data.error || ''
errorDetails.tfa_type = data.tfa_type
}

var error = new Error()
Expand Down
71 changes: 69 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/management",
"version": "1.23.2",
"version": "1.24.0",
"description": "The Content Management API is used to manage the content of your Contentstack account",
"main": "./dist/node/contentstack-management.js",
"browser": "./dist/web/contentstack-management.js",
Expand Down Expand Up @@ -58,6 +58,7 @@
"form-data": "^4.0.4",
"husky": "^9.1.7",
"lodash": "^4.17.21",
"otplib": "^12.0.1",
"qs": "^6.14.0",
"stream-browserify": "^3.0.0"
},
Expand Down
Loading
Loading