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

ClientID is not allowed in scopes. #4652

Closed
yuezk opened this issue Mar 1, 2024 · 10 comments · Fixed by #4661
Closed

ClientID is not allowed in scopes. #4652

yuezk opened this issue Mar 1, 2024 · 10 comments · Fixed by #4661

Comments

@yuezk
Copy link

yuezk commented Mar 1, 2024

Hi,

We are using the B2C OIDC. The B2C login needs to pass the Client ID to return the access token, which is not allowed explicitly in this library.

image

From the picture, I understand that we can pass the Client ID as the scope when using B2C OIDC.

private void ValidateScopeInput(ISet<string> scopesToValidate)
{
if (scopesToValidate.Contains(AuthenticationRequestParameters.AppConfig.ClientId))
{
throw new ArgumentException("API does not accept client id as a user-provided scope");
}
}

The same library for Python has been fixed in AzureAD/microsoft-authentication-library-for-python#530

Related issues: #2372

@bgavrilMS
Copy link
Member

Do I understand correctly that you have a single app registration and you'd like to use it as both a client and a web api? Did you expose a web api in the app registration?

Also, do you know from the Python app how the access token looks like? What is the "scp" (scopes) claim?

@yuezk
Copy link
Author

yuezk commented Mar 1, 2024

Do I understand correctly that you have a single app registration and you'd like to use it as both a client and a web api? Did you expose a web api in the app registration?

Our case is hard to explain. We expect the OIDC token response to also include the access token, and the only way to achieve this in B2C is to include the client ID in the authorization request scope.

When using openid <client_id> as the the scopes, the token response from B2C is something like this:

{
  "access_token": "eyJhbGciOiJSUz...",
  "id_token": "eyJhbGciOiJSUzI1N...",
  "token_type": "Bearer",
  "not_before": 1709296392,
  "expires_in": 3600,
  "expires_on": 1709299992,
  "resource": "73c4acd4-xxxx-xxxx-xxxx-fd0657497f57",
  "id_token_expires_in": 3600,
  "profile_info": "eyJ2ZXIiOi",
  "scope": "73c4acd4-xxxx-xxxx-xxxx-fd0657497f57 openid"
}

The decoded access token has no "scp" (scopes) claim.

@bgavrilMS
Copy link
Member

What is this access token used for? What protected downstream api does it access?

Normally if you create your own web api, you define the scope like: api://{client_id}/Scope2

image

@yuezk
Copy link
Author

yuezk commented Mar 1, 2024

Normally if you create your own web api, you define the scope like: api://{client_id}/Scope2

We tried this. We created a separate application registration named webapi, and added the custom scope to it, say api://{webapi_client_id}/read.

Then, in the API Permissions of the OIDC application registration, we added the webapi, selected the read scope we added above, and granted the admin consent.

In the OIDC authorization request, we set the scopes to openid api://{webapi_client_id}/read, and perform authentication, but the OIDC token response still doesn't include the access token. And token response only has the openid scope, "scope": openid. Looks like B2C doesn't return scopes other than openid if openid is included in the authorization request.

If we remove openid from the authorization request scope, we can get the access token. But the key point is that we want the access token to be returned along with the OIDC token response. We cannot issue a separate token exchange request to get the access token due to the current architecture limits.

What is this access token used for? What protected downstream api does it access?

This doesn't matter, we just want to get both the ID token and the access token in the OIDC token exchange request and we can't send a separate token exchange request to get the access token due to the current architecture limits.

@bgavrilMS
Copy link
Member

Please help me understand what you mean by "OIDC" request, because there are 2 requests involved: one for the authorization endpoint and one for the token endpoint.

  • browsers perform the authorization request, you just tell the browser where to go (in public client scenarios MSAL will).
  • MSAL performs the token request

In both requests the scopes should be openid profile offline_access api://{webapi_client_id}/read. Note that MSAL always adds openid profile offline_access

Are you saying that requesting openid profile offline_access api://{webapi_client_id}/read from the token endpoint using the auth code from the authorization endpoint does not return a token?

@yuezk
Copy link
Author

yuezk commented Mar 1, 2024

Please help me understand what you mean by "OIDC" request.
It is the authorization request.

Are you saying that requesting openid profile offline_access api://{webapi_client_id}/read from the token endpoint using the auth code from the authorization endpoint does not return a token?

Yes, that's it. It won't return the access token, you could try it with the B2C login. Whereas using the client ID as the scope, will return the access token, so we added the client ID, but this library doesn't allow this.

@bgavrilMS
Copy link
Member

We really want to understand the scenario a bit better. There's a long discussion on #562

Also CC-ing @rayluo for an opinion.

@rayluo
Copy link
Contributor

rayluo commented Mar 1, 2024

  1. The MSAL Python PR mentioned by @yuezk was solely based on the B2C documentation which declares that a ClientID-only scope shall be accepted. The test case also only covered that scenario, without even the api:// prefix. And the outcome was probably the same as what @yuezk mentioned earlier.

  2. But we did not test the api://{webapi_client_id}/read situation. Now that reading from @yuezk 's report:

    In the OIDC authorization request, we set the scopes to openid api://{webapi_client_id}/read, and perform authentication, but the OIDC token response still doesn't include the access token. And token response only has the openid scope, "scope": openid. Looks like B2C doesn't return scopes other than openid if openid is included in the authorization request.

    If we remove openid from the authorization request scope, we can get the access token.

    The first paragraph could theoretically happen if the B2C somehow chose to ignore the scope api://{webapi_client_id}/read. (AAD also has similar behavior but that happens only when requesting token for multiple resources.)

    But then the second paragraph seems to suggest that the api://{webapi_client_id}/read alone works fine. So, I don't know why B2C would ignore it when accompanied by openid scope.

Overall, I would suggest that,

  • MSAL .Net to also support #1 ClientId-only as scope, if it has not already. That would unblock @yuezk .
  • I do not have much first hand experience on #2. My understanding is that @yuezk is not blocked by #2. But if it is, then we might have to get B2C service team involved in troubleshooting.

@yuezk
Copy link
Author

yuezk commented Mar 2, 2024

@rayluo thanks for the summary, you got my point.

bgavrilMS added a commit that referenced this issue Mar 12, 2024
bgavrilMS added a commit that referenced this issue Mar 12, 2024
bgavrilMS added a commit that referenced this issue Mar 20, 2024
bgavrilMS added a commit that referenced this issue Mar 20, 2024
@pmaytak pmaytak added this to the 4.60.0 milestone Mar 20, 2024
@neha-bhargava
Copy link
Contributor

Fixed in MSAL.Net 4.60.0 release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

5 participants