feat: Adds MRRT support#1056
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1056 +/- ##
=======================================
Coverage 25.49% 25.49%
=======================================
Files 3154 3154
Lines 148230 148230
Branches 10015 10015
=======================================
Hits 37785 37785
Misses 108132 108132
Partials 2313 2313
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| Console.WriteLine($"Granted Scope: {tokenResponse.Scope}"); | ||
|
|
||
| // The server may grant fewer scopes than requested — check before relying on them. | ||
| if (tokenResponse.Scope != requestedScope) |
There was a problem hiding this comment.
This comparison is not reliable. The scope value is a space separated list and the server does not guarantee any ordering. If the server grants everything but returns the scopes in a different order (like write:data read:data), this check will still report a warning even though nothing is actually missing.
Can we compare them as sets instead? Something like splitting both strings on spaces and checking that every requested scope is present in the granted ones. Since this example gets copied into real apps, I would rather we show the correct pattern here.
There was a problem hiding this comment.
Updated the example
| /// requested scope to detect an insufficient-scope grant. | ||
| /// </remarks> | ||
| [JsonPropertyName("scope")] | ||
| public string Scope { get; set; } |
There was a problem hiding this comment.
Moving Scope up to the base class is fine for source code, but it is a binary breaking change. Any app that was compiled against the older version and reads ClientInitiatedBackchannelAuthorizationTokenResponse.Scope will throw a MissingMethodException at runtime until it is recompiled.
In practice most people recompile with the new package, so the impact is small, but the PR description says this is non breaking. Can we update the description or the release notes to call this out?
There was a problem hiding this comment.
Added a callout in the PR description. Will ensure to add a callout in the CHANGELOG on Release
| ).ConfigureAwait(false); | ||
|
|
||
| await AssertIdTokenValid(response.IdToken, request.ClientId, request.SigningAlgorithm, request.ClientSecret, request.Organization, request.Nonce).ConfigureAwait(false); | ||
| await AssertIdTokenValidIfExisting(response.IdToken, request.ClientId, request.SigningAlgorithm, request.ClientSecret, request.Organization, request.Nonce).ConfigureAwait(false); |
There was a problem hiding this comment.
This change looks correct for MRRT. A refresh that targets a different audience and drops openid will not return an id_token, so skipping validation when the token is absent is the right call and it matches the other flows.
One small thing worth noting: when a caller does request openid and expects an id_token back, an absent token now passes silently and IdToken will be null instead of throwing. Not a blocker, but it might be worth a line in the docs telling openid callers to null check response.IdToken.
Changes
Adds Multi-Resource Refresh Token (MRRT) support to the
Auth0.AuthenticationApipackage.MRRT lets a single refresh token be exchanged for access tokens targeting different APIs (
audience) and/or broader scopes, without a full re-authentication. At the protocol level this is already a standardPOST /oauth/tokenrefresh-token grant —RefreshTokenRequestalready carriesAudienceandScope, andGetTokenAsync(RefreshTokenRequest)already sends them. The gap this PR closes is on the response side: surfacing the actually-granted scopes so callers can detect under-granting.Classes/methods changed:
AccessTokenResponse— addedScopeproperty ([JsonPropertyName("scope")]).The token endpoint returns the granted scope, which may be narrower than requested. Promoting it to the base response makes it available to every flow that returns
AccessTokenResponse.name, wire name (scope), type, and serialization are unchanged, so any code recompiled against this version behaves identically. However, an assembly compiled against a previous version that reads
ClientInitiatedBackchannelAuthorizationTokenResponse.Scopeholds a member reference to the derived type; because the property now lives on the base, that reference will throwMissingMethodExceptionat runtimeuntil the consuming assembly is recompiled.
AuthenticationApiClient.GetTokenAsync(RefreshTokenRequest)— changedAssertIdTokenValid→AssertIdTokenValidIfExisting. A refresh-token grant only returns anid_tokenwhenopenidscope is requested, which MRRT audience-targeted refreshes frequently omit; the previous call threw"ID token is required but missing."on an absent token. This aligns the refresh flow with the client-credentials, passwordless, and device-code flows, which already validate the ID token only when present. Backward-compatible: refresh responses that do carry anid_tokenare still validated.Usage (also added to
Examples.md, §5):References
Testing
This change adds unit test coverage
This change adds integration test coverage
This change has been tested on the latest version of the platform/language or why not
Checklist
I have read the Auth0 general contribution guidelines
I have read the Auth0 Code of Conduct
All existing and new tests complete without errors