-
Notifications
You must be signed in to change notification settings - Fork 0
Audience value changes when adding offline_access #415
Comments
Again, thanks for a bug report. But again, I'm unfortunately unable to reproduce this. |
G'day @josephdecock, long time no speak 👋. I was able to reproduce the behavior using demo.duendesoftware.com. Observe that the access token produced has an audience claim of I have included har files that capture each flow in this zip file I hope that helps Gav |
Hi Joseph Any update on this issue, are you able to reproduce with Gavin's reproduce steps? All the best Andy |
Hi, good to hear from you all! Thanks for the really detailed repro steps. Using the HAR file I was able to see that your request to the authorize endpoint is including the resource indicator, but the subsequent request to the token endpoint is not. If you add the resource parameter to the token request as well, you should get the expected audience. |
Hi Joseph Thanks didn't spot that, so I guess you are hinting that this is a bug in the OIDC client library? or you are expected to add code like options.Events.OnAuthorizationCodeReceived = context => { context.TokenEndpointRequest.Resource = context.Protocol; }; When exchanging an auth code for a token, I would have expected that the token you get back is the one described in the authorization request. That certainly is the case if you don't mention offline_access. Why is IDS behavior different when offline_access scope is supplied? All the best Andy |
Typically if you're using resources it make sense to include the resource in both the authorization and token request. That's the way RFC 8707 does it in their examples.
Which library?
Yeah, that's what I would expect generally
The auth request is more "general" then that. It doesn't (at its most general) describe a single access token. Rather, it grants authorization for all the possible parameters that you might ask for when you actually ask for a token. It's actually possible to get multiple different tokens with different audiences, all based on this one initial grant.
There's a special workflow for obtaining multiple tokens with different audiences where you use the refresh token to call the token endpoint and get those different tokens. I suspect that workflow is involved here, and I'm going to do a bit more investigation to make sure that this is working as designed. |
So, I tested this behavior and can confirm that this does successfully correct the Before: However, I think this behavior makes sense. I asked for a token for the purpose of interacting with With all of that said though I still believe the originally reported behavior is very problematic. I think the RFC that you mentioned even says as much.
Acknowledging that multiple resources can be requested yet |
Hi Joe Any news on your investigation? All the best Andy |
@GavinOsborn - we're thinking along the same lines, but also trying to make sure that any changes we consider aren't too disruptive |
Hi Joe |
Resource indicators are layered on top of scopes, which remain the mechanism for specifying the scope of access. While Instead, the request to the authorize endpoint is setting up the possible requests at the token endpoint. The token endpoint will accept one of the originally requested resources or no resource indicator at all. If you request a resource, then IdentityServer will create a token with scopes filtered to what is supported by that resource, and with that resource as the audience. An implication of this filtering is that, when you request a resource at the token endpoint, you get a token that can't be used at the userinfo endpoint. The userinfo endpoint requires the profile scope and/or other identity resource scopes, which aren't supported by an The userinfo endpoint is a resource in the sense of being an OAuth Protected Resource, but there's no appropriate resource parameter to send when making requests to it. Thus, to make requests to the userinfo endpoint, you need to request an access token without passing any resource to the token endpoint. When you do that, you get the standard behavior of IdentityServer, which is to produce a token with all the originally requested scopes. This will include profile, etc, if they were originally requested at the authorize endpoint, thus producing a token that can be used at the userinfo endpoint. An implication of the need to support userinfo calls as well as calls to As we were testing this, we did discover that we seem to have a bug when The other default behavior that happens when you don't specify a resource at the token endpoint is that IdentityServer will determine the audiences of the token based on the requested scopes. Any The cause of this behavior is the The default behavior of IdentityServer is that |
Hey Joe, thank you for the excellent and thoughtful write-up. There are a couple of lenses through which I'd like to reply, selfishly I'm going to focus my own immediate project-centric needs first :) Resource Isolation I'm aware of an existing issue w/regards to ASP.NET OIDC middleware accepting multiple resource parameters. But I note the workaround listed in thread and hope🙏 that promised new middlware lands us in a better place all round. So I believe we may now be able to solve for our use-case. Thank you. |
Priviledge Escalation
Your point here is well made and may or may not stand-up to scrutiny from a purist point of view - I don't mind admitting I'm unsure. But if we allow ourselves to assume (not unreasonably) that resource servers use the Agree to disagree? |
Joe said all of this already, to be fair, so sorry for the redundancy: One problem with resource indicators is that on the authorize request the list of scopes and resources is flat, and there's no way in that request to say that you want scope1 only for resource1, and scope2 for resource2. And in our configuration system, ApiResources don't "own" ApiScopes -- they only indicate the ones they support. This is needed because often you have scopes that needs to be used at different resources, and maybe not all of them require isolation. So the scopes being requested is still the scopes a client is being granted. The resource isolation feature doesn't restrict the access that the client has. Resource isolation just ensures that an access token obtained can be resource-constrained so that the resource server can't abuse or misuse the access token presented to it. Or another use is if there's resource specific processing needed on the access token (e.g. encryption). Were you wanting it to work differently?
Correct, and I don't see that IdentityServer allows for escalation of access when using the refresh token. But if you don't request offline_access, then the token exchange only has one chance to obtain an access token, this it only has one chance to indicate the isolated resource. Thus, it's not practical if you need multiple access tokens for different audience constraints unless you obtain one. Or am I misunderstanding? |
Also, I suppose, the thing to keep in mind is that we're a framework, and our ApiResource concept supports two things: 1) allowing control of the "aud" claim in the access token based on the scopes requested (independent of resource indicators), and 2) allow resource isolation. And since these overlap, and since we also need to weave in the support for ~/userinfo, there might certainly be a tension from trying to use all of these together. In other words, it's hard to indicate with the resource param you do or do not want access to ~/userinfo and/or non-resource isolated resources. I suspect, again what Joe already said, is that in your architecture if you really want resource isolation for everything, then it seems that you'd want to configure every ApiResource with RequireResourceIndicator=true. The resource indicator spec is written from the OAuth-only mindset, and thus is under specified how this should work with OIDC (and ~/userinfo), and how the absence of the resource param should work on the token endpoint. This means that some things are left up to policy (or somesuch) in the token server for how things should work. We'd like to hope that, as a framework, you can configure things to make it work the way you want it to, so see how far setting RequireResourceIndicator gets you, and if there are other roadblocks please let us know. |
We've merged the PR with the fix for this. |
Which version of Duende IdentityServer are you using?
6.5.1
Which version of .NET are you using?
.NET 6
Describe the bug
The Audience in the JWT changes when adding offline_access scope
To Reproduce
scope.325908 is a shared scope
Two resources have the scope
urn:repro/325908 and urn:repro/325908-2
Make a request with a resource indicator.
resource : urn:repro/325908
scopes : openid profile scope.325908
Resource is not configured to Require Resource Indicator
Get back the expected JWT
{
"iss": "https://localhost:63024",
"nbf": 1671711275,
"iat": 1671711275,
"exp": 1671714875,
"aud": "urn:repro/325908",
"scope": "openid profile scope.325908",
"amr": [
"external"
],
"client_id": "demo-client",
"sub": "600808ab-8a13-41ae-b1a2-6ee4cd95aa2d",
"auth_time": 1671711266,
"idp": "okta",
"sid": "C27D158C4F665B6E50DD90F0AA043151",
"jti": "6F36E0729091B03F0EBBFEBC082BBA4E"
}
Repeat the request but adding offline_access scope,
resource : urn:repro/325908
scopes : openid profile scope.325908 offline_access
and now the audience contains both resources associated with the shared scope.
{
"iss": "https://localhost:63024",
"nbf": 1671711016,
"iat": 1671711016,
"exp": 1671714616,
"aud": [
"urn:repro/325908",
"urn:repro/325908-2"
],
"scope": "openid profile scope.325908 offline_access",
"amr": [
"external"
],
"client_id": "demo-client",
"sub": "600808ab-8a13-41ae-b1a2-6ee4cd95aa2d",
"auth_time": 1671710838,
"idp": "okta",
"sid": "8252ABEA0C36CF27541D873F8CA4CA63",
"jti": "75C60658C99E4D45B5AB622EE5DBF66F"
}
Expected behavior
Expected the audience to not change by adding offline_access
The text was updated successfully, but these errors were encountered: