Closed DamianEdwards closed 2 years ago
Suggested by @pranavkm, this might be better incorporated into the the existing dotnet user-secrets
tools, which we should alias to dotnet dev-secrets
in .NET 7, giving us a nice symmetry of dotnet dev-certs jwt
and dotnet dev-secrets jwt
, e.g.:
> dotnet new webapi -minimal -o MyApi
> cd MyApi
MyApi> dotnet dev-secrets jwt list
Could not find the global property 'UserSecretsId' in MSBuild project 'MyApi/MyApi.csproj'. Ensure this property
is set in the project or use the 'dotnet dev-secrets init' command to initialize this project.
MyApi> dotnet dev-secrets init
Set UserSecretsId to '4105052b-5b99-4fff-8fc1-9d6c59887d0a' for MSBuild project 'MyApi/MyApi.csproj'.
MyApi> dotnet dev-secrets jwt list
No tokens configured for this application.
MyApi> dotnet dev-secrets jwt create
Token created for user "damian":
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.
MyApi> dotnet dev-secrets jwt create --name privileged --claim scope="myapi:protected-access"
Token created for user "privileged":
jHy8bGciOiJIUzIR5cCI61NiIsInIkpXVCIxMjM0NTweiuI6IkpvakwIiwiJ9.eyJzdWIiOibmFtZSG4iLCJpYMTYyMzkwMjJ9XQiOjE1.
MyApi> dotnet dev-secrets jwt list
User Issued Expires
------ ------------------- -------------------
damian 2022-01-28 17:37:34 2022-07-28 17:37:34
privileged 2022-01-28 17:37:48 2022-07-28 17:37:48
Thanks for contacting us.
We're moving this issue to the .NET 7 Planning
milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
@davidfowl convinced me that it's likely not worth using a cert for the dev JWT tokens, rather we can simply generate a symmetric key and store it in user/dev secrets. Certificates have a habit of causing issues, especially on Linux, and don't provide any additional benefit in this scenario.
Making a variant of this work for automated integration test scenarios too, like with Mvc.Testing, would be most welcome.
A very very very off-the-top-of-my-head idea of what I'm getting at is something like this:
WebApplicationFactory<Program> webApplicationFactory = ...;
HttpClient httpClient = webApplicationFactory
.CreateDefaultClient()
.WithBearerJwtAuthorization(x => x.WithClaim(ClaimTypes.NameIdentifier, "john-smith"));
// The below call to the protected endpoint succeeds because there's a valid JWT
// for the john-smith user in the Authorization header on the HttpClient
string html = await httpClient.GetStringAsync("/admin-secrets");
Starting to explore this over at https://github.com/DamianEdwards/AspNetCoreDevJwts
Some thoughts which might not be related to this work but rather more general related to auth in ASP.NET Core:
builder.Authentication.RequireAuthentication()
or similar, which could just hide the call to add the AuthorizeAttribute
filter.AddJwtBearer
call because it doesn't use the sub
claim for the User.Identity.Name
by default. This should be the default, with maybe a fallback/flag for the old behaviour.More related to this effort:
Thanks for the feedback @CamiloTerevinto. Some thoughts:
Make it easier to discover how to enforce auth: builder.Authentication.RequireAuthentication() or similar, which could just hide the call to add the AuthorizeAttribute filter.
I like this idea. I've personally struggled with finding the right settings to "just require auth for the whole app" (FallbackPolicy
et al). We'll consider this scenario.
More for Swashbuckle than here, but it needs to be made much simpler to add auth to SwaggerUI. I've seen a lot of times people turn off auth for debugging was because they didn't know how to add the security requirements using Swashbuckle's library (and magically that ends up going to production...).
Absolutely. We have another issue where we're tracking that scenario and @captainsafia is especially keen to get this scenario working by default.
It's almost embarrassing to have to look up how to configure the AddJwtBearer call because it doesn't use the sub claim for the User.Identity.Name by default. This should be the default, with maybe a fallback/flag for the old behaviour.
I found that odd too (admittedly I'm not hugely familiar with JWT "in the real world") and in my experiment I made it so the dev JWTs do indeed set a sub
claim with the username. We can follow up with the identity folks as to why this isn't a default.
A sample application should make it very clear that this is intended for development only and should make it effortless to have the production configuration applied instead.
Agreed. The code in my experiment is written to work this way, I just haven't added the "not development" configuration in the app yet. The intention is it will work that way though. Of course it all comes down to how the configuration code is written in the app, i.e. whether it overwrites the JWT options or adds to them, e.g.:
builder.Authentication.AddJwtBearer(c =>
{
if (!builder.Environment.IsDevelopment())
{
// Code here to add the issuers, audiences, signing credentials, etc. for non-dev environments
}
});
It should be possible to accept these JWTs as well as from a STS - especially useful to validate integrations and when the "development" environment is not localhost.
Do you mean you'd like an easy way to standup endpoints that act as an STS that serves JWTs in the same fashion, either in the same application or as a separate application? Or just that I should be able to configure the dev JWT options manually when not in development too, e.g. builder.Authentication.AddJwtBearer(useDevDefaults: true)
?
As @martincostello said, it should be straightforward to use these JWTs under integration testing. This would simplify tests when running on a CI system.
Yep that's a great scenario we'll consider too.
Thanks for the response @DamianEdwards.
Agreed. The code in my experiment is written to work this way, I just haven't added the "not development" configuration in the app yet. The intention is it will work that way though. Of course it all comes down to how the configuration code is written in the app, i.e. whether it overwrites the JWT options or adds to them, e.g.:
You see all this stuff here in your example lib? Ideally, when "not in development" (akin to your example), the production settings would be used. Do notice though that you would have to discard the default dev-jwt settings in that case since it could cause confusion or incorrect settings. I would say that, ideally, we should be able to handle all that through IConfiguration (appsettings and/or environment variables).
Do you mean you'd like an easy way to standup endpoints that act as an STS that serves JWTs in the same fashion, either in the same application or as a separate application?
Sorry, let me rephrase that. What I meant is that it may be desired to be able to have the dev-jwt tokens accepted as well as the tokens by some other service. Think of this case:
However, if we could have the library have endpoints to generate JWTs, it would be fantastic! As long as the default options are secure enough (short lived, strong keys, etc.), it would save a lot of problems of people re-implementing these for small apps that cannot afford a separate STS. But that's... not too far from what Identity does. Side note: it would be awesome to have Identity generate JWTs and non-UI-based endpoints.
@CamiloTerevinto the intent is exactly what you state: that the dev configuration can be used in conjunction with any custom configuration so that both kinds of JWTs are accepted. That's why the code in my example is written to add to the configured signers, etc. rather than just set them.
The initial version of user-jwts
shipped in preview5. We are tracking some follow-ups in #41820 and #41888.
Basic idea is to do for JWT bearer authentication what we did for HTTPS in development, i.e. make it extremely easy to configure apps to use JWT bearer authentication in development, without the need for a discrete token issuing server.
dotnet dev-certs jwt
. Like the HTTPS cert this would be initialized during SDK setup/first-rundotnet dev-jwts
which is similar to the existingdotnet user-secrets
tool but for issuing and managing JWTsAuthenticationBuilder.AddJwtBearer()
overloads configure the application to accept dev JWTs as valid when in the development environmentExample Minimal APIs using dev JWTs