diff --git a/.travis.yml b/.travis.yml index 0b8fecdb1..27105ca44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ addons: - mx1.mail.example.com - mx2.mail.example.com - public.example.com + - authelia.example.com before_install: - npm install -g npm@'>=2.13.5' diff --git a/README.md b/README.md index 682b5ce3b..b70d69a71 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [![Build](https://travis-ci.org/clems4ever/authelia.svg?branch=master)](https://travis-ci.org/clems4ever/authelia) [![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/authelia/general?utm_source=share-link&utm_medium=link&utm_campaign=share-link) -**Authelia** is a complete HTTP 2-factor authentication server for proxies like -nginx. It has been made to work with nginx [auth_request] module and is currently -used in production to secure internal services in a small docker swarm cluster. +**Authelia** is a complete HTTP 2-factor authentication server for proxies like +Nginx or Traefik. It has been designed to be proxy agnostic so that you can +use whichever proxy supporting authentication forwarding. # Table of Contents 1. [Features summary](#features-summary) diff --git a/doc/api_data.js b/doc/api_data.js index 2eacc2aaf..7aba8ada3 100644 --- a/doc/api_data.js +++ b/doc/api_data.js @@ -20,7 +20,7 @@ define({ "api": [ } }, "description": "
Serves the login page and create a create a cookie for the client.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -56,7 +56,7 @@ define({ "api": [ } }, "description": "Log out the user and redirect to the URL.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -80,7 +80,7 @@ define({ "api": [ } }, "description": "Serves the second factor page
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -145,7 +145,7 @@ define({ "api": [ } }, "description": "Verify credentials against the LDAP.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication", "header": { "fields": { @@ -169,7 +169,7 @@ define({ "api": [ "group": "PasswordReset", "version": "1.0.0", "description": "Start password reset request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -240,7 +240,7 @@ define({ "api": [ "group": "PasswordReset", "version": "1.0.0", "description": "Serve a page that requires the username.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -277,7 +277,7 @@ define({ "api": [ } }, "description": "Set a new password for the user.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -301,7 +301,7 @@ define({ "api": [ "group": "PasswordReset", "version": "1.0.0", "description": "Start password reset request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -366,7 +366,7 @@ define({ "api": [ "group": "TOTP", "version": "1.0.0", "description": "Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -437,7 +437,7 @@ define({ "api": [ "group": "TOTP", "version": "1.0.0", "description": "Initiates the identity validation
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -521,7 +521,7 @@ define({ "api": [ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } @@ -549,7 +549,7 @@ define({ "api": [ } }, "description": "Verify TOTP token. The user is authenticated upon success.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -579,7 +579,7 @@ define({ "api": [ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } @@ -607,7 +607,7 @@ define({ "api": [ } }, "description": "Complete authentication request of the U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -637,13 +637,13 @@ define({ "api": [ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } }, "description": "Complete U2F registration request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -679,7 +679,7 @@ define({ "api": [ "name": "RequestU2FRegistration", "group": "U2F", "version": "1.0.0", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -745,7 +745,7 @@ define({ "api": [ "group": "U2F", "version": "1.0.0", "description": "Serves the U2F registration page that asks the user to touch the token of the U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -850,7 +850,7 @@ define({ "api": [ } }, "description": "Initiate an authentication request using a U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -908,7 +908,7 @@ define({ "api": [ } }, "description": "Initiate a U2F device registration request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -926,11 +926,24 @@ define({ "api": [ }, { "type": "get", - "url": "/verify", + "url": "/api/verify", "title": "Verify user authentication", "name": "VerifyAuthentication", "group": "Verification", "version": "1.0.0", + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "redirect", + "description": "Optional parameter set to the url where the user is redirected if access is refused. It is mainly used by Traefik that does not control the redirection itself.
" + } + ] + } + }, "success": { "fields": { "Success 204": [ @@ -945,18 +958,26 @@ define({ "api": [ }, "error": { "fields": { + "Error 302": [ + { + "group": "Error 302", + "optional": false, + "field": "redirect", + "description": "The user is redirected if redirect parameter is provided.
" + } + ], "Error 401": [ { "group": "Error 401", "optional": false, "field": "status", - "description": "The user is not authenticated.
" + "description": "The user get an error if access failed
" } ] } }, - "description": "Verify that the user is authenticated, i.e., the two factors have been validated
", - "filename": "src/server/endpoints.ts", + "description": "Verify that the user is authenticated, i.e., the two factors have been validated. If the user is authenticated the response headers Remote-User and Remote-Groups are set. Remote-User contains the user id of the currently logged in user and Remote-Groups a comma separated list of assigned groups.
", + "filename": "shared/api.ts", "groupTitle": "Verification", "header": { "fields": { diff --git a/doc/api_data.json b/doc/api_data.json index 527b67508..c6935ffe7 100644 --- a/doc/api_data.json +++ b/doc/api_data.json @@ -20,7 +20,7 @@ } }, "description": "Serves the login page and create a create a cookie for the client.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -56,7 +56,7 @@ } }, "description": "Log out the user and redirect to the URL.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -80,7 +80,7 @@ } }, "description": "Serves the second factor page
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication" }, { @@ -145,7 +145,7 @@ } }, "description": "Verify credentials against the LDAP.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "Authentication", "header": { "fields": { @@ -169,7 +169,7 @@ "group": "PasswordReset", "version": "1.0.0", "description": "Start password reset request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -240,7 +240,7 @@ "group": "PasswordReset", "version": "1.0.0", "description": "Serve a page that requires the username.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -277,7 +277,7 @@ } }, "description": "Set a new password for the user.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -301,7 +301,7 @@ "group": "PasswordReset", "version": "1.0.0", "description": "Start password reset request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "PasswordReset", "header": { "fields": { @@ -366,7 +366,7 @@ "group": "TOTP", "version": "1.0.0", "description": "Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -437,7 +437,7 @@ "group": "TOTP", "version": "1.0.0", "description": "Initiates the identity validation
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -521,7 +521,7 @@ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } @@ -549,7 +549,7 @@ } }, "description": "Verify TOTP token. The user is authenticated upon success.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "TOTP", "header": { "fields": { @@ -579,7 +579,7 @@ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } @@ -607,7 +607,7 @@ } }, "description": "Complete authentication request of the U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -637,13 +637,13 @@ "group": "Success 302", "optional": false, "field": "Redirect", - "description": "to the URL that has been stored during last call to /verify.
" + "description": "to the URL that has been stored during last call to /api/verify.
" } ] } }, "description": "Complete U2F registration request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -679,7 +679,7 @@ "name": "RequestU2FRegistration", "group": "U2F", "version": "1.0.0", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -745,7 +745,7 @@ "group": "U2F", "version": "1.0.0", "description": "Serves the U2F registration page that asks the user to touch the token of the U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -850,7 +850,7 @@ } }, "description": "Initiate an authentication request using a U2F device.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -908,7 +908,7 @@ } }, "description": "Initiate a U2F device registration request.
", - "filename": "src/server/endpoints.ts", + "filename": "shared/api.ts", "groupTitle": "U2F", "header": { "fields": { @@ -926,11 +926,24 @@ }, { "type": "get", - "url": "/verify", + "url": "/api/verify", "title": "Verify user authentication", "name": "VerifyAuthentication", "group": "Verification", "version": "1.0.0", + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "redirect", + "description": "Optional parameter set to the url where the user is redirected if access is refused. It is mainly used by Traefik that does not control the redirection itself.
" + } + ] + } + }, "success": { "fields": { "Success 204": [ @@ -945,18 +958,26 @@ }, "error": { "fields": { + "Error 302": [ + { + "group": "Error 302", + "optional": false, + "field": "redirect", + "description": "The user is redirected if redirect parameter is provided.
" + } + ], "Error 401": [ { "group": "Error 401", "optional": false, "field": "status", - "description": "The user is not authenticated.
" + "description": "The user get an error if access failed
" } ] } }, - "description": "Verify that the user is authenticated, i.e., the two factors have been validated
", - "filename": "src/server/endpoints.ts", + "description": "Verify that the user is authenticated, i.e., the two factors have been validated. If the user is authenticated the response headers Remote-User and Remote-Groups are set. Remote-User contains the user id of the currently logged in user and Remote-Groups a comma separated list of assigned groups.
", + "filename": "shared/api.ts", "groupTitle": "Verification", "header": { "fields": { diff --git a/doc/api_project.js b/doc/api_project.js index 9b4ecf09d..727258a09 100644 --- a/doc/api_project.js +++ b/doc/api_project.js @@ -1,14 +1,14 @@ define({ "title": "Authelia API documentation", "name": "authelia", - "version": "2.1.3", + "version": "3.7.0", "description": "2FA Single Sign-On server for nginx using LDAP, TOTP and U2F", "sampleUrl": false, "defaultVersion": "0.0.0", "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2017-06-11T20:41:36.025Z", + "time": "2017-12-04T21:38:44.927Z", "url": "http://apidocjs.com", "version": "0.17.6" } diff --git a/doc/api_project.json b/doc/api_project.json index b27e7e63e..a3017b24f 100644 --- a/doc/api_project.json +++ b/doc/api_project.json @@ -1,14 +1,14 @@ { "title": "Authelia API documentation", "name": "authelia", - "version": "2.1.3", + "version": "3.7.0", "description": "2FA Single Sign-On server for nginx using LDAP, TOTP and U2F", "sampleUrl": false, "defaultVersion": "0.0.0", "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2017-06-11T20:41:36.025Z", + "time": "2017-12-04T21:38:44.927Z", "url": "http://apidocjs.com", "version": "0.17.6" } diff --git a/example/nginx/portal/nginx.conf b/example/nginx/portal/nginx.conf index c7d4016b0..1ce5fcfc9 100644 --- a/example/nginx/portal/nginx.conf +++ b/example/nginx/portal/nginx.conf @@ -322,5 +322,25 @@ http { proxy_pass $upstream_headers; } } + + server { + listen 443 ssl; + server_name authelia.example.com; + + resolver 127.0.0.11 ipv6=off; + set $upstream_endpoint http://authelia; + + ssl on; + ssl_certificate /etc/ssl/server.crt; + ssl_certificate_key /etc/ssl/server.key; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "SAMEORIGIN"; + + location / { + proxy_set_header Host $http_host; + proxy_pass $upstream_endpoint; + } + } } diff --git a/server/src/lib/ErrorReplies.ts b/server/src/lib/ErrorReplies.ts index dc74abbe0..f1c5f4fd1 100644 --- a/server/src/lib/ErrorReplies.ts +++ b/server/src/lib/ErrorReplies.ts @@ -19,6 +19,15 @@ function replyWithError(req: express.Request, res: express.Response, }; } +export function redirectTo(redirectUrl: string, req: express.Request, + res: express.Response, logger: IRequestLogger) { + return function(err: Error) { + logger.error(req, "Error: %s", err.message); + logger.debug(req, "Redirecting to %s", redirectUrl); + res.redirect(redirectUrl); + }; +} + export function replyWithError400(req: express.Request, res: express.Response, logger: IRequestLogger) { return replyWithError(req, res, 400, logger); diff --git a/server/src/lib/routes/verify/get.ts b/server/src/lib/routes/verify/get.ts index 54f0c2ec5..79fda877e 100644 --- a/server/src/lib/routes/verify/get.ts +++ b/server/src/lib/routes/verify/get.ts @@ -5,6 +5,7 @@ import ErrorReplies = require("../../ErrorReplies"); import { ServerVariables } from "../../ServerVariables"; import GetWithSessionCookieMethod from "./get_session_cookie"; import GetWithBasicAuthMethod from "./get_basic_auth"; +import Constants = require("../../../../../shared/constants"); import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; @@ -50,6 +51,12 @@ function replyWith200(res: Express.Response) { }; } +function getRedirectParam(req: Express.Request) { + return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" + ? req.query[Constants.REDIRECT_QUERY_PARAM] + : undefined; +} + export default function (vars: ServerVariables) { return function (req: Express.Request, res: Express.Response) : BluebirdPromise