diff --git a/api/mailinabox.yml b/api/mailinabox.yml index 57ba5aa4..27c248da 100644 --- a/api/mailinabox.yml +++ b/api/mailinabox.yml @@ -8,7 +8,7 @@ info: This API is documented in [**OpenAPI format**](http://spec.openapis.org/oas/v3.0.3). ([View the full HTTP specification](https://raw.githubusercontent.com/mail-in-a-box/mailinabox/api-spec/api/mailinabox.yml).) - All endpoints are relative to `https://{host}/admin` and are secured with [`Basic Access` authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). + All endpoints are relative to `https://{host}/admin` and are secured with [`Basic Access` authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). If you have multi-factor authentication enabled, authentication with a `user:password` combination will fail unless a valid OTP is supplied via the `x-auth-token` header. Authentication via a `user:user_key` pair is possible without the header being present. contact: name: Mail-in-a-Box support url: https://mailinabox.email/ @@ -46,6 +46,9 @@ tags: - name: Web description: | Static web hosting operations, which include getting domain information and updating domain root directories. + - name: MFA + description: | + Manage multi-factor authentication schemes. Currently, only TOTP is supported. - name: System description: | System operations, which include system status checks, new version checks @@ -1662,6 +1665,95 @@ paths: text/html: schema: type: string + /mfa/status: + get: + tags: + - MFA + summary: Retrieve MFA status + description: Retrieves which type of MFA is used and configuration + operationId: mfaStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mfa/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MfaStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mfa/totp/enable: + post: + tags: + - MFA + summary: Enable TOTP authentication + description: Enables TOTP authentication for the currently logged-in admin user + operationId: mfaTotpEnable + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mfa/totp/enable" \ + -d "code=123456" \ + -d "secret=" \ + -u ":" + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MfaEnableRequest' + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MfaEnableSuccessResponse' + 400: + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/MfaEnableBadRequestResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mfa/totp/disable: + post: + tags: + - MFA + summary: Disable TOTP authentication + description: Disable TOTP authentication for the currently logged-in admin user + operationId: mfaTotpDisable + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mfa/totp/disable" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MfaDisableSuccessResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string components: securitySchemes: basicAuth: @@ -2529,3 +2621,37 @@ components: type: string example: web updated description: Web update response. + MfaStatusResponse: + type: object + properties: + type: + type: string + example: totp + nullable: true + totp_secret: + type: string + nullable: true + totp_qr: + type: string + nullable: true + MfaEnableRequest: + type: object + required: + - secret + - code + properties: + secret: + type: string + code: + type: string + MfaEnableSuccessResponse: + type: object + MfaEnableBadRequestResponse: + type: object + required: + - error + properties: + error: + type: string + MfaDisableSuccessResponse: + type: object \ No newline at end of file diff --git a/management/daemon.py b/management/daemon.py index 75e438d5..55c0a3ec 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -412,7 +412,7 @@ def ssl_provision_certs(): # Two Factor Auth -@app.route('/2fa/status', methods=['GET']) +@app.route('/mfa/status', methods=['GET']) @authorized_personnel_only def two_factor_auth_get_status(): email, _ = auth_service.authenticate(request, env) @@ -433,11 +433,12 @@ def two_factor_auth_get_status(): "totp_qr": secret_qr }) -@app.route('/2fa/totp/enable', methods=['POST']) +@app.route('/mfa/totp/enable', methods=['POST']) @authorized_personnel_only def totp_post_enable(): email, _ = auth_service.authenticate(request, env) + # TODO: Handle case where user already has TOTP enabled secret = request.form.get('secret') token = request.form.get('token') @@ -450,7 +451,7 @@ def totp_post_enable(): return json_response({ "error": 'token_mismatch' }, 400) -@app.route('/2fa/totp/disable', methods=['POST']) +@app.route('/mfa/totp/disable', methods=['POST']) @authorized_personnel_only def totp_post_disable(): email, _ = auth_service.authenticate(request, env) diff --git a/management/templates/two-factor-auth.html b/management/templates/two-factor-auth.html index 810e600e..6a6b6c31 100644 --- a/management/templates/two-factor-auth.html +++ b/management/templates/two-factor-auth.html @@ -154,7 +154,7 @@ reset_view(); api( - '/2fa/status', + '/mfa/status', 'GET', {}, function(res) { @@ -170,7 +170,7 @@ hide_error(); api( - '/2fa/totp/disable', + '/mfa/totp/disable', 'POST', {}, function() { show_two_factor_auth(); } @@ -184,7 +184,7 @@ hide_error(); api( - '/2fa/totp/enable', + '/mfa/totp/enable', 'POST', { token: $(el.totpSetupToken).val(),