Skip to content

Commit

Permalink
fix(api-2fa): Added 2FA API endpoints to API docs generation ZMS-124 (#…
Browse files Browse the repository at this point in the history
…626)

* added Generate TOTP seed api endpoint to API generation

* added Enable TOTP api endpoint to API docs generation

* added Disable TOTP auth api endpoint to API docs generation

* added Validate TOTP token api endpoint to API docs generation

* added Disable 2FA api endpoint to API docs generation

* Added Enable custom 2FA for a user api endpoint for API docs generation

* Disable custom 2FA for a user endpoint added to API docs generation. Fix imports

* added Get WebAuthN credentials for a user api endpoint to api docs generation

* WebAuthN del and registration endpoints added to API docs generation

* webAuthN authentication challenge and attestation endpoints added to API docs generation

* fix rpId descriptions

* add response objects to endpoints
  • Loading branch information
NickOvt authored Feb 26, 2024
1 parent 0e987de commit 0efae19
Show file tree
Hide file tree
Showing 3 changed files with 486 additions and 128 deletions.
54 changes: 44 additions & 10 deletions lib/api/2fa/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,38 @@ const ObjectId = require('mongodb').ObjectId;
const tools = require('../../tools');
const roles = require('../../roles');
const { sessSchema, sessIPSchema } = require('../../schemas');
const { userId } = require('../../schemas/request/general-schemas');
const { successRes } = require('../../schemas/response/general-schemas');

// Custom 2FA needs to be enabled if your website handles its own 2FA and you want to disable
// master password usage for IMAP/POP/SMTP clients

module.exports = (db, server, userHandler) => {
server.put(
'/users/:user/2fa/custom',
{
path: '/users/:user/2fa/custom',
tags: ['TwoFactorAuth'],
summary: 'Enable custom 2FA for a user',
description: 'This method disables account password for IMAP/POP3/SMTP',
validationObjs: {
requestBody: {
sess: sessSchema,
ip: sessIPSchema
},
queryParams: {},
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -52,14 +70,30 @@ module.exports = (db, server, userHandler) => {
);

server.del(
'/users/:user/2fa/custom',
{
path: '/users/:user/2fa/custom',
tags: ['TwoFactorAuth'],
summary: 'Disable custom 2FA for a user',
description: 'This method disables custom 2FA. If it was the only 2FA set up, then account password for IMAP/POP3/SMTP gets enabled again',
validationObjs: {
requestBody: {},
queryParams: {
sess: sessSchema,
ip: sessIPSchema
},
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down
147 changes: 118 additions & 29 deletions lib/api/2fa/totp.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,48 @@ const ObjectId = require('mongodb').ObjectId;
const tools = require('../../tools');
const roles = require('../../roles');
const { sessSchema, sessIPSchema } = require('../../schemas');
const { userId } = require('../../schemas/request/general-schemas');
const { successRes } = require('../../schemas/response/general-schemas');

module.exports = (db, server, userHandler) => {
// Create TOTP seed and request a QR code

server.post(
'/users/:user/2fa/totp/setup',
{
path: '/users/:user/2fa/totp/setup',
tags: ['TwoFactorAuth'],
summary: 'Generate TOTP seed',
description: 'This method generates TOTP seed and QR code for 2FA. User needs to verify the seed value using 2fa/totp/enable endpoint',
validationObjs: {
requestBody: {
label: Joi.string().empty('').trim().max(255).description('Label text for QR code (defaults to username)'),
issuer: Joi.string().trim().max(255).required().description('Description text for QR code (defaults to "WildDuck")'),
sess: sessSchema,
ip: sessIPSchema
},
queryParams: {},
pathParams: { user: userId },
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
seed: Joi.string().required().description('Generated TOTP seed value'),
qrcode: Joi.string().required().description('Base64 encoded QR code')
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
label: Joi.string().empty('').trim().max(255),
issuer: Joi.string().trim().max(255).required(),
sess: sessSchema,
ip: sessIPSchema

const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -54,15 +82,31 @@ module.exports = (db, server, userHandler) => {
);

server.post(
'/users/:user/2fa/totp/enable',
{
path: '/users/:user/2fa/totp/enable',
tags: ['TwoFactorAuth'],
summary: 'Enable TOTP seed',
description: 'This method enables TOTP for a user by verifying the seed value generated from 2fa/totp/setup',
validationObjs: {
requestBody: {
token: Joi.string().length(6).required().description('6-digit number that matches seed value from 2fa/totp/setup'),
sess: sessSchema,
ip: sessIPSchema
},
queryParams: {},
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
token: Joi.string().length(6).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -113,14 +157,27 @@ module.exports = (db, server, userHandler) => {
);

server.del(
'/users/:user/2fa/totp',
{
path: '/users/:user/2fa/totp',
tags: ['TwoFactorAuth'],
summary: 'Disable TOTP auth',
description: 'This method disables TOTP for a user. Does not affect other 2FA mechanisms a user might have set up',
validationObjs: {
requestBody: {},
queryParams: { sess: sessSchema, ip: sessIPSchema },
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -154,15 +211,31 @@ module.exports = (db, server, userHandler) => {
);

server.post(
'/users/:user/2fa/totp/check',
{
path: '/users/:user/2fa/totp/check',
tags: ['TwoFactorAuth'],
summary: 'Validate TOTP Token',
description: 'This method checks if a TOTP token provided by a User is valid for authentication',
validationObjs: {
requestBody: {
token: Joi.string().length(6).required().description('6-digit number'),
sess: sessSchema,
ip: sessIPSchema
},
queryParams: {},
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
token: Joi.string().length(6).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -204,14 +277,30 @@ module.exports = (db, server, userHandler) => {
);

server.del(
'/users/:user/2fa',
{
path: '/users/:user/2fa',
tags: ['TwoFactorAuth'],
summary: 'Disable 2FA',
description: 'This method disables all 2FA mechanisms a user might have set up',
validationObjs: {
requestBody: {},
queryParams: {
sess: sessSchema,
ip: sessIPSchema
},
pathParams: { user: userId },
response: { 200: { description: 'Success', model: Joi.object({ success: successRes }) } }
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down
Loading

0 comments on commit 0efae19

Please sign in to comment.