-
Notifications
You must be signed in to change notification settings - Fork 232
feat(jwt-verifier): Adds verifications to verifyAccessToken() per spec #481
Conversation
79a3372
to
d518530
Compare
@@ -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", |
There was a problem hiding this comment.
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
762bb33
to
4cfa955
Compare
TCK_VERSION=0.4.0-SNAPSHOT | ||
if [ -z "$1" ] | ||
then | ||
TCK_VERSION=0.5.4-SNAPSHOT |
There was a problem hiding this comment.
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.
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!' |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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'); } ) |
There was a problem hiding this comment.
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 => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)', () => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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']) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
access token validation was only checking signature and expiration
Issue Number: N/A
What is the new behavior?
iss
claimaud
claim if passed toverifyAccessToken()
Does this PR introduce a breaking change?
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