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

JwtBearer doesn't populate the ValidIssuer and the ValidAudience properties of the JwtBearerOptions.TokenValidationParameters. #52820

Closed
1 task done
satma0745 opened this issue Dec 14, 2023 · 5 comments
Labels
area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer area-identity Includes: Identity and providers bug This issue describes a behavior which is not expected - a bug.
Milestone

Comments

@satma0745
Copy link
Contributor

satma0745 commented Dec 14, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

The JwtBearerConfigureOptions class reads values for both the ValidIssuer and the ValidAudience from the configuration and even saves them to the ValidIssuers and ValidAudiences collections in the TokenValidationParameters, but completely ignores the corresponding ValidIssuer and ValidAudience properties of the same TokenValidationParameters.

The simplified representation of what is happening right now:

// Load ValidIssuers from authentication configuration.
var issuers = configSection
    .GetSection(nameof(TokenValidationParameters.ValidIssuers))
    .GetChildren()
    .Select(iss => iss.Value)
    .ToList();

// Also load a ValidIssuer from the authentication configuration, then add it
// to the ValidIssuers collection.
var issuer = configSection[nameof(TokenValidationParameters.ValidIssuer)];
if (issuer is not null)
{
    issuers.Add(issuer);
}

// Load ValidAudiences from authentication configuration.
var audiences = configSection
    .GetSection(nameof(TokenValidationParameters.ValidAudiences))
    .GetChildren()
    .Select(aud => aud.Value)
    .ToList();

// Also load a ValidAudience from the authentication configuration, then add it
// to the ValidAudiences collection.
var audience = configSection[nameof(TokenValidationParameters.ValidAudience)];.
if (audience is not null)
{
    audiences.Add(audience);
}

// Only populate the ValidIssuers and the ValidAudiences properties, completely
// ignoring the ValidIssuer and the ValidAudience properties.
options.TokenValidationParameters = new()
{
    ValidIssuers = issuers,
    ValidAudiences = audiences
};

Note

Please note that this is a very abbreviated code example and that it differs significantly from the actual JwtBearerConfigureOptions.Configure method implementation.

Describe the solution you'd like

I would like that if configuration explicitly specifies a value for the ValidIssuer and ValidAudience, that value will end up in the TokenValidationParameters.

Basically, I would like the JwtBearerConfigureOptions.Configure method to also populate the ValidIssuer and the ValidAudience properties too:

// Load ValidIssuers from authentication configuration.
var issuers = configSection
    .GetSection(nameof(TokenValidationParameters.ValidIssuers))
    .GetChildren()
    .Select(iss => iss.Value)
    .ToList();

// Also load a ValidIssuer from the authentication configuration, then add it
// to the ValidIssuers collection.
var issuer = configSection[nameof(TokenValidationParameters.ValidIssuer)];
if (issuer is not null)
{
    issuers.Add(issuer);
}

// Load ValidAudiences from authentication configuration.
var audiences = configSection
    .GetSection(nameof(TokenValidationParameters.ValidAudiences))
    .GetChildren()
    .Select(aud => aud.Value)
    .ToList();

// Also load a ValidAudience from the authentication configuration, then add it
// to the ValidAudiences collection.
var audience = configSection[nameof(TokenValidationParameters.ValidAudience)];.
if (audience is not null)
{
    audiences.Add(audience);
}

// Only populate the ValidIssuers and the ValidAudiences properties, completely
// ignoring the ValidIssuer and the ValidAudience properties.
options.TokenValidationParameters = new()
{
+ ValidIssuer = issuer,
    ValidIssuers = issuers,
+ ValidAudience = audience,
    ValidAudiences = audiences
};
@satma0745
Copy link
Contributor Author

I'll also provide a PR (I just need a little bit of time).

@Kahbazi
Copy link
Member

Kahbazi commented Dec 14, 2023

I believe this was intentional. #42679 (comment)

Ah! I should've recognized this. I noticed while exploring the identity model code that even though they expose single-value properties for config (e.g. ValidIssuer and ValidAudience) it looks like the multi-value properties are ultimately used as the source of truth. Updated this to just set the multi-value property.

@satma0745 Is there a scenario that blocks you by not having these properties?

@satma0745
Copy link
Contributor Author

satma0745 commented Dec 15, 2023

@Kahbazi, there are no scenario that blocks me, but it just felt counter-intuitive to have such behavior.
Having these configuration fields recognized and loaded from the configuration, but not properly populated looks kind of odd, at least just for me.

And about that ValidIssuers and ValidAudiences are considered a single source of truth by design:

  1. That fact wasn't covered in tests, so I'm not exactly sure if the current behavior was intentional.
  2. I was planning to raise another issue as soon as I'm done with this one, in which I would suggest validating if ValidAudience and ValidAudiences (or ValidIssuer and ValidIssuers) are both specified, but contradict each other (I think we could throw an exception in this case, or at least log a warning, so the Developer will be notified).

@satma0745
Copy link
Contributor Author

satma0745 commented Dec 15, 2023

Guess, there's no point in discussing that topic in the separate Issue any more, so I'll do it here.

Suppose, we have the following configuration:

{
    "ValidAudience": "A",
    "ValidAudiences": [ "B", "C" ]
}

With the current behavior we'll end up with the following TokenValidationParameters:

{
    ValidAudience = null,
    ValidAudences = new string[] { "A", "B", "C" }
}

And it doesn't look right to me: it basically looks like we just copied over value and that there's just 2 sources for the ValidAudiences value.

Basically, I'd be satisfied with one of the following decisions:

  1. If we all agree that the current behavior is intentional I will at least provide tests for it (enforcing consistency for the future) and maybe doucment it somewhere (I'm currently not sure where this kind of documentation should go, and the fact that there's no article on Microsoft Docs [about JwtBearerOptions loading from configuration] doesn't help - maybe I'll end up creating it myself). Either way, I think that this behavior should be described and enforced explicitly.
  2. If we agree that ValidAudiences property is the single source of truth, I would like to get rid of the ValidAudience property alltogether (both configuration and JwtBearerOption's one), since it only brings more uncertainty and suggests that the two existing properties are used on equal terms both being the current source of truth.
  3. If we agree, that the ValidAudience configuration property should not be ignored and it also should be populated into the TokenValidationParameters, I would suggest not just blindly copying it over to the ValidAudiences collection, but also validate ValidAudiences collection if it already contains ValidAudience and:
    a. If it already does, then there's no conflict in two values and ValidAudience is a default (currently selected) option from the ValidAudiences collection (which contains all available options for the ValidAudience)
    b. If the ValidAudences desn't contain a value of the ValidAudience, then we should notify User about this inconsistency (for example, throw an exception or at least log a warning).

I'm quite happy with any of these options as long as we can all agree on it, although my favorite is the 3rd one.

@mkArtakMSFT mkArtakMSFT added area-identity Includes: Identity and providers and removed area-security labels Dec 19, 2023
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@mkArtakMSFT mkArtakMSFT added the bug This issue describes a behavior which is not expected - a bug. label Feb 14, 2024
@mkArtakMSFT mkArtakMSFT added this to the Backlog milestone Feb 14, 2024
@halter73 halter73 modified the milestones: Backlog, 9.0-preview2 Feb 23, 2024
@halter73
Copy link
Member

Fixed by #52821.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer area-identity Includes: Identity and providers bug This issue describes a behavior which is not expected - a bug.
Projects
None yet
Development

No branches or pull requests

5 participants