Skip to content

Fix bug in validation of multiple audiences #441

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sfinnman-cotn
Copy link

In a situation where multiple audiences are validated by the validator, the order of evaluation of the for-range loop affects the result.

If we produce matches such as:

{
  "example.org": true,
  "example.com": false,
}

and we configured the validator to expect a single match on audience, the code would either:

  1. produce "token has invalid audience" if "example.org" was evaluated first

  2. produce a passing result if "example.com" was evaluated first

This commit fixes this bug, and adds a suite of tests as well as regression tests to prevent this issue in future.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes a bug in the audience validation logic where the order of evaluation in a for-range loop could yield inconsistent results when validating multiple audiences. Key changes include updates to the audience validation logic in validator.go and the addition of comprehensive tests in validator_test.go to cover various audience validation scenarios.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
validator_test.go Added multiple test cases to verify audience validation behavior for different modes.
validator.go Adjusted the loop logic in verifyAudience() to fix the order-dependent bug.

Copy link
Collaborator

@oxisto oxisto left a comment

Choose a reason for hiding this comment

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

Thanks for spotting this. This indeed seemed to be a problem. It's really a piece of code that is not easily readable. I wonder if this could be approached from a different angle alltogether. I wonder whether there are any function available in the slices package that could help here.

@sfinnman-cotn
Copy link
Author

There might be! I didn't want to change the implementation too much, since i assume there is a good reason for using a map key lookup as a string comparison function. I don't want to accidentally introduce new bugs with this fix. Ill add the comment as asked for!

@oxisto
Copy link
Collaborator

oxisto commented May 28, 2025

There might be! I didn't want to change the implementation too much, since i assume there is a good reason for using a map key lookup as a string comparison function. I don't want to accidentally introduce new bugs with this fix. Ill add the comment as asked for!

I am not really happy with this implementation, so if you have a better way that is easier to maintain, please go ahead!

In a situation where multiple audiences are validated by the
validator, the order of evaluation of the for-range loop
affects the result.

If we produce matches such as:

```
{
  "example.org": true,
  "example.com": false,
}
```

and we configured the validator to expect a single match on
audience, the code would either:

1. produce "token has invalid audience" if "example.org" was
evaluated first

2. produce a passing result if "example.com" was evaluated first

This commit fixes this bug, and adds a suite of tests as well
as regression tests to prevent this issue in future.
@sfinnman-cotn
Copy link
Author

I pushed a rewrite of the method. This implementation is easier to understand, has worse asymptotic complexity, but is probably faster in most real world cases.

@sfinnman-cotn
Copy link
Author

I'm a bit unsure what the expectation should be for the required flag, it could mean that there must exist a non-zero value in the aud claim, or just that the aud claim is present. It feels like a bit of a toss-up in terms of what a consumer of the api would expect in behaviour.

@oxisto oxisto requested a review from Copilot June 4, 2025 09:46
@oxisto
Copy link
Collaborator

oxisto commented Jun 4, 2025

I pushed a rewrite of the method. This implementation is easier to understand, has worse asymptotic complexity, but is probably faster in most real world cases.

I think it's much cleaner, thanks!

@mfridman please have a look as well

@oxisto oxisto requested a review from mfridman June 4, 2025 09:46
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

}

stringClaims = stringClaims + a
return ErrTokenInvalidAudience
Copy link
Member

Choose a reason for hiding this comment

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

Should we be respecting required bool here? I.e., we best-effort (expectAllAud: false) looped through the audience(s) and didn't find a single match, but if required: false should we still be returning an error?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator

@oxisto oxisto Jun 4, 2025

Choose a reason for hiding this comment

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

Hmm yes but we should still respect required within this function (even though it is always set to true from the outside).

It probably depends then whether we had an expected aud or not. If I did not have any expected ones and none match then this should not be an error.

Copy link
Author

Choose a reason for hiding this comment

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

It feels odd to me that required would swallow validation errors. I do check that the aud claim is present if required is set to true. If required is false and the aud claim is either empty or missing, this validation short-circuits to a nil error.

What is the purpose, or intended behaviour of the required parameter?

@sfinnman-cotn
Copy link
Author

What more needs to be done for this to get merged @mfridman @oxisto?

@mfridman
Copy link
Member

@oxisto I'm okay we merge this, and we follow up with any improvements, if any. The only thing I can see is if !required and returning no error on line 252.

@oxisto
Copy link
Collaborator

oxisto commented Jun 12, 2025

@oxisto I'm okay we merge this, and we follow up with any improvements, if any. The only thing I can see is if !required and returning no error on line 252.

I am currently swamped at work and I need some time to think about this unhindered, I will have a look at this at the weekend, the rest looks good to merge.

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.

Verifying multiple audiences produces undeterministic results
3 participants