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 optional JWT authorizer for HttpApi events #1022

Merged
merged 7 commits into from Jun 23, 2020

Conversation

qswinson
Copy link
Contributor

This adds basic checking of JWTs based on authorizers defined in the serverless file for HttpApi events. It does not validate the signature of the JWT, therefore an opt-in flag is required to enable this feature. This is only meant for local testing of locally issued JWTs. It is not meant for consuming and granting access based on externally generate JWTs due to the security concerns.

export default function createAuthScheme(jwtOptions) {
const authorizerName = jwtOptions.name

const identitySourceMatch = /^\$request.header.((?:\w+-?)+\w+)$/.exec(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get a comment roughly describing what this does for the people who are terrible with regex (like myself)?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggle with regex too - here's the explanation from regex 101
/
^$request.header.((?:\w+-?)+\w+)$
/
^ asserts position at start of the string
$ matches the character $ literally (case sensitive)
request matches the characters request literally (case sensitive)
. matches any character (except for line terminators)
header matches the characters header literally (case sensitive)
. matches any character (except for line terminators)
1st Capturing Group ((?:\w+-?)+\w+)
Non-capturing group (?:\w+-?)+

  • Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
    \w+
    matches any word character (equal to [a-zA-Z0-9_])
    -?
    matches the character - literally (case sensitive)
    \w+
    matches any word character (equal to [a-zA-Z0-9_])
    $ asserts position at the end of the string, or before the line terminator right at the end of the string (if any)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it's parsing the authorization header btw @chardos

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex is just copied from src/events/http/createAuthScheme.js

I did a little digging in the git history. Based on this commit:

e52cc2e

the regex was modified from just grabbing all characters (the w+ part) to also grabbing hyphens since those are valid characters in a request header.

This doesn't actually parse the auth header. It figures out what the name of the header key is if you have customized the header value that will actually contain the token. That header key is then used when the requests are coming in to grab the auth token from the request.

@chardos
Copy link
Collaborator

chardos commented Jun 18, 2020

Hey @qswinson,

Thanks for this awesome functionality! I've had a look at the PR, and am ready to approve, as soon as the tests are passing.

Copy link

@trevorgk trevorgk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it - when are you adding the signature validation? ;)


// TODO: add code that will actually validate a JWT.
if (!ignoreJWTSignature) {
return buildSuccessResult(null)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be better to throw an error or return failure result if the user specifies that the signature should not be ignored?
They may be unaware that the signature isn't being validated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't set ignoreJWTSignature you will have to set noAuth for serverless-offline to run at all for HttpApi events with authorization. The current behavior of serverless-offline is to treat authorization as another lambda. Since there isn't a function mapped to the authorizer, serverless-offline throws an error on startup. My intention with this flag is to make it explicit that while the authorization rules defined in the serverless.yml file are being tested, this is should not be considered valid security.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me know if you'd like to see the change you suggested. my intention was not to modify the current behavior of the library.

export default function createAuthScheme(jwtOptions) {
const authorizerName = jwtOptions.name

const identitySourceMatch = /^\$request.header.((?:\w+-?)+\w+)$/.exec(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggle with regex too - here's the explanation from regex 101
/
^$request.header.((?:\w+-?)+\w+)$
/
^ asserts position at start of the string
$ matches the character $ literally (case sensitive)
request matches the characters request literally (case sensitive)
. matches any character (except for line terminators)
header matches the characters header literally (case sensitive)
. matches any character (except for line terminators)
1st Capturing Group ((?:\w+-?)+\w+)
Non-capturing group (?:\w+-?)+

  • Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
    \w+
    matches any word character (equal to [a-zA-Z0-9_])
    -?
    matches the character - literally (case sensitive)
    \w+
    matches any word character (equal to [a-zA-Z0-9_])
    $ asserts position at the end of the string, or before the line terminator right at the end of the string (if any)

export default function createAuthScheme(jwtOptions) {
const authorizerName = jwtOptions.name

const identitySourceMatch = /^\$request.header.((?:\w+-?)+\w+)$/.exec(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it's parsing the authorization header btw @chardos

const { req } = request.raw
let jwtToken = req.headers[identityHeader]
if (jwtToken && jwtToken.split(' ')[0] === 'Bearer') {
;[, jwtToken] = jwtToken.split(' ')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using array destructuring is a clever way of leveraging the output of split

tests/integration/jwt-authorizer/handler.js Outdated Show resolved Hide resolved
path: '/dev/user2',
status: 403,
},
].forEach(({ description, expected, jwt, path, status }) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat trick

@qswinson
Copy link
Contributor Author

I realize I still have a failing test. I've spent a little time and am not sure what is going on. This test is also failing when I run the test suite on the master branch on my local machine. If someone could point me in the right direction, I should have time this weekend to look at it.

@qswinson qswinson requested a review from chardos June 20, 2020 14:10
Copy link
Owner

@dherault dherault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chardos what do you think?
If the documentation is updated I think we are good to go.

@chardos
Copy link
Collaborator

chardos commented Jun 23, 2020

Got my friend @trevorgk who specialises in Auth to take a look (since I've got no idea), so I'm confident this PR is as awesome as it looks 👍

@dherault dherault merged commit f893da3 into dherault:master Jun 23, 2020
@dherault
Copy link
Owner

Thanks @qswinson and @chardos !
It will be included in the next version in a matter of days.

@dherault
Copy link
Owner

v6.5.0
Sorry for the delay!

@qswinson qswinson deleted the add-jwt-authorizer branch February 1, 2022 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants