Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authentication with multiple jwt providers #694

Merged
merged 42 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
62feeb9
refactor(aws-creds): move to aws-creds folder
Jan 31, 2022
5e85979
feat(auth): basic auth guard
MeStrak Jan 31, 2022
748f8b4
feat: begin support mutli jwt providers
MeStrak Feb 2, 2022
11d871a
feat: load from config
MeStrak Feb 2, 2022
76531c9
feat: get ExtractJwt func from string
MeStrak Feb 3, 2022
7474658
Add auth config to .env files
MeStrak Feb 3, 2022
1123176
test: add first auth e2e test
MeStrak Feb 4, 2022
03de5f8
chore: lint cleanup
MeStrak Feb 4, 2022
cf83380
style: run formatter
MeStrak Feb 4, 2022
4dc0524
test: fix src path
MeStrak Feb 4, 2022
164e8a6
stylr: run formatter
MeStrak Feb 4, 2022
8c75e86
test(auth e2e): valid JWT test
MeStrak Feb 4, 2022
d04df8c
style: fix linting errors
MeStrak Feb 4, 2022
f5ec1a6
test(auth e2e): add negative JWT tests
MeStrak Feb 5, 2022
2e42421
feat(config): make auth_config required
MeStrak Feb 5, 2022
0905e4c
test(load): correct auth config string
MeStrak Feb 6, 2022
5e40261
chore: exclude test code duplication detection
MeStrak Feb 7, 2022
345f35a
test: export testing costs
MeStrak Feb 7, 2022
cb8b9d9
feat: auth funcName and args as array
MeStrak Feb 7, 2022
487edd9
feat: Joi validation of auth config
MeStrak Feb 8, 2022
ec331e6
style: lint and format
MeStrak Feb 8, 2022
f2843cc
feat: improve joi auth config validation
MeStrak Feb 9, 2022
d74ab67
feat: update to AUTH_CONFIG_SECRET
MeStrak Feb 9, 2022
069789b
feat: remove async from jwt validate
MeStrak Feb 9, 2022
5b0c396
feat: coerce Joi object from JSON
MeStrak Feb 9, 2022
879565c
style: lint and format
MeStrak Feb 9, 2022
43d7487
docs: create security page
MeStrak Feb 9, 2022
4f2627a
feat: add passportJwtSecret support
MeStrak Feb 11, 2022
1a1a443
docs: add missing comma
MeStrak Feb 11, 2022
f2fe455
style: lint and format
MeStrak Feb 11, 2022
d96da7c
test: add initial getAuthConfig tests
MeStrak Feb 11, 2022
81ea110
test: add secretOrKey and some negative tests
MeStrak Feb 11, 2022
dd27d75
test: auth config
MeStrak Feb 13, 2022
4530512
test: add jwt.strategy unit test
MeStrak Feb 13, 2022
f8609e1
feat: load algorithms as array
MeStrak Feb 15, 2022
85bee88
test: add jwks e2e test
MeStrak Feb 15, 2022
b3075f4
docs: link to security page
MeStrak Feb 15, 2022
09f0331
chore: update example .envs with auth_config
MeStrak Feb 15, 2022
15e9be5
ci: fix test auth_config
MeStrak Feb 15, 2022
6bca22f
ci: revert previous change
MeStrak Feb 15, 2022
cb97336
test: install nock dependency
MeStrak Feb 15, 2022
56eeb98
feat(auth): conf and move to node 16
over-flo79 Feb 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: "12.x"
node-version: "16.x"

- name: Cache dependencies
uses: actions/cache@v2
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: "12.x"
node-version: "16.x"

- name: Cache dependencies
uses: actions/cache@v2
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/k6-load-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
AWS_S3_ENDPOINT: http://localhost:4566
AWS_ACCESS_KEY_ID: dummy
AWS_SECRET_ACCESS_KEY: dummy
AUTH_CONFIG_SECRET: '{"config":[{"jwtFromRequest":{"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKey":"VALID_SECRET_KEY", "secretOrKey":"VALID_SECRET_KEY"},{"jwtFromRequest": {"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKey":"another_secret_key"}]}'
services:
mongo:
image: mongo
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.spin-up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ then
fi
echo
echo "Setting node version just in case..."
nvm install 12.13.0
nvm install 16.14.0
echo
echo "Installing K6 for load testing..."
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ image:
tasks:
- init: |
touch /tmp/.npm-lock
npm install && npm run build && make && npm i -g @nestjs/cli && nvm install 12.13.0
npm install && npm run build && make && npm i -g @nestjs/cli && nvm install 16.14.0
rm /tmp/.npm-lock
name: init
- command: sudo docker-up
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NODE_VERSION=12.18.1
ARG NODE_VERSION=16.14.0

########################
# Builder stage
Expand Down
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'/introduction/',
'/architecture/',
'/installation.html',
'/security.html',
{
title: 'Api',
collapsable: false,
Expand Down
5 changes: 3 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ MONGOOSE_PORT_READ = 27018

REDIS_HOST = localhost
REDIS_HOST_READ = localhost
WHISPR_AUTH_CONFIG_SECRET = (change to your auth server - see [Security](./security.md))
```

### Setup a local AWS instance with localstack (optional)
Expand Down Expand Up @@ -189,9 +190,9 @@ Switch to Node 12 (required for some file upload dependencies)

```bash
# Setting Node version
nvm install 12.13.0
nvm install 16.14.0

# Check the node version is correctly set, must be 12.13.0
# Check the node version is correctly set, must be 16.14.0
node -v
```

Expand Down
82 changes: 82 additions & 0 deletions docs/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Security

## Authentication

Whispr supports authentication via JWT, and can be configured to authenticate against mutliple configurations (mutliple issuers, multiple secrets).

### Configuration

Security is implemented via an extended version of passport-jwt which allows an array of configurations to be passed on startup.

To configure, set the `AUTH_CONFIG_SECRET`

_ Note that even though the `AUTH_CONFIG_SECRET` is not necessarily a secret (in the case of config with JWKS providers), this environment variable is named `_SECRET` to ensure that it is hidden by APM tools in case it contains secret keys. _

The provided configuration is very similar to the NestJS passport-jwt example with a few modifications:
- it must be provided as a JSON string containing a `config` property which is an array of valid passport-jwt configurations
- the `jwtFromRequest` function which tells passport-jwt where to find the JWT in the request (usually the `Authorization` header) function must be deconstructed into an object containing funcName and args properties
- `passportJwtSecret` function should not be written in the configuration file, only the arguments are required in the `passportJwtSecret` object

### Examples

This is an example for multiple passport JWT configurations as they would be configured in Javascript.

```js
[{
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: "https://auth.issuer.com",
}),

jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: 'MY_APP_AUDIENCE',
issuer: `https://auth.issuer.com/`,
algorithms: ['RS256'],
},
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'APPX_SECRET_KEY',
},
{
jwtFromRequest: ExtractJwt.fromBodyField('USER_JWT'),
ignoreExpiration: false,
secretOrKey: 'APPY_SECRET_KEY',
}]
```

And an example env file configuration for `AUTH_CONFIG_SECRET` based on the configuration above.

```bash
AUTH_CONFIG_SECRET =
# note that in a .env file this should be formatted onto one line - it is shown multiline here for easier readability
# see project example.env file for a single line config
{"config":[{
"secretOrKeyProvider": {
"cache": true,
"rateLimit": true,
"jwksRequestsPerMinute": 5,
"jwksUri": "https://uri.com"
},
"jwtFromRequest":{"funcName": "fromAuthHeaderAsBearerToken"},
"audience": "MY_APP_AUDIENCE",
"issuer":"https://fact.cognito.com/",
"algorithms": ['RS256']
},
{
"jwtFromRequest":{"funcName": "fromAuthHeaderAsBearerToken"},
"ignoreExpiration":false,
"secretOrKey":"APPX_SECRET_KEY"},
{
"jwtFromRequest": {"funcName": "fromBodyField", args: 'USER_JWT'},
"ignoreExpiration":false,
"secretOrKey":"APPY_SECRET_KEY"}
]
}
```

## Authorisation

No specific authorisation is currently implemented in Whispr - if you are authenticated it is assumed that you should have access to all data in Whispr.
1 change: 1 addition & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ AWS_S3_ENDPOINT = http://localstack:4566/
AWS_BUCKET_NAME = demo-bucket

EXAMPLE_TEST = test
AUTH_CONFIG_SECRET = {"config":[{"jwtFromRequest":{"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKeyProvider": { "cache": true, "rateLimit": true, "jwksRequestsPerMinute": 5, "jwksUri": "https://whispr-dev.authtest.com/.well-known/jwks.json"}, "iss": "https://whispr-dev.authtest.com/"}, {"jwtFromRequest": {"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKey":"another_secret_key"}]}
3 changes: 2 additions & 1 deletion example.rep.env
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ COGNITO_REGION
COGNITO_IDENTITY_POOL_ID
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
AWS_S3_ENDPOINT
AWS_BUCKET_NAME
AWS_BUCKET_NAME
AUTH_CONFIG_SECRET = {"config":[{"jwtFromRequest":{"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKeyProvider": { "cache": true, "rateLimit": true, "jwksRequestsPerMinute": 5, "jwksUri": "https://whispr-dev.authtest.com/.well-known/jwks.json"}, "iss": "https://whispr-dev.authtest.com/"}, {"jwtFromRequest": {"funcName": "fromAuthHeaderAsBearerToken"},"ignoreExpiration":false,"secretOrKey":"another_secret_key"}]}
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ module.exports = {
collectCoverageFrom: ['**/src/**/*.{js,ts}'],
coverageDirectory: 'tests/unit/coverage',
testEnvironment: 'node',
"verbose": true
};
Loading