├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── auth-svc-data └── jwt-secret.txt ├── auth-svc ├── Dockerfile ├── index.js ├── package-lock.json └── package.json ├── docker-compose.yml ├── docs ├── AuthenticationSequence.png └── sequence.txt ├── keycloak_password_file.txt ├── keycloak_user_file.txt ├── ldap-config.json ├── svc1 ├── Dockerfile ├── index.js ├── package-lock.json └── package.json └── traefik.toml /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.15.1-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY index.js ./ 10 | COPY ldap-config.json ./ 11 | 12 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forward Authentication Example 2 | 3 | ## Purpose 4 | 5 | Exploring ways to configure authentication in a microservices architecture. Specifically, using a reverse proxy to ensure all incoming application requests contain valid authentication tokens. 6 | 7 | ## Technology 8 | 9 | * Reverse Proxy: Traefik v2.1 10 | * Identity Provider: KeyCloak 11 | * Forward auth service: written in Node.JS 12 | 13 | ## Contents of Repo 14 | 15 | Simple set of services to illustrate: 16 | 17 | * Auth requests are forwarded to an auth server, which authenticates request and returns an auth token 18 | * API requests have a valid auth header token before being forwarded to an application server 19 | 20 | This example uses [Traefik](http://traefik.io) as the front end to route auth requests to the auth server and API requests to the application server, first checking for a valid token. 21 | 22 | ## Request Flow 23 | 24 | There are two parts to the request flow. 25 | 26 | First, a user authenticates with the auth endpoint (port 79 in the examples below). If successful a JWT token is returned. 27 | 28 | Second, the client sends a request to the Application endpoint (port 80 in the examples below), including the JWT token. Assuming the token is valid, the request will be forwarded to the application server and the server will respond accordingly. 29 | 30 | **Request Auth Token** 31 | 32 | * Make request to Auth endpoint, with credentials 33 | * Reverse Proxy forwards to Auth Server 34 | * Auth server validates or rejects credentials 35 | * Reverse Proxy returns Auth response 36 | 37 | **Use Auth Token** 38 | 39 | * Make request to Application with Auth Token 40 | * Reverse Proxy forwards request to Auth Server 41 | * Auth server validates or rejects token 42 | * Reverse Proxy forwards successful auth requests to Application Server, returns failed auth response to client 43 | * Application server processes requests, returns response 44 | * Reverse Proxy returns application response 45 | 46 | ![Authentication Sequence](./docs/AuthenticationSequence.png) 47 | 48 | ## Attribution and Resources 49 | 50 | * [Intro to JWT](https://jwt.io/introduction/) 51 | * [How to deal with JWT expiration?](https://gist.github.com/soulmachine/b368ce7292ddd7f91c15accccc02b8df) 52 | * [Much of the JS code was taken from here, so thanks lucianweber](https://github.com/lucianweber/ldap-jwt/) 53 | 54 | ## Additional Research 55 | 56 | * Configure LDAP authentication 57 | * Handle JWT Expiration 58 | * Token Management (https://gist.github.com/soulmachine/b368ce7292ddd7f91c15accccc02b8df) 59 | * 'Extend' a token 60 | * Revoke token 61 | * Look at "authorization code flow" 62 | * https://openid.net/connect/ 63 | * https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth 64 | * Redirect HTTP traffic to HTTPS 65 | * Allow dev workflow without HTTPS 66 | 67 | ## Concerns 68 | 69 | * Ensure Auth server is: 70 | * Robust and Secure 71 | * Libraries do not have vulnerabilities 72 | * How to prevent/monitor for suspicious activity 73 | * JWT Token Secret will need to be available on all application servers for validating tokens 74 | * Or use Public/Private keys, where only Auth server has private and application servers have public keys 75 | 76 | ## To Do 77 | 78 | * Forward on particular headers from the token 79 | 80 | ## Run It 81 | 82 | * Start the service 83 | 84 | ``` 85 | docker-compose up 86 | ``` 87 | 88 | * Configure KeyCloak (the Identity Provider) 89 | * Go to `http://localhost/auth` 90 | * Open KeyCloak Admin console 91 | * Log in with `admin` `password` 92 | * Configure a client 93 | * Go to "Configure" > "Clients" on the left panel 94 | * Select "Create", to create a new client 95 | * In the "Client ID" field, enter `client1` 96 | * In "Client Protocol", select `openid-connect` 97 | * Click "Save" 98 | * Change "Access Type" to "public" ('public' means that a client_secret doesn't need to be supplied) 99 | * In "Valid Redirect URIs", enter `http://localhost` 100 | * Click "Save" 101 | * Configure a user 102 | * Go to "Manage" > "Users" on the left panel 103 | * Select "Add User" 104 | * In `Username` field, enter "user1" 105 | * Click "Save" 106 | * Select the "Credentials" selection header 107 | * In the "New Password" and "Password Confirmation" fields, enter `password` 108 | * Turn "Temporary" to "Off" 109 | * Click "Reset Password" 110 | 111 | * Get Auth Token Cert 112 | * Go to "Configure" > "Realm Settings" on the left panel 113 | * Select "Keys" from header menu 114 | * In the "RS256" row, click the "Certificate" button 115 | * Copy the cert 116 | * In the project's file structure, go to `./auth-svc-data/jwt-secret.txt` 117 | * Replace line 2 with the cert copied from KeyCloak 118 | * Save the file 119 | 120 | * Execute Requests 121 | * Request an auth token with a client called `client` and a user `user1` with password `password` 122 | * Call `svc1`s `/health` endpoint, passing in the `access_token` 123 | 124 | ```bash 125 | curl -XPOST -d "grant_type=password&username=user1&password=password&client_id=client1" http://localhost/auth/realms/master/protocol/openid-connect/token |\ 126 | jq '.access_token' |\ 127 | cut -d "\"" -f 2 |\ 128 | curl -i http://localhost/svc1/secure -H "Authorization: Bearer $(cat -)" 129 | ``` 130 | 131 | ## Example Requests 132 | 133 | This will get an auth token, assuming there is a client called `client` and a user `user1` with password `password`: 134 | 135 | ```bash 136 | curl -i -XPOST -d "grant_type=password&username=user1&password=password&client_id=client1" http://localhost/auth/realms/master/protocol/openid-connect/token 137 | ``` 138 | 139 | This will make the request to the service. Traefik will pass the auth token to the auth service to verify the token, and the request will be forwarded to the internal service. 140 | 141 | ```bash 142 | curl -i http://localhost/svc1/secure -H "Authorization: Bearer $(AUTH_TOKEN)" 143 | ``` 144 | 145 | In one fell swoop. 146 | 147 | This example uses [jq](https://stedolan.github.io/jq/download/) to parse the JSON to extract the token. 148 | 149 | ```bash 150 | curl -XPOST -d "grant_type=password&username=user1&password=password&client_id=client1" http://localhost/auth/realms/master/protocol/openid-connect/token |\ 151 | jq '.access_token' |\ 152 | cut -d "\"" -f 2 |\ 153 | curl -i http://localhost/svc1/secure -H "Authorization: Bearer $(cat -)" 154 | ``` 155 | 156 | ### API Request 157 | 158 | Will return a 401 Unauthorized 159 | 160 | ```bash 161 | curl -i -XGET "http://localhost/svc1/secure" 162 | 163 | HTTP/1.1 401 Unauthorized 164 | Access-Control-Allow-Origin: * 165 | Content-Length: 0 166 | Date: Thu, 11 Apr 2019 05:12:18 GMT 167 | ``` 168 | 169 | ```bash 170 | curl -i -XGET "http://localhost/svc1/secure" -H "Authorization: Bearer ABC" 171 | 172 | HTTP/1.1 200 OK 173 | Content-Length: 16 174 | Content-Type: text/plain; charset=utf-8 175 | Date: Thu, 11 Apr 2019 05:12:39 GMT 176 | 177 | {"success":true} 178 | ``` 179 | 180 | ## Tests 181 | 182 | ### Missing Auth Header 183 | 184 | ```bash 185 | curl -i -XGET http://localhost/svc1/secure 186 | 187 | HTTP/1.1 400 Bad Request 188 | Access-Control-Allow-Origin: * 189 | Content-Type: application/json; charset=utf-8 190 | Content-Length: 43 191 | ETag: W/"2b-JpYRGD0cbj9/XPwi3ve83p8GieM" 192 | Date: Thu, 11 Apr 2019 05:32:03 GMT 193 | Connection: keep-alive 194 | 195 | {"error":"Authorization Header is missing"} 196 | ``` 197 | 198 | ### Auth header missing 'Bearer' type 199 | 200 | ```bash 201 | curl -i -XGET http://localhost/svc1/secure -H "Authorization: asdf" 202 | 203 | HTTP/1.1 400 Bad Request 204 | Access-Control-Allow-Origin: * 205 | Content-Type: application/json; charset=utf-8 206 | Content-Length: 62 207 | ETag: W/"3e-/HIoBOKCUWqjQ9holacN5/rZamk" 208 | Date: Thu, 11 Apr 2019 05:32:30 GMT 209 | Connection: keep-alive 210 | 211 | {"error":"Authorization Header does not contain Bearer token"} 212 | ``` 213 | 214 | ### Auth Token missing 215 | 216 | ```bash 217 | curl -i http://localhost/svc1/secure -H "Authorization: Bearer" 218 | 219 | HTTP/1.1 400 Bad Request 220 | Access-Control-Allow-Origin: * 221 | Content-Type: application/json; charset=utf-8 222 | Content-Length: 56 223 | ETag: W/"38-y9E61ZfxQiWq9FNtvC85AVSYjdE" 224 | Date: Thu, 11 Apr 2019 05:42:19 GMT 225 | Connection: keep-alive 226 | 227 | {"error":"Authorization Header Access token is missing"} 228 | ``` 229 | 230 | ### Access token error 231 | 232 | ```bash 233 | curl -i http://localhost/svc1/secure -H "Authorization: Bearer ABC" 234 | 235 | HTTP/1.1 400 Bad Request 236 | Access-Control-Allow-Origin: * 237 | Content-Type: application/json; charset=utf-8 238 | Content-Length: 55 239 | ETag: W/"37-Nkl0CLHEHIZxW8w78ZY79pLWZQE" 240 | Date: Thu, 11 Apr 2019 05:46:44 GMT 241 | Connection: keep-alive 242 | 243 | {"error":"Invalid Access Token. Could not be decoded."} 244 | ``` 245 | -------------------------------------------------------------------------------- /auth-svc-data/jwt-secret.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICmzCCAYMCBgFvXZZClTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTkxMjMxMjAxMDI2WhcNMjkxMjMxMjAxMjA2WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwHRhVLkgWNJPG1dryRoOcx1MTr6RQaWfDxhXw0k/1Z9cwoGYDk/oruTg2KioW4QrjMqr0aw557aSQQJytqIiuP/DU6KfvT/QnhGGWmUPS5t1P/QNF81v4DrX6bCxvdh7xLH30RsjqzW3Q4Wu1xGXz+YHjnq1yItZWU3YCUDYslKz9Qw4nrzDJWz1QZJ+nZ+yxL9K0pbKO0qsS2C6nbdnp/IzS0xNzlHXYYdiph+urtyc2+4TZ14Rnzu/zXkH1ZUEcimmM6WfBr/RhOL5Wz9bLsZajssvmSPa1ERSJgFXs3IQ51fqzXWG7U4PNCWXSnFJ0gR8oM6c7g8+AT8hlszQFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABKz0l/KKjPANbDFLokkg0858wP7a8vmCTNg7lW1WeKZ0RxbFhATjkKhD0o9OWGOYm60MgTz9f73WS3POmlqWyhizD0bZgVBJh4j+nJ0rapgfJR/hbmB7kcVqU8OBqTe3RAn7wuZViTXqD0yv1A6HW7CZer3cAbS8s/3UZnjEZMzc+m2DYJxWCeDB0DxuHetnnx+QQoAObeeyoktPXW+bFracKdm+kEWiYUZA/idCi2+qDzeVEIduzGXjPUEpEwzUObBpBys0O22DkEaW+K+N9Ympc4GF7b4cEw2WC4KZc7EQAmDFVhBM2OZL0Rj+fkARpUroa0Y1fDRH3Mt5pvNKyo= 3 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /auth-svc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.15.1-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY index.js ./ 10 | 11 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /auth-svc/index.js: -------------------------------------------------------------------------------- 1 | //https://github.com/lucianweber/ldap-jwt/blob/master/index.js 2 | 3 | var fs = require('fs'); 4 | var bodyParser = require('body-parser'); 5 | var jwt = require('jwt-simple'); 6 | var moment = require('moment'); 7 | var Promise = require('promise'); 8 | 9 | app = require('express')(); 10 | app.use(bodyParser.json()); 11 | app.use(bodyParser.urlencoded({ extended: false })); 12 | app.use(require('cors')()); 13 | app.disable('x-powered-by'); 14 | 15 | console.log(`JWT_SECRET_PATH=${process.env.JWT_SECRET_PATH}`) 16 | if (process.env.JWT_SECRET_PATH == undefined || process.env.JWT_SECRET_PATH == null || process.env.JWT_SECRET_PATH == "") { 17 | console.error("JWT_SECRET_PATH environment variable not found.") 18 | throw new Error("JWT_SECRET_PATH environment variable not found.") 19 | } 20 | 21 | var jwtTokenSecret = fs.readFileSync(process.env.JWT_SECRET_PATH, 'utf8'); 22 | app.set('jwtTokenSecret', jwtTokenSecret); 23 | console.log(`jwtTokenSecret set. Token length: ${jwtTokenSecret.length}, Token: ${jwtTokenSecret}`) 24 | 25 | // https://www.keycloak.org/docs/latest/securing_apps/#_certificate_endpoint 26 | // http://localhost/auth/realms/master/protocol/openid-connect/certs 27 | 28 | app.get('/health', function (req, res) { 29 | console.log('health') 30 | res.json({ success: true }); 31 | }) 32 | 33 | app.get('/verify', function (req, res) { 34 | console.log('/verify called') 35 | console.log('AuthHeader', req.headers.authorization) 36 | 37 | if (!req.headers.authorization) { 38 | res.status(400).send({ error: 'Authorization Header is missing' }); 39 | return; 40 | } 41 | 42 | const [bearer, token] = req.headers.authorization.split(' '); 43 | // req.headers.authorization.split(' ')[0] 44 | if (bearer !== 'Bearer') { 45 | res.status(400).send({ error: 'Authorization Header does not contain Bearer token' }); 46 | return; 47 | } 48 | 49 | if (!token) { 50 | res.status(400).send({ error: 'Authorization Header Access token is missing' }); 51 | return; 52 | } 53 | 54 | var decoded = null; 55 | let jwtTokenSecret = app.get('jwtTokenSecret'); 56 | console.log(`token: ${token}`) 57 | try { 58 | decoded = jwt.decode(token, jwtTokenSecret); 59 | } catch (err) { 60 | console.error('Invalid Access Token. Could not be decoded.', token, err); 61 | res.status(400).send({ error: 'Invalid Access Token. Could not be decoded.' }); 62 | return; 63 | } 64 | 65 | if (decoded.exp <= parseInt(moment().format("X"))) { 66 | console.log(`Response: 400. Expired token.`) 67 | res.status(400).send({ error: 'Access token has expired' }); 68 | } else { 69 | console.log(`Response: 200`) 70 | res.status(200).send(); 71 | } 72 | 73 | }); 74 | 75 | var port = (process.env.PORT || 3000); 76 | app.listen(port, function () { 77 | console.log('Listening on port: ' + port); 78 | }); 79 | -------------------------------------------------------------------------------- /auth-svc/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-svc", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "asap": { 22 | "version": "2.0.6", 23 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 24 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 25 | }, 26 | "body-parser": { 27 | "version": "1.19.0", 28 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 29 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 30 | "requires": { 31 | "bytes": "3.1.0", 32 | "content-type": "~1.0.4", 33 | "debug": "2.6.9", 34 | "depd": "~1.1.2", 35 | "http-errors": "1.7.2", 36 | "iconv-lite": "0.4.24", 37 | "on-finished": "~2.3.0", 38 | "qs": "6.7.0", 39 | "raw-body": "2.4.0", 40 | "type-is": "~1.6.17" 41 | } 42 | }, 43 | "bytes": { 44 | "version": "3.1.0", 45 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 46 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 47 | }, 48 | "content-disposition": { 49 | "version": "0.5.3", 50 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 51 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 52 | "requires": { 53 | "safe-buffer": "5.1.2" 54 | } 55 | }, 56 | "content-type": { 57 | "version": "1.0.4", 58 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 59 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 60 | }, 61 | "cookie": { 62 | "version": "0.4.0", 63 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 64 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 65 | }, 66 | "cookie-signature": { 67 | "version": "1.0.6", 68 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 69 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 70 | }, 71 | "cors": { 72 | "version": "2.8.5", 73 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 74 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 75 | "requires": { 76 | "object-assign": "^4", 77 | "vary": "^1" 78 | } 79 | }, 80 | "debug": { 81 | "version": "2.6.9", 82 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 83 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 84 | "requires": { 85 | "ms": "2.0.0" 86 | } 87 | }, 88 | "depd": { 89 | "version": "1.1.2", 90 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 91 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 92 | }, 93 | "destroy": { 94 | "version": "1.0.4", 95 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 96 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 97 | }, 98 | "ee-first": { 99 | "version": "1.1.1", 100 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 101 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 102 | }, 103 | "encodeurl": { 104 | "version": "1.0.2", 105 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 106 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 107 | }, 108 | "escape-html": { 109 | "version": "1.0.3", 110 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 111 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 112 | }, 113 | "etag": { 114 | "version": "1.8.1", 115 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 116 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 117 | }, 118 | "express": { 119 | "version": "4.17.1", 120 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 121 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 122 | "requires": { 123 | "accepts": "~1.3.7", 124 | "array-flatten": "1.1.1", 125 | "body-parser": "1.19.0", 126 | "content-disposition": "0.5.3", 127 | "content-type": "~1.0.4", 128 | "cookie": "0.4.0", 129 | "cookie-signature": "1.0.6", 130 | "debug": "2.6.9", 131 | "depd": "~1.1.2", 132 | "encodeurl": "~1.0.2", 133 | "escape-html": "~1.0.3", 134 | "etag": "~1.8.1", 135 | "finalhandler": "~1.1.2", 136 | "fresh": "0.5.2", 137 | "merge-descriptors": "1.0.1", 138 | "methods": "~1.1.2", 139 | "on-finished": "~2.3.0", 140 | "parseurl": "~1.3.3", 141 | "path-to-regexp": "0.1.7", 142 | "proxy-addr": "~2.0.5", 143 | "qs": "6.7.0", 144 | "range-parser": "~1.2.1", 145 | "safe-buffer": "5.1.2", 146 | "send": "0.17.1", 147 | "serve-static": "1.14.1", 148 | "setprototypeof": "1.1.1", 149 | "statuses": "~1.5.0", 150 | "type-is": "~1.6.18", 151 | "utils-merge": "1.0.1", 152 | "vary": "~1.1.2" 153 | } 154 | }, 155 | "finalhandler": { 156 | "version": "1.1.2", 157 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 158 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 159 | "requires": { 160 | "debug": "2.6.9", 161 | "encodeurl": "~1.0.2", 162 | "escape-html": "~1.0.3", 163 | "on-finished": "~2.3.0", 164 | "parseurl": "~1.3.3", 165 | "statuses": "~1.5.0", 166 | "unpipe": "~1.0.0" 167 | } 168 | }, 169 | "forwarded": { 170 | "version": "0.1.2", 171 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 172 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 173 | }, 174 | "fresh": { 175 | "version": "0.5.2", 176 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 177 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 178 | }, 179 | "http-errors": { 180 | "version": "1.7.2", 181 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 182 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 183 | "requires": { 184 | "depd": "~1.1.2", 185 | "inherits": "2.0.3", 186 | "setprototypeof": "1.1.1", 187 | "statuses": ">= 1.5.0 < 2", 188 | "toidentifier": "1.0.0" 189 | } 190 | }, 191 | "iconv-lite": { 192 | "version": "0.4.24", 193 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 194 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 195 | "requires": { 196 | "safer-buffer": ">= 2.1.2 < 3" 197 | } 198 | }, 199 | "inherits": { 200 | "version": "2.0.3", 201 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 202 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 203 | }, 204 | "ipaddr.js": { 205 | "version": "1.9.0", 206 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 207 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 208 | }, 209 | "jwt-simple": { 210 | "version": "0.5.6", 211 | "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.6.tgz", 212 | "integrity": "sha512-40aUybvhH9t2h71ncA1/1SbtTNCVZHgsTsTgqPUxGWDmUDrXyDf2wMNQKEbdBjbf4AI+fQhbECNTV6lWxQKUzg==" 213 | }, 214 | "media-typer": { 215 | "version": "0.3.0", 216 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 217 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 218 | }, 219 | "merge-descriptors": { 220 | "version": "1.0.1", 221 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 222 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 223 | }, 224 | "methods": { 225 | "version": "1.1.2", 226 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 227 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 228 | }, 229 | "mime": { 230 | "version": "1.6.0", 231 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 232 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 233 | }, 234 | "mime-db": { 235 | "version": "1.42.0", 236 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", 237 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" 238 | }, 239 | "mime-types": { 240 | "version": "2.1.25", 241 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", 242 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", 243 | "requires": { 244 | "mime-db": "1.42.0" 245 | } 246 | }, 247 | "moment": { 248 | "version": "2.24.0", 249 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 250 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" 251 | }, 252 | "ms": { 253 | "version": "2.0.0", 254 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 255 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 256 | }, 257 | "negotiator": { 258 | "version": "0.6.2", 259 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 260 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 261 | }, 262 | "object-assign": { 263 | "version": "4.1.1", 264 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 265 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 266 | }, 267 | "on-finished": { 268 | "version": "2.3.0", 269 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 270 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 271 | "requires": { 272 | "ee-first": "1.1.1" 273 | } 274 | }, 275 | "parseurl": { 276 | "version": "1.3.3", 277 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 278 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 279 | }, 280 | "path-to-regexp": { 281 | "version": "0.1.7", 282 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 283 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 284 | }, 285 | "promise": { 286 | "version": "8.0.3", 287 | "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.3.tgz", 288 | "integrity": "sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw==", 289 | "requires": { 290 | "asap": "~2.0.6" 291 | } 292 | }, 293 | "proxy-addr": { 294 | "version": "2.0.5", 295 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 296 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 297 | "requires": { 298 | "forwarded": "~0.1.2", 299 | "ipaddr.js": "1.9.0" 300 | } 301 | }, 302 | "qs": { 303 | "version": "6.7.0", 304 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 305 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 306 | }, 307 | "range-parser": { 308 | "version": "1.2.1", 309 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 310 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 311 | }, 312 | "raw-body": { 313 | "version": "2.4.0", 314 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 315 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 316 | "requires": { 317 | "bytes": "3.1.0", 318 | "http-errors": "1.7.2", 319 | "iconv-lite": "0.4.24", 320 | "unpipe": "1.0.0" 321 | } 322 | }, 323 | "safe-buffer": { 324 | "version": "5.1.2", 325 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 326 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 327 | }, 328 | "safer-buffer": { 329 | "version": "2.1.2", 330 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 331 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 332 | }, 333 | "send": { 334 | "version": "0.17.1", 335 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 336 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 337 | "requires": { 338 | "debug": "2.6.9", 339 | "depd": "~1.1.2", 340 | "destroy": "~1.0.4", 341 | "encodeurl": "~1.0.2", 342 | "escape-html": "~1.0.3", 343 | "etag": "~1.8.1", 344 | "fresh": "0.5.2", 345 | "http-errors": "~1.7.2", 346 | "mime": "1.6.0", 347 | "ms": "2.1.1", 348 | "on-finished": "~2.3.0", 349 | "range-parser": "~1.2.1", 350 | "statuses": "~1.5.0" 351 | }, 352 | "dependencies": { 353 | "ms": { 354 | "version": "2.1.1", 355 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 356 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 357 | } 358 | } 359 | }, 360 | "serve-static": { 361 | "version": "1.14.1", 362 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 363 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 364 | "requires": { 365 | "encodeurl": "~1.0.2", 366 | "escape-html": "~1.0.3", 367 | "parseurl": "~1.3.3", 368 | "send": "0.17.1" 369 | } 370 | }, 371 | "setprototypeof": { 372 | "version": "1.1.1", 373 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 374 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 375 | }, 376 | "statuses": { 377 | "version": "1.5.0", 378 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 379 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 380 | }, 381 | "toidentifier": { 382 | "version": "1.0.0", 383 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 384 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 385 | }, 386 | "type-is": { 387 | "version": "1.6.18", 388 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 389 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 390 | "requires": { 391 | "media-typer": "0.3.0", 392 | "mime-types": "~2.1.24" 393 | } 394 | }, 395 | "unpipe": { 396 | "version": "1.0.0", 397 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 398 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 399 | }, 400 | "utils-merge": { 401 | "version": "1.0.1", 402 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 403 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 404 | }, 405 | "vary": { 406 | "version": "1.1.2", 407 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 408 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 409 | } 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /auth-svc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-svc", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "cors": "^2.8.5", 15 | "express": "^4.17.1", 16 | "jwt-simple": "^0.5.6", 17 | "moment": "^2.24.0", 18 | "promise": "^8.0.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | reverse-proxy: 5 | image: traefik:v2.1 6 | command: --api.insecure=true --providers.docker --log.level=DEBUG 7 | ports: 8 | - "80:80" 9 | - "81:8080" 10 | networks: 11 | - api 12 | - auth 13 | volumes: 14 | - /var/run/docker.sock:/var/run/docker.sock 15 | 16 | svc1: 17 | build: 18 | context: ./svc1 19 | dockerfile: ./Dockerfile 20 | ports: 21 | - "3000:3000" 22 | networks: 23 | - api 24 | labels: 25 | - "traefik.http.routers.svc1.rule=PathPrefix(`/svc1/`)" 26 | - "traefik.http.routers.svc1.middlewares=svc1-stripprefix, svc1-forwardauth-address, svc1-forwardauth-trust" 27 | - "traefik.http.middlewares.svc1-stripprefix.stripprefix.prefixes=/svc1" 28 | - "traefik.http.middlewares.svc1-forwardauth-address.forwardauth.address=http://auth-svc:3000/verify" 29 | - "traefik.http.middlewares.svc1-forwardauth-trust.forwardauth.trustForwardHeader=false" 30 | # - "traefik.http.middlewares.svc1-forwardauth-headers.forwardauth.authResponseHeaders=X-Injected-UserInfo" 31 | - "traefik.http.services.svc1.loadbalancer.server.port=3000" 32 | 33 | auth-svc: 34 | build: 35 | context: ./auth-svc 36 | dockerfile: ./Dockerfile 37 | ports: 38 | - "8889:3000" 39 | environment: 40 | - JWT_SECRET_PATH=/run/secrets/jwt_secret_token 41 | networks: 42 | - auth 43 | secrets: 44 | - jwt_secret_token 45 | secrets: 46 | - source: jwt_secret_token 47 | target: jwt_secret_token 48 | 49 | keycloak: 50 | image: jboss/keycloak:6.0.1 51 | ports: 52 | - "8888:8080" 53 | environment: 54 | DB_VENDOR: postgres 55 | DB_ADDR: keycloak-db 56 | DB_PORT: 5432 57 | DB_DATABASE: keycloak 58 | DB_USER: keycloak 59 | DB_PASSWORD: password 60 | KEYCLOAK_USER_FILE: /run/secrets/keycloak_user 61 | KEYCLOAK_PASSWORD_FILE: /run/secrets/keycloak_password 62 | # https://stackoverflow.com/a/47200330/684966 63 | PROXY_ADDRESS_FORWARDING: "true" 64 | secrets: 65 | - keycloak_user 66 | - keycloak_password 67 | networks: 68 | # - api 69 | - auth 70 | labels: 71 | - "traefik.http.routers.auth.rule=PathPrefix(`/auth`)" 72 | - "traefik.http.services.auth.loadbalancer.server.port=8080" 73 | deploy: 74 | mode: replicated 75 | replicas: 1 76 | labels: 77 | - "traefik.http.routers.auth.rule=PathPrefix(`/auth`)" 78 | - "traefik.http.services.auth.loadbalancer.server.port=8080" 79 | 80 | keycloak-db: 81 | image: postgres:11.1-alpine 82 | ports: 83 | - target: 5432 84 | environment: 85 | POSTGRES_DB: keycloak 86 | POSTGRES_USER: keycloak 87 | POSTGRES_PASSWORD: password 88 | networks: 89 | - auth 90 | 91 | ldap: 92 | image: rroemhild/test-openldap 93 | ports: 94 | - "389:389" 95 | - "636:636" 96 | networks: 97 | - auth 98 | 99 | secrets: 100 | keycloak_user: 101 | file: ./keycloak_user_file.txt 102 | keycloak_password: 103 | file: ./keycloak_password_file.txt 104 | jwt_secret_token: 105 | file: ./auth-svc-data/jwt-secret.txt 106 | 107 | networks: 108 | auth: 109 | api: 110 | -------------------------------------------------------------------------------- /docs/AuthenticationSequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregberns/forward-auth/5cd7d42116e3738a2f05bc2c3f3f5b10d5655a5e/docs/AuthenticationSequence.png -------------------------------------------------------------------------------- /docs/sequence.txt: -------------------------------------------------------------------------------- 1 | title Authentication Sequence 2 | 3 | Client->Reverse-Proxy: Auth Request 4 | Reverse-Proxy->+Auth-Server: Auth Request 5 | alt 200 6 | Auth-Server->-Client: JWT Auth Token 7 | else 401 8 | Auth-Server-->-Client: Auth Failed 9 | end 10 | 11 | Client->Reverse-Proxy: Application Request 12 | Reverse-Proxy->+Auth-Server: Validate JWT Token 13 | 14 | alt 200 15 | Auth-Server->-Reverse-Proxy: Is Valid Token 16 | Reverse-Proxy->+Application-Server: Application Request 17 | Application-Server->-Client: Application Response 18 | else 401 19 | Auth-Server-->-Client: Invalid Token 20 | end -------------------------------------------------------------------------------- /keycloak_password_file.txt: -------------------------------------------------------------------------------- 1 | password -------------------------------------------------------------------------------- /keycloak_user_file.txt: -------------------------------------------------------------------------------- 1 | admin -------------------------------------------------------------------------------- /ldap-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ldap": { 3 | "url": "ldap://ldap.forumsys.com:389", 4 | "bindCredentials": "password", 5 | "searchBase": "dc=example,dc=com", 6 | "searchFilter": "(uid={{username}})", 7 | "timeout": 5000, 8 | "connectTimeout": 10000, 9 | "reconnect": true 10 | }, 11 | "jwt": { 12 | "secret": "test_secret" 13 | } 14 | } -------------------------------------------------------------------------------- /svc1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.15.1-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY index.js ./ 10 | # COPY ldap-config.json ./ 11 | 12 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /svc1/index.js: -------------------------------------------------------------------------------- 1 | //https://github.com/lucianweber/ldap-jwt/blob/master/index.js 2 | 3 | var bodyParser = require('body-parser'); 4 | var jwt = require('jwt-simple'); 5 | var moment = require('moment'); 6 | // var LdapAuth = require('ldapauth-fork'); 7 | var Promise = require('promise'); 8 | 9 | app = require('express')(); 10 | 11 | app.use(bodyParser.json()); 12 | app.use(bodyParser.urlencoded({ extended: false })); 13 | app.use(require('cors')()); 14 | app.disable('x-powered-by'); 15 | 16 | // var settings = require('./ldap-config.json'); 17 | // var auth = new LdapAuth(settings.ldap); 18 | 19 | app.set('jwtTokenSecret', "test_secret"); //settings.jwt.secret); 20 | 21 | // var authenticate = function (username, password) { 22 | // return new Promise(function (resolve, reject) { 23 | // // Hardcode credentials for now 24 | // if (username === "admin") { 25 | // resolve({ 26 | // uid: "123", 27 | // cn: "admin", 28 | // mail: "admin@test.com", 29 | // }); 30 | // } else { 31 | // reject({ 32 | // name: 'InvalidCredentialsError' 33 | // }); 34 | // } 35 | 36 | // // auth.authenticate(username, password, function (err, user) { 37 | // // if(err) 38 | // // reject(err); 39 | // // else if (!user) 40 | // // reject(); 41 | // // else 42 | // // resolve(user); 43 | // // }); 44 | // }); 45 | // }; 46 | 47 | app.get('/health', function (req, res) { 48 | console.log('health') 49 | res.json({ success: true }); 50 | }) 51 | 52 | // app.post('/auth', function (req, res) { 53 | // if(req.body.username && req.body.password) { 54 | // authenticate(req.body.username, req.body.password) 55 | // .then(function(user) { 56 | // var expires = parseInt(moment().add(2, 'days').format("X")); 57 | 58 | // console.log('user', user) 59 | 60 | // var secret = app.get('jwtTokenSecret') 61 | 62 | // console.log('secret', secret) 63 | 64 | // var token = jwt.encode({ 65 | // exp: expires, 66 | // user_name: user.uid, 67 | // full_name: user.cn, 68 | // mail: user.mail 69 | // }, secret); 70 | 71 | // res.json({token: token, full_name: user.cn}); 72 | // }) 73 | // .catch(function (err) { 74 | // // Ldap reconnect config needs to be set to true to reliably 75 | // // land in this catch when the connection to the ldap server goes away. 76 | // // REF: https://github.com/vesse/node-ldapauth-fork/issues/23#issuecomment-154487871 77 | 78 | // console.log(err); 79 | 80 | // if (err.name === 'InvalidCredentialsError' || (typeof err === 'string' && err.match(/no such user/i)) ) { 81 | // res.status(401).send({ error: 'Wrong user or password'}); 82 | // } else { 83 | // // ldapauth-fork or underlying connections may be in an unusable state. 84 | // // Reconnect option does re-establish the connections, but will not 85 | // // re-bind. Create a new instance of LdapAuth. 86 | // // REF: https://github.com/vesse/node-ldapauth-fork/issues/23 87 | // // REF: https://github.com/mcavage/node-ldapjs/issues/318 88 | 89 | // res.status(500).send({ error: 'Unexpected Error'}); 90 | // auth = new LdapAuth(settings.ldap); 91 | // } 92 | 93 | // }); 94 | // } else { 95 | // res.status(400).send({error: 'No username or password supplied'}); 96 | // } 97 | // }) 98 | 99 | app.get('/verify', function (req, res) { 100 | console.log('AuthHeader', req.headers.authorization) 101 | 102 | if (!req.headers.authorization) { 103 | res.status(400).send({ error: 'Authorization Header is missing' }); 104 | return; 105 | } 106 | 107 | const [bearer, token] = req.headers.authorization.split(' '); 108 | // req.headers.authorization.split(' ')[0] 109 | if (bearer !== 'Bearer') { 110 | res.status(400).send({ error: 'Authorization Header does not contain Bearer token' }); 111 | return; 112 | } 113 | 114 | if (!token) { 115 | res.status(400).send({ error: 'Authorization Header Access token is missing' }); 116 | return; 117 | } 118 | 119 | var decoded = null; 120 | try { 121 | decoded = jwt.decode(token, app.get('jwtTokenSecret')); 122 | } catch (err) { 123 | res.status(400).send({ error: 'Invalid Access Token. Could not be decoded.' }); 124 | return; 125 | } 126 | 127 | if (decoded.exp <= parseInt(moment().format("X"))) { 128 | res.status(400).send({ error: 'Access token has expired' }); 129 | } else { 130 | res.status(200).send(); 131 | } 132 | 133 | }); 134 | 135 | // app.get('/verify', function (req, res) { 136 | // console.log('/verify called') 137 | // if (req.headers.authorization 138 | // && req.headers.authorization.split(' ')[0] === 'Bearer') { 139 | // console.log('Verify Auth Successful') 140 | // res.json({success: true}); 141 | // } else { 142 | // console.log('Verify Failed') 143 | // res.status(401).send() 144 | // } 145 | // }) 146 | 147 | app.get('/secure', function (req, res) { 148 | console.log(`/secure called. headers: ${JSON.stringify(req.headers)}`) 149 | if (req.headers.authorization 150 | && req.headers.authorization.split(' ')[0] === 'Bearer') { 151 | console.log('Verify Auth Successful') 152 | res.json({ success: true }); 153 | } else { 154 | console.log('Verify Failed') 155 | res.status(401).send() 156 | } 157 | }) 158 | 159 | var port = (process.env.PORT || 3000); 160 | app.listen(port, function () { 161 | console.log('Listening on port: ' + port); 162 | 163 | // if (typeof settings.ldap.reconnect === 'undefined' || settings.ldap.reconnect === null || settings.ldap.reconnect === false) { 164 | // console.warn('WARN: This service may become unresponsive when ldap reconnect is not configured.') 165 | // } 166 | }); 167 | -------------------------------------------------------------------------------- /svc1/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forward-ldap-auth", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "asap": { 22 | "version": "2.0.6", 23 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 24 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 25 | }, 26 | "asn1": { 27 | "version": "0.2.3", 28 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 29 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 30 | }, 31 | "assert-plus": { 32 | "version": "0.1.5", 33 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", 34 | "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" 35 | }, 36 | "backoff": { 37 | "version": "2.4.1", 38 | "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.4.1.tgz", 39 | "integrity": "sha1-L2jFDg3Xidvv4kIApi77BNJFbWg=", 40 | "requires": { 41 | "precond": "0.2" 42 | } 43 | }, 44 | "balanced-match": { 45 | "version": "1.0.0", 46 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 47 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 48 | "optional": true 49 | }, 50 | "bcryptjs": { 51 | "version": "2.3.0", 52 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz", 53 | "integrity": "sha1-WCaQDP73q680JccuTUZN5Qm4wuw=" 54 | }, 55 | "body-parser": { 56 | "version": "1.18.3", 57 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 58 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 59 | "requires": { 60 | "bytes": "3.0.0", 61 | "content-type": "~1.0.4", 62 | "debug": "2.6.9", 63 | "depd": "~1.1.2", 64 | "http-errors": "~1.6.3", 65 | "iconv-lite": "0.4.23", 66 | "on-finished": "~2.3.0", 67 | "qs": "6.5.2", 68 | "raw-body": "2.3.3", 69 | "type-is": "~1.6.16" 70 | } 71 | }, 72 | "brace-expansion": { 73 | "version": "1.1.11", 74 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 75 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 76 | "optional": true, 77 | "requires": { 78 | "balanced-match": "^1.0.0", 79 | "concat-map": "0.0.1" 80 | } 81 | }, 82 | "bunyan": { 83 | "version": "1.5.1", 84 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", 85 | "integrity": "sha1-X259RMQ7lS9WsPQTCeOrEjkbTi0=", 86 | "requires": { 87 | "dtrace-provider": "~0.6", 88 | "mv": "~2", 89 | "safe-json-stringify": "~1" 90 | } 91 | }, 92 | "bytes": { 93 | "version": "3.0.0", 94 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 95 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 96 | }, 97 | "concat-map": { 98 | "version": "0.0.1", 99 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 100 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 101 | "optional": true 102 | }, 103 | "content-disposition": { 104 | "version": "0.5.2", 105 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 106 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 107 | }, 108 | "content-type": { 109 | "version": "1.0.4", 110 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 111 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 112 | }, 113 | "cookie": { 114 | "version": "0.3.1", 115 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 116 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 117 | }, 118 | "cookie-signature": { 119 | "version": "1.0.6", 120 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 121 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 122 | }, 123 | "cors": { 124 | "version": "2.7.1", 125 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.7.1.tgz", 126 | "integrity": "sha1-PC5QpYr574yJvuISJrCZvh8Cc5s=", 127 | "requires": { 128 | "vary": "^1" 129 | } 130 | }, 131 | "dashdash": { 132 | "version": "1.10.1", 133 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.10.1.tgz", 134 | "integrity": "sha1-Cr8a+JqPUSmoHxjCs1sh3yJiL2A=", 135 | "requires": { 136 | "assert-plus": "0.1.x" 137 | } 138 | }, 139 | "debug": { 140 | "version": "2.6.9", 141 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 142 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 143 | "requires": { 144 | "ms": "2.0.0" 145 | } 146 | }, 147 | "depd": { 148 | "version": "1.1.2", 149 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 150 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 151 | }, 152 | "destroy": { 153 | "version": "1.0.4", 154 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 155 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 156 | }, 157 | "dtrace-provider": { 158 | "version": "0.6.0", 159 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", 160 | "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", 161 | "optional": true, 162 | "requires": { 163 | "nan": "^2.0.8" 164 | } 165 | }, 166 | "ee-first": { 167 | "version": "1.1.1", 168 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 169 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 170 | }, 171 | "encodeurl": { 172 | "version": "1.0.2", 173 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 174 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 175 | }, 176 | "escape-html": { 177 | "version": "1.0.3", 178 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 179 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 180 | }, 181 | "etag": { 182 | "version": "1.8.1", 183 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 184 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 185 | }, 186 | "express": { 187 | "version": "4.16.4", 188 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 189 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 190 | "requires": { 191 | "accepts": "~1.3.5", 192 | "array-flatten": "1.1.1", 193 | "body-parser": "1.18.3", 194 | "content-disposition": "0.5.2", 195 | "content-type": "~1.0.4", 196 | "cookie": "0.3.1", 197 | "cookie-signature": "1.0.6", 198 | "debug": "2.6.9", 199 | "depd": "~1.1.2", 200 | "encodeurl": "~1.0.2", 201 | "escape-html": "~1.0.3", 202 | "etag": "~1.8.1", 203 | "finalhandler": "1.1.1", 204 | "fresh": "0.5.2", 205 | "merge-descriptors": "1.0.1", 206 | "methods": "~1.1.2", 207 | "on-finished": "~2.3.0", 208 | "parseurl": "~1.3.2", 209 | "path-to-regexp": "0.1.7", 210 | "proxy-addr": "~2.0.4", 211 | "qs": "6.5.2", 212 | "range-parser": "~1.2.0", 213 | "safe-buffer": "5.1.2", 214 | "send": "0.16.2", 215 | "serve-static": "1.13.2", 216 | "setprototypeof": "1.1.0", 217 | "statuses": "~1.4.0", 218 | "type-is": "~1.6.16", 219 | "utils-merge": "1.0.1", 220 | "vary": "~1.1.2" 221 | }, 222 | "dependencies": { 223 | "statuses": { 224 | "version": "1.4.0", 225 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 226 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 227 | } 228 | } 229 | }, 230 | "extsprintf": { 231 | "version": "1.2.0", 232 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", 233 | "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=" 234 | }, 235 | "finalhandler": { 236 | "version": "1.1.1", 237 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 238 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 239 | "requires": { 240 | "debug": "2.6.9", 241 | "encodeurl": "~1.0.2", 242 | "escape-html": "~1.0.3", 243 | "on-finished": "~2.3.0", 244 | "parseurl": "~1.3.2", 245 | "statuses": "~1.4.0", 246 | "unpipe": "~1.0.0" 247 | }, 248 | "dependencies": { 249 | "statuses": { 250 | "version": "1.4.0", 251 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 252 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 253 | } 254 | } 255 | }, 256 | "forwarded": { 257 | "version": "0.1.2", 258 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 259 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 260 | }, 261 | "fresh": { 262 | "version": "0.5.2", 263 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 264 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 265 | }, 266 | "glob": { 267 | "version": "6.0.4", 268 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 269 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 270 | "optional": true, 271 | "requires": { 272 | "inflight": "^1.0.4", 273 | "inherits": "2", 274 | "minimatch": "2 || 3", 275 | "once": "^1.3.0", 276 | "path-is-absolute": "^1.0.0" 277 | } 278 | }, 279 | "http-errors": { 280 | "version": "1.6.3", 281 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 282 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 283 | "requires": { 284 | "depd": "~1.1.2", 285 | "inherits": "2.0.3", 286 | "setprototypeof": "1.1.0", 287 | "statuses": ">= 1.4.0 < 2" 288 | }, 289 | "dependencies": { 290 | "inherits": { 291 | "version": "2.0.3", 292 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 293 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 294 | } 295 | } 296 | }, 297 | "iconv-lite": { 298 | "version": "0.4.23", 299 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 300 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 301 | "requires": { 302 | "safer-buffer": ">= 2.1.2 < 3" 303 | } 304 | }, 305 | "inflight": { 306 | "version": "1.0.6", 307 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 308 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 309 | "optional": true, 310 | "requires": { 311 | "once": "^1.3.0", 312 | "wrappy": "1" 313 | } 314 | }, 315 | "inherits": { 316 | "version": "2.0.1", 317 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 318 | "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", 319 | "optional": true 320 | }, 321 | "ipaddr.js": { 322 | "version": "1.8.0", 323 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 324 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 325 | }, 326 | "jwt-simple": { 327 | "version": "0.5.6", 328 | "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.6.tgz", 329 | "integrity": "sha512-40aUybvhH9t2h71ncA1/1SbtTNCVZHgsTsTgqPUxGWDmUDrXyDf2wMNQKEbdBjbf4AI+fQhbECNTV6lWxQKUzg==" 330 | }, 331 | "ldap-filter": { 332 | "version": "0.2.2", 333 | "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", 334 | "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=", 335 | "requires": { 336 | "assert-plus": "0.1.5" 337 | } 338 | }, 339 | "ldapauth-fork": { 340 | "version": "2.5.2", 341 | "resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-2.5.2.tgz", 342 | "integrity": "sha1-eDmSc5YvIc3oTEpMD2iVC2Uh5tU=", 343 | "requires": { 344 | "bcryptjs": "2.3.0", 345 | "ldapjs": "1.0.0", 346 | "lru-cache": "3.2.0" 347 | } 348 | }, 349 | "ldapjs": { 350 | "version": "1.0.0", 351 | "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.0.tgz", 352 | "integrity": "sha1-HaLNW/ucsQPBulFpONqXG8K7w/I=", 353 | "requires": { 354 | "asn1": "0.2.3", 355 | "assert-plus": "0.1.5", 356 | "backoff": "2.4.1", 357 | "bunyan": "1.5.1", 358 | "dashdash": "1.10.1", 359 | "dtrace-provider": "0.6.0", 360 | "ldap-filter": "0.2.2", 361 | "once": "1.3.2", 362 | "vasync": "1.6.3", 363 | "verror": "1.6.0" 364 | } 365 | }, 366 | "lru-cache": { 367 | "version": "3.2.0", 368 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", 369 | "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", 370 | "requires": { 371 | "pseudomap": "^1.0.1" 372 | } 373 | }, 374 | "media-typer": { 375 | "version": "0.3.0", 376 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 377 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 378 | }, 379 | "merge-descriptors": { 380 | "version": "1.0.1", 381 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 382 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 383 | }, 384 | "methods": { 385 | "version": "1.1.2", 386 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 387 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 388 | }, 389 | "mime": { 390 | "version": "1.4.1", 391 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 392 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 393 | }, 394 | "mime-db": { 395 | "version": "1.38.0", 396 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 397 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" 398 | }, 399 | "mime-types": { 400 | "version": "2.1.22", 401 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 402 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 403 | "requires": { 404 | "mime-db": "~1.38.0" 405 | } 406 | }, 407 | "minimatch": { 408 | "version": "3.0.4", 409 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 410 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 411 | "optional": true, 412 | "requires": { 413 | "brace-expansion": "^1.1.7" 414 | } 415 | }, 416 | "minimist": { 417 | "version": "0.0.8", 418 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 419 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 420 | "optional": true 421 | }, 422 | "mkdirp": { 423 | "version": "0.5.1", 424 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 425 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 426 | "optional": true, 427 | "requires": { 428 | "minimist": "0.0.8" 429 | } 430 | }, 431 | "moment": { 432 | "version": "2.21.0", 433 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", 434 | "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" 435 | }, 436 | "ms": { 437 | "version": "2.0.0", 438 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 439 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 440 | }, 441 | "mv": { 442 | "version": "2.1.1", 443 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 444 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 445 | "optional": true, 446 | "requires": { 447 | "mkdirp": "~0.5.1", 448 | "ncp": "~2.0.0", 449 | "rimraf": "~2.4.0" 450 | } 451 | }, 452 | "nan": { 453 | "version": "2.13.2", 454 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", 455 | "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", 456 | "optional": true 457 | }, 458 | "ncp": { 459 | "version": "2.0.0", 460 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 461 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 462 | "optional": true 463 | }, 464 | "negotiator": { 465 | "version": "0.6.1", 466 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 467 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 468 | }, 469 | "on-finished": { 470 | "version": "2.3.0", 471 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 472 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 473 | "requires": { 474 | "ee-first": "1.1.1" 475 | } 476 | }, 477 | "once": { 478 | "version": "1.3.2", 479 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", 480 | "integrity": "sha1-2P7sqTsDnsHc3ud0HJK9rF4oCBs=", 481 | "requires": { 482 | "wrappy": "1" 483 | } 484 | }, 485 | "parseurl": { 486 | "version": "1.3.2", 487 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 488 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 489 | }, 490 | "path-is-absolute": { 491 | "version": "1.0.1", 492 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 493 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 494 | "optional": true 495 | }, 496 | "path-to-regexp": { 497 | "version": "0.1.7", 498 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 499 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 500 | }, 501 | "precond": { 502 | "version": "0.2.3", 503 | "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", 504 | "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" 505 | }, 506 | "promise": { 507 | "version": "7.3.1", 508 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", 509 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 510 | "requires": { 511 | "asap": "~2.0.3" 512 | } 513 | }, 514 | "proxy-addr": { 515 | "version": "2.0.4", 516 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 517 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 518 | "requires": { 519 | "forwarded": "~0.1.2", 520 | "ipaddr.js": "1.8.0" 521 | } 522 | }, 523 | "pseudomap": { 524 | "version": "1.0.2", 525 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 526 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 527 | }, 528 | "qs": { 529 | "version": "6.5.2", 530 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 531 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 532 | }, 533 | "range-parser": { 534 | "version": "1.2.0", 535 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 536 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 537 | }, 538 | "raw-body": { 539 | "version": "2.3.3", 540 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 541 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 542 | "requires": { 543 | "bytes": "3.0.0", 544 | "http-errors": "1.6.3", 545 | "iconv-lite": "0.4.23", 546 | "unpipe": "1.0.0" 547 | } 548 | }, 549 | "rimraf": { 550 | "version": "2.4.5", 551 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 552 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 553 | "optional": true, 554 | "requires": { 555 | "glob": "^6.0.1" 556 | } 557 | }, 558 | "safe-buffer": { 559 | "version": "5.1.2", 560 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 561 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 562 | }, 563 | "safe-json-stringify": { 564 | "version": "1.2.0", 565 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", 566 | "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", 567 | "optional": true 568 | }, 569 | "safer-buffer": { 570 | "version": "2.1.2", 571 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 572 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 573 | }, 574 | "send": { 575 | "version": "0.16.2", 576 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 577 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 578 | "requires": { 579 | "debug": "2.6.9", 580 | "depd": "~1.1.2", 581 | "destroy": "~1.0.4", 582 | "encodeurl": "~1.0.2", 583 | "escape-html": "~1.0.3", 584 | "etag": "~1.8.1", 585 | "fresh": "0.5.2", 586 | "http-errors": "~1.6.2", 587 | "mime": "1.4.1", 588 | "ms": "2.0.0", 589 | "on-finished": "~2.3.0", 590 | "range-parser": "~1.2.0", 591 | "statuses": "~1.4.0" 592 | }, 593 | "dependencies": { 594 | "statuses": { 595 | "version": "1.4.0", 596 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 597 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 598 | } 599 | } 600 | }, 601 | "serve-static": { 602 | "version": "1.13.2", 603 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 604 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 605 | "requires": { 606 | "encodeurl": "~1.0.2", 607 | "escape-html": "~1.0.3", 608 | "parseurl": "~1.3.2", 609 | "send": "0.16.2" 610 | } 611 | }, 612 | "setprototypeof": { 613 | "version": "1.1.0", 614 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 615 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 616 | }, 617 | "statuses": { 618 | "version": "1.5.0", 619 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 620 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 621 | }, 622 | "type-is": { 623 | "version": "1.6.16", 624 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 625 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 626 | "requires": { 627 | "media-typer": "0.3.0", 628 | "mime-types": "~2.1.18" 629 | } 630 | }, 631 | "unpipe": { 632 | "version": "1.0.0", 633 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 634 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 635 | }, 636 | "utils-merge": { 637 | "version": "1.0.1", 638 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 639 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 640 | }, 641 | "vary": { 642 | "version": "1.1.2", 643 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 644 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 645 | }, 646 | "vasync": { 647 | "version": "1.6.3", 648 | "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.3.tgz", 649 | "integrity": "sha1-SmnXBSpH9M6FUD12Qd8cv0BDKpQ=", 650 | "requires": { 651 | "verror": "1.6.0" 652 | } 653 | }, 654 | "verror": { 655 | "version": "1.6.0", 656 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", 657 | "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", 658 | "requires": { 659 | "extsprintf": "1.2.0" 660 | } 661 | }, 662 | "wrappy": { 663 | "version": "1.0.2", 664 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 665 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 666 | } 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /svc1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forward-ldap-auth", 3 | "version": "1.0.0", 4 | "description": "* Setup Traefik to route traffic to a 'whoami' service * Add Forward Auth in Traefik to Auth service * Auth Service accepts", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.18.3", 14 | "cors": "2.7.1", 15 | "express": "^4.16.4", 16 | "jwt-simple": "^0.5.6", 17 | "ldapauth-fork": "2.5.2", 18 | "moment": "2.21.0", 19 | "promise": "^7.1.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /traefik.toml: -------------------------------------------------------------------------------- 1 | logLevel = "DEBUG" 2 | 3 | [accessLog] 4 | 5 | [api] 6 | dashboard = true 7 | 8 | [docker] 9 | endpoint = "unix:///var/run/docker.sock" 10 | watch = true 11 | domain = "docker.localhost" 12 | 13 | defaultEntryPoints = ["http", "https"] 14 | 15 | [entryPoints] 16 | 17 | # # For incoming authentication requests 18 | # [entryPoints.authentication] 19 | # address = ":79" 20 | 21 | # For all API requests that need to be authenticated 22 | [entryPoints.http] 23 | address = ":80" 24 | 25 | # [entryPoints.http.auth] 26 | # headerField = "Authorization" 27 | # # ... 28 | # # To enable forward auth on an entrypoint 29 | # [entryPoints.http.auth.forward] 30 | # address = "http://auth:3000/verify" 31 | 32 | # # Trust existing X-Forwarded-* headers. 33 | # # Useful with another reverse proxy in front of Traefik. 34 | # # 35 | # # Optional 36 | # # Default: false 37 | # # 38 | # trustForwardHeader = true 39 | 40 | # # Copy headers from the authentication server to the request. 41 | # # 42 | # # Optional 43 | # # 44 | # authResponseHeaders = ["X-Auth-User", "X-Secret", "X-Forwarded-User"] 45 | --------------------------------------------------------------------------------