Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

feat(jwt-verifier): Adds verifications to verifyAccessToken() per spec #481

Merged
merged 5 commits into from
Jun 27, 2019

Conversation

swiftone
Copy link
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Adding Tests
  • Build related changes
  • CI related changes
  • Documentation changes
  • Other... Please describe:

What is the current behavior?

access token validation was only checking signature and expiration

Issue Number: N/A

What is the new behavior?

  • Checks for issuer match for iss claim
  • Optionally checks audience for aud claim if passed to verifyAccessToken()

Does this PR introduce a breaking change?

  • Yes
  • No

Issuer is now a required claim. This should break nothing for existing production code where issuer was always included. Nonetheless, this will be part of the 1.0 release so will be a major version change.

Other information

Reviewers

@vijetmahabaleshwar-okta vijetmahabaleshwar-okta force-pushed the br-hat-jwt-access-toke-to-spec-OKTA-233219 branch from 79a3372 to d518530 Compare June 25, 2019 19:49
@@ -19,7 +19,7 @@
"test": "yarn test:unit && yarn test:e2e && yarn test:integration",
"pretest:e2e": "../../scripts/updateSeDrivers.sh",
"test:e2e": "protractor test/e2e/protractor.conf.js",
"test:integration": "../../scripts/tck.sh",
"test:integration": "../../scripts/tck.sh 0.4.0-SNAPSHOT",
Copy link
Contributor

Choose a reason for hiding this comment

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

Still using older version of TCK in oidc-middleware. Tests failing with latest version.
Created this JIRA to track and fix it - https://oktainc.atlassian.net/browse/OKTA-234895

@vijetmahabaleshwar-okta vijetmahabaleshwar-okta force-pushed the br-hat-jwt-access-toke-to-spec-OKTA-233219 branch from 762bb33 to 4cfa955 Compare June 25, 2019 20:55
TCK_VERSION=0.4.0-SNAPSHOT
if [ -z "$1" ]
then
TCK_VERSION=0.5.4-SNAPSHOT
Copy link
Contributor

Choose a reason for hiding this comment

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

If no version is passed, use the latest.

Copy link
Contributor

@robertjd robertjd left a comment

Choose a reason for hiding this comment

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

A few questions to resolve, looks great otherwise.

resolve(jwt);
});
});
}

async verifyAccessToken(accessTokenString, expectedAudience) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we converting this to async becuase we want to be ready for a situation where we support remote validation via introspect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not really a conversion - it previously returned a promise, so the async is just explicit.

// - issuer claim
// - any custom claims passed in

const jwt = await this.verifyAsPromise(accessTokenString);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it useful to break out this verifyAsPromise logic to its own function? Asking because I only see it being used once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm a proponent of having each function do one job (organizing calls to other functions is "one job").

Originally I had the verifyAsPromise in here, and it made this function a little confusing - it was juggling a callback, a promise from that callback, and calling some other functions, so I refactored it to this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm down

@@ -94,7 +96,7 @@ app.get('/api/messages', authenticationRequired, (req, res) => {
},
{
date: new Date(new Date().getTime() - 1000 * 60 * 60),
text: 'Hello, word!'
text: 'Hello, world!'
Copy link
Contributor

Choose a reason for hiding this comment

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

:D


return oktaJwtVerifier.verifyAccessToken(accessToken)
return oktaJwtVerifier.verifyAccessToken(accessToken, expectedAud)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see a test that asserts a failure if the expected audience doesn't match the provided audience?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops nevermind this one, I found it later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't line 373 in verifier.spec.js cover this?
it('fails with a invalid audience when given a valid expectation', () => {

mockKidAsKeyFetch(verifier);

return verifier.verifyAccessToken(token, 'http://myapp.com/')
.then( () => { throw new Error('Invalid Signature was accepted'); } )
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

mockKidAsKeyFetch(verifier);

return verifier.verifyAccessToken(token)
.catch( err => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add add a then() handler here, and do a "this shouldn't have passed" exception like you did above for signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can do - I left it out since every other test is implicitly doing this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

});
});

it('passes when no audience expectation is passed (Legacy)', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we are doing a major rev, can we remove the legacy behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Is that a request to do so?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, let's remove

});
mockKidAsKeyFetch(verifier);

return verifier.verifyAccessToken(token, ['one', 'http://myapp.com/', 'three'])
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if i do verifyAccessToken(token, []) ? Seems like test we should have

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What happens is a guaranteed failure, since the actual audience won't (can't) match any entry in the empty array, but it's a reasonable enough test to add.

Copy link
Contributor

@robertjd robertjd Jun 26, 2019

Choose a reason for hiding this comment

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

I'd advocate for adding a test, so that in the future we can refactor internals and know we're catching this case. I've been bitten by internal logic that allows an empty value to bypass validations 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

@aarongranick-okta aarongranick-okta left a comment

Choose a reason for hiding this comment

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

LGTM

@swiftone swiftone merged commit 9d76c9f into master Jun 27, 2019
@swiftone swiftone deleted the br-hat-jwt-access-toke-to-spec-OKTA-233219 branch July 26, 2019 17:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants