First commit with tests

This commit is contained in:
Clement Michaud 2016-12-10 01:47:58 +01:00
commit d7d743bdfa
24 changed files with 830 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM node
WORKDIR /usr/src
COPY app /usr/src
CMD ["node", "app.js"]

37
app/index.js Normal file
View File

@ -0,0 +1,37 @@
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var routes = require('./lib/routes');
var ldap = require('ldapjs');
var speakeasy = require('speakeasy');
var totpSecret = process.env.SECRET;
var LDAP_URL = process.env.LDAP_URL || 'ldap://127.0.0.1:389';
var USERS_DN = process.env.USERS_DN;
var PORT = process.env.PORT || 80
var JWT_SECRET = 'this is the secret';
var EXPIRATION_TIME = process.env.EXPIRATION_TIME || '1h';
var ldap_client = ldap.createClient({
url: LDAP_URL
});
var app = express();
app.use(cookieParser());
app.use(express.static(__dirname + '/public_html'));
app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine', 'ejs');
app.get ('/login', routes.login);
app.post ('/login', routes.login);
app.get ('/logout', routes.logout);
app.get ('/_auth', routes.auth);
app.listen(PORT, function(err) {
console.log('Listening on %d...', PORT);
});

57
app/lib/authentication.js Normal file
View File

@ -0,0 +1,57 @@
module.exports = {
'authenticate': authenticate,
'verify_authentication': verify_authentication
}
var objectPath = require('object-path');
var Jwt = require('./jwt');
var ldap_checker = require('./ldap_checker');
var totp_checker = require('./totp_checker');
var replies = require('./replies');
var Q = require('q');
var utils = require('./utils');
function authenticate(req, res, args) {
var defer = Q.defer();
var username = req.body.username;
var password = req.body.password;
var token = req.body.token;
console.log('Start authentication');
if(!username || !password || !token) {
replies.authentication_failed(res);
return;
}
var totp_promise = totp_checker.validate(args.totp_interface, token, args.totp_secret);
var credentials_promise = ldap_checker.validate(args.ldap_interface, username, password, args.users_dn);
Q.all([totp_promise, credentials_promise])
.then(function() {
var token = args.jwt.sign({ user: username }, args.jwt_expiration_time);
res.cookie('access_token', token);
res.redirect('/');
console.log('Authentication succeeded');
defer.resolve();
})
.fail(function(err1, err2) {
res.render('login');
console.log('Authentication failed', err1, err2);
defer.reject();
});
return defer.promise;
}
function verify_authentication(req, res, args) {
console.log('Verify authentication');
if(!objectPath.has(req, 'cookies.access_token')) {
return utils.reject('No access token provided');
}
var jsonWebToken = req.cookies['access_token'];
return args.jwt.verify(jsonWebToken);
}

29
app/lib/jwt.js Normal file
View File

@ -0,0 +1,29 @@
module.exports = Jwt;
var jwt = require('jsonwebtoken');
var utils = require('./utils');
var Q = require('q');
function Jwt(secret) {
var _secret;
this._secret = secret;
}
Jwt.prototype.sign = function(data, expiration_time) {
return jwt.sign(data, this._secret, { expiresIn: expiration_time });
}
Jwt.prototype.verify = function(token) {
var defer = Q.defer();
try {
var decoded = jwt.verify(token, this._secret);
defer.resolve(decoded);
}
catch(err) {
defer.reject(err);
}
return defer.promise;
}

14
app/lib/ldap_checker.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
'validate': validateCredentials
}
var Q = require('q');
var util = require('util');
var utils = require('./utils');
function validateCredentials(ldap_client, username, password, users_dn) {
var userDN = util.format("cn=%s,%s", username, users_dn);
var bind_promised = utils.promisify(ldap_client.bind, ldap_client);
return bind_promised(userDN, password);
}

29
app/lib/replies.js Normal file
View File

@ -0,0 +1,29 @@
module.exports = {
'authentication_failed': authentication_failed,
'authentication_succeeded': authentication_succeeded,
'already_authenticated': already_authenticated
}
function authentication_failed(res) {
console.log('Reply: authentication failed');
res.status(401)
res.send('Authentication failed');
}
function send_success(res, username, msg) {
res.status(200);
res.set({ 'X-Remote-User': username });
res.send(msg);
}
function authentication_succeeded(res, username) {
console.log('Reply: authentication succeeded');
send_success(res, username, 'Authentication succeeded');
}
function already_authenticated(res, username) {
console.log('Reply: already authenticated');
send_success(res, username, 'Authentication succeeded');
}

35
app/lib/routes.js Normal file
View File

@ -0,0 +1,35 @@
module.exports = {
'auth': serveAuth,
'login': serveLogin,
'logout': serveLogout
}
var authentication = require('./authentication');
var replies = require('./replies');
function serveAuth(req, res) {
authentication.verify(req, res)
.then(function(user) {
replies.already_authenticated(res, user);
})
.fail(function(err) {
replies.authentication_failed(res);
console.error(err);
});
}
function serveLogin(req, res) {
console.log('METHOD=%s', req.method);
if(req.method == 'POST') {
authentication.authenticate(req, res);
}
else {
res.render('login');
}
}
function serveLogout(req, res) {
res.clearCookie('access_token');
res.redirect('/');
}

22
app/lib/totp_checker.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
'validate': validate
}
var Q = require('q');
function validate(speakeasy, token, totp_secret) {
var defer = Q.defer();
var real_token = speakeasy.totp({
secret: totp_secret,
encoding: 'base32'
});
if(token == real_token) {
defer.resolve();
}
else {
defer.reject('Wrong challenge');
}
return defer.promise;
}

35
app/lib/utils.js Normal file
View File

@ -0,0 +1,35 @@
module.exports = {
'promisify': promisify,
'resolve': resolve,
'reject': reject
}
var Q = require('q');
function promisify(fn, context) {
return function() {
var defer = Q.defer();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, val) {
if (err !== null && err !== undefined) {
return defer.reject(err);
}
return defer.resolve(val);
});
fn.apply(context || {}, args);
return defer.promise;
};
}
function resolve(data) {
var defer = Q.defer();
defer.resolve(data);
return defer.promise;
}
function reject(err) {
var defer = Q.defer();
defer.reject(err);
return defer.promise;
}

58
app/public_html/login.css Normal file
View File

@ -0,0 +1,58 @@
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
.btn { display: inline-block; *display: inline; *zoom: 1; padding: 4px 10px 4px; margin-bottom: 0; font-size: 13px; line-height: 18px; color: #333333; text-align: center;text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; background-color: #f5f5f5; background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(top, #ffffff, #e6e6e6); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#ffffff, endColorstr=#e6e6e6, GradientType=0); border-color: #e6e6e6 #e6e6e6 #e6e6e6; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border: 1px solid #e6e6e6; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); cursor: pointer; *margin-left: .3em; }
.btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { background-color: #e6e6e6; }
.btn-large { padding: 9px 14px; font-size: 15px; line-height: normal; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; }
.btn:hover { color: #333333; text-decoration: none; background-color: #e6e6e6; background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -ms-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; }
.btn-primary, .btn-primary:hover { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); color: #ffffff; }
.btn-primary.active { color: rgba(255, 255, 255, 0.75); }
.btn-primary { background-color: #4a77d4; background-image: -moz-linear-gradient(top, #6eb6de, #4a77d4); background-image: -ms-linear-gradient(top, #6eb6de, #4a77d4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#6eb6de), to(#4a77d4)); background-image: -webkit-linear-gradient(top, #6eb6de, #4a77d4); background-image: -o-linear-gradient(top, #6eb6de, #4a77d4); background-image: linear-gradient(top, #6eb6de, #4a77d4); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#6eb6de, endColorstr=#4a77d4, GradientType=0); border: 1px solid #3762bc; text-shadow: 1px 1px 1px rgba(0,0,0,0.4); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.5); }
.btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { filter: none; background-color: #4a77d4; }
.btn-block { width: 100%; display:block; }
* { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; -ms-box-sizing:border-box; -o-box-sizing:border-box; box-sizing:border-box; }
html { width: 100%; height:100%; overflow:hidden; }
body {
width: 100%;
height:100%;
font-family: 'Open Sans', sans-serif;
background: #092756;
background: -moz-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%),-moz-linear-gradient(top, rgba(57,173,219,.25) 0%, rgba(42,60,87,.4) 100%), -moz-linear-gradient(-45deg, #670d10 0%, #092756 100%);
background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -webkit-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -webkit-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -o-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -o-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -o-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -ms-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -ms-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -ms-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), linear-gradient(to bottom, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), linear-gradient(135deg, #670d10 0%,#092756 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3E1D6D', endColorstr='#092756',GradientType=1 );
}
.login {
position: absolute;
top: 50%;
left: 50%;
margin: -150px 0 0 -150px;
width:300px;
height:300px;
}
.login h1 { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; }
input {
width: 100%;
margin-bottom: 10px;
background: rgba(0,0,0,0.3);
border: none;
outline: none;
padding: 10px;
font-size: 13px;
color: #fff;
text-shadow: 1px 1px 1px rgba(0,0,0,0.3);
border: 1px solid rgba(0,0,0,0.3);
border-radius: 4px;
box-shadow: inset 0 -5px 45px rgba(100,100,100,0.2), 0 1px 1px rgba(255,255,255,0.2);
-webkit-transition: box-shadow .5s ease;
-moz-transition: box-shadow .5s ease;
-o-transition: box-shadow .5s ease;
-ms-transition: box-shadow .5s ease;
transition: box-shadow .5s ease;
}
input:focus { box-shadow: inset 0 -5px 45px rgba(100,100,100,0.4), 0 1px 1px rgba(255,255,255,0.2); }

View File

@ -0,0 +1,118 @@
var assert = require('assert');
var authentication = require('../lib/authentication');
var create_res_mock = require('./res_mock');
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
var autoResolving = sinon.promise().resolves();
function create_req_mock(token) {
return {
body: {
username: 'username',
password: 'password',
token: token
},
cookies: {
'access_token': 'cookie_token'
}
}
}
function create_mocks() {
var totp_token = 'totp_token';
var jwt_token = 'jwt_token';
var res_mock = create_res_mock();
var req_mock = create_req_mock(totp_token);
var bind_mock = sinon.mock();
var totp_mock = sinon.mock();
var sign_mock = sinon.mock();
var verify_mock = sinon.promise();
var jwt = {
sign: sign_mock,
verify: verify_mock
};
var ldap_interface_mock = {
bind: bind_mock
};
var totp_interface_mock = {
totp: totp_mock
};
bind_mock.yields();
totp_mock.returns(totp_token);
sign_mock.returns(jwt_token);
var args = {
totp_secret: 'totp_secret',
jwt: jwt,
jwt_expiration_time: '1h',
users_dn: 'dc=example,dc=com',
ldap_interface: ldap_interface_mock,
totp_interface: totp_interface_mock
}
return {
req: req_mock,
res: res_mock,
args: args,
totp: totp_mock,
jwt: jwt
}
}
describe('test jwt', function() {
describe('test authentication', function() {
it('should authenticate user successfuly', function(done) {
var jwt_token = 'jwt_token';
var clock = sinon.useFakeTimers();
var mocks = create_mocks();
authentication.authenticate(mocks.req, mocks.res, mocks.args)
.then(function() {
clock.restore();
assert(mocks.res.cookie.calledWith('access_token', jwt_token));
assert(mocks.res.redirect.calledWith('/'));
done();
})
});
it('should fail authentication', function(done) {
var clock = sinon.useFakeTimers();
var mocks = create_mocks();
mocks.totp.returns('wrong token');
authentication.authenticate(mocks.req, mocks.res, mocks.args)
.fail(function(err) {
clock.restore();
done();
})
});
});
describe('test verify authentication', function() {
it('should be already authenticated', function(done) {
var mocks = create_mocks();
var data = { user: 'username' };
mocks.jwt.verify = sinon.promise().resolves(data);
authentication.verify_authentication(mocks.req, mocks.res, mocks.args)
.then(function(actual_data) {
assert.equal(actual_data, data);
done();
});
});
it('should not be already authenticated', function(done) {
var mocks = create_mocks();
var data = { user: 'username' };
mocks.jwt.verify = sinon.promise().rejects('Error with JWT token');
return authentication.verify_authentication(mocks.req, mocks.res, mocks.args)
.fail(function() {
done();
});
});
});
});

33
app/tests/jwt_test.js Normal file
View File

@ -0,0 +1,33 @@
var Jwt = require('../lib/jwt');
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
var autoResolving = sinon.promise().resolves();
describe('test jwt', function() {
it('should sign and verify the token', function() {
var data = {user: 'user'};
var secret = 'secret';
var jwt = new Jwt(secret);
var token = jwt.sign(data, '1m');
return jwt.verify(token);
});
it('should verify and fail on wrong token', function() {
var jwt = new Jwt('secret');
return jwt.verify('wrong token').fail(autoResolving);
});
it('should fail after expiry', function(done) {
var clock = sinon.useFakeTimers(0);
var data = {user: 'user'};
var jwt = new Jwt('secret');
var token = jwt.sign(data, '1m');
clock.tick(1000 * 61); // 61 seconds
jwt.verify(token).fail(function() { done(); });
clock.restore();
});
});

View File

@ -0,0 +1,35 @@
var ldap_checker = require('../lib/ldap_checker');
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
var autoResolving = sinon.promise().resolves();
function test_validate(bind_mock) {
var username = 'user';
var password = 'password';
var ldap_url = 'http://ldap';
var users_dn = 'dc=example,dc=com';
var ldap_client_mock = {
bind: bind_mock
}
return ldap_checker.validate(ldap_client_mock, username, password, ldap_url, users_dn);
}
describe('test ldap checker', function() {
it('should bind the user if good credentials provided', function() {
var bind_mock = sinon.mock().yields();
return test_validate(bind_mock);
});
it('should not bind the user if wrong credentials provided', function() {
var bind_mock = sinon.mock().yields('wrong credentials');
var promise = test_validate(bind_mock);
return promise.fail(autoResolving);
});
});

52
app/tests/replies_test.js Normal file
View File

@ -0,0 +1,52 @@
var replies = require('../lib/replies');
var assert = require('assert');
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
var autoResolving = sinon.promise().resolves();
function create_res_mock() {
var status_mock = sinon.mock();
var send_mock = sinon.mock();
var set_mock = sinon.mock();
return {
status: status_mock,
send: send_mock,
set: set_mock
};
}
describe('test jwt', function() {
it('should authenticate with success', function() {
var res_mock = create_res_mock();
var username = 'username';
replies.authentication_succeeded(res_mock, username);
assert(res_mock.status.calledWith(200));
assert(res_mock.set.calledWith({'X-Remote-User': username }));
});
it('should reply successfully when already authenticated', function() {
var res_mock = create_res_mock();
var username = 'username';
replies.already_authenticated(res_mock, username);
assert(res_mock.status.calledWith(200));
assert(res_mock.set.calledWith({'X-Remote-User': username }));
});
it('should reply with failed authentication', function() {
var res_mock = create_res_mock();
var username = 'username';
replies.authentication_failed(res_mock, username);
assert(res_mock.status.calledWith(401));
});
});

24
app/tests/res_mock.js Normal file
View File

@ -0,0 +1,24 @@
module.exports = create_res_mock;
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
function create_res_mock() {
var status_mock = sinon.mock();
var send_mock = sinon.mock();
var set_mock = sinon.mock();
var cookie_mock = sinon.mock();
var render_mock = sinon.mock();
var redirect_mock = sinon.mock();
return {
status: status_mock,
send: send_mock,
set: set_mock,
cookie: cookie_mock,
render: render_mock,
redirect: redirect_mock
};
}

View File

@ -0,0 +1,32 @@
var totp_checker = require('../lib/totp_checker');
var sinon = require('sinon');
var sinonPromise = require('sinon-promise');
sinonPromise(sinon);
var autoResolving = sinon.promise().resolves();
describe('test TOTP checker', function() {
it('should validate the TOTP token', function() {
var totp_secret = 'NBD2ZV64R9UV1O7K';
var token = 'token';
var totp_mock = sinon.mock();
totp_mock.returns('token');
var speakeasy_mock = {
totp: totp_mock
}
return totp_checker.validate(speakeasy_mock, token, totp_secret);
});
it('should not validate a wrong TOTP token', function() {
var totp_secret = 'NBD2ZV64R9UV1O7K';
var token = 'wrong token';
var totp_mock = sinon.mock();
totp_mock.returns('token');
var speakeasy_mock = {
totp: totp_mock
}
return totp_checker.validate(speakeasy_mock, token, totp_secret).fail(autoResolving);
});
});

17
app/views/login.ejs Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="login.css">
</head>
<body>
<div class="login">
<h1>Login</h1>
<form method="POST">
<input type="text" name="username" placeholder="Username" required="required" />
<input type="password" name="password" placeholder="Password" required="required" />
<input type="text" name="token" placeholder="Verification token" required="required" />
<button type="submit" class="btn btn-primary btn-block btn-large">Enter</button>
</form>
<br>
</div>
</body>
</html>

39
docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
version: '2'
services:
auth-server:
build: .
environment:
- LDAP_URL=ldap://ldap
- SECRET=NBD2ZV64R9UV1O7K
- USERS_DN=dc=example,dc=com
- PORT=80
- EXPIRATION_TIME=2m
depends_on:
- ldap
expose:
- "80"
ldap:
image: osixia/openldap:1.1.7
environment:
- LDAP_ORGANISATION=MyCompany
- LDAP_DOMAIN=example.com
- LDAP_ADMIN_PASSWORD=test
expose:
- "389"
nginx:
image: nginx:alpine
volumes:
- ./proxy/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- auth-server
ports:
- "8085:80"
secret:
image: nginx:alpine
volumes:
- ./secret/nginx.conf:/etc/nginx/nginx.conf
- ./secret/index.html:/usr/share/nginx/html/index.html

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "ldap-totp-nginx-auth",
"version": "1.0.0",
"description": "",
"main": "app/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/clems4ever/ldap-totp-nginx-auth.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/clems4ever/ldap-totp-nginx-auth/issues"
},
"homepage": "https://github.com/clems4ever/ldap-totp-nginx-auth#readme",
"dependencies": {
"body-parser": "^1.15.2",
"cookie-parser": "^1.4.3",
"ejs": "^2.5.5",
"express": "^4.14.0",
"jsonwebtoken": "^7.2.1",
"ldapjs": "^1.0.1",
"object-path": "^0.11.3",
"q": "^1.4.1",
"speakeasy": "^2.0.0"
},
"devDependencies": {
"mocha": "^3.2.0",
"should": "^11.1.1",
"sinon": "^1.17.6",
"sinon-promise": "^0.1.3"
}
}

5
proxy/index.html Normal file
View File

@ -0,0 +1,5 @@
<html>
<body>
Coucou
</body>
</html>

76
proxy/nginx.conf Normal file
View File

@ -0,0 +1,76 @@
# nginx-sso - example nginx config
#
# (c) 2015 by Johannes Gilger <heipei@hackvalue.de>
#
# This is an example config for using nginx with the nginx-sso cookie system.
# For simplicity, this config sets up two fictional vhosts that you can use to
# test against both components of the nginx-sso system: ssoauth & ssologin.
# In a real deployment, these vhosts would be separate hosts.
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
# This is the vserver for the service that you want to protect.
server {
listen 80;
error_page 401 = @error401;
location @error401 {
return 302 http://127.0.0.1:8085/login;
}
location = /_auth {
internal;
proxy_pass http://auth-server/_auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
location /secret/ {
auth_request /_auth;
auth_request_set $user $upstream_http_x_remote_user;
proxy_set_header X-Forwarded-User $user;
# auth_request_set $groups $upstream_http_remote_groups;
# proxy_set_header Remote-Groups $groups;
# auth_request_set $expiry $upstream_http_remote_expiry;
# proxy_set_header Remote-Expiry $expiry;
rewrite ^/secret/(.*)$ /$1 break;
proxy_pass http://secret;
}
location /login {
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://auth-server/login;
}
location /logout {
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://auth-server/logout;
}
}
}

5
secret/index.html Normal file
View File

@ -0,0 +1,5 @@
<html>
<body>
Coucou
</body>
</html>

32
secret/nginx.conf Normal file
View File

@ -0,0 +1,32 @@
# nginx-sso - example nginx config
#
# (c) 2015 by Johannes Gilger <heipei@hackvalue.de>
#
# This is an example config for using nginx with the nginx-sso cookie system.
# For simplicity, this config sets up two fictional vhosts that you can use to
# test against both components of the nginx-sso system: ssoauth & ssologin.
# In a real deployment, these vhosts would be separate hosts.
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
# This is the vserver for the service that you want to protect.
server {
listen 80;
root /usr/share/nginx/html;
}
}