3

User Story: Given an ADB2C User, with Global Administrator role and an oid of 01234567-901a-bcde-f012-3456789abcde (not a real oid), I want to be able to log in as that user and retrieve the user profile from "https://graph.microsoft.com/beta/me" or "https://graph.microsoft.com/beta/users/01234567-901a-bcde-f012-3456789abcde". Both are listed in the documentation as valid endpoints for B2C.

It's not working: No Access Token!

In an app registration with only Microsoft Graph permission scopes assigned, I used postman to request a bearer token for access to MS Graph. There is one Web redirect URI (https://oauth.pstmn.io/v1/callback), one client secret, and implicit grant is on for both access and id tokens.

The scopes requested are: openid offline_access https://graph.microsoft.com/Directory.AccessAsUser.All

Again, the B2C user account has the Global Administrator role.

  • The Implicit flow returns the error message

AADB2C90205: This application does not have sufficient permissions against this web resource to perform the operation.

  • The Authorization Code flow, when the app secret is included, lacks an access bearer token. There is an ID token and a refresh token, but no access token. That's with and without PKCE, whether or not I send the authorization to an external browser.

The app in my tenant has a user flow, B2C_1_postman, which is basically default. It works just fine with postman, other test apps, and the "Run User Flow" function in the B2C management blade.

The auth endpoint is:

https://{Tenant}.b2clogin.com/{Tenant}.onmicrosoft.com/B2C_1_postman/oauth2/v2.0/authorize

The token endpoint is:

https://{Tenant}.b2clogin.com/{Tenant}onmicrosoft.com/B2C_1_postman/oauth2/v2.0/token

I've duplicated this behavior with a couple of desktop demos from Microsoft's github repository, and now with Postman. The app, called "postman", is in the ADB2C tenant. I granted it the app API scopes of:

Directory.AccessAsUser.All
Directory.Read.All
Directory.ReadWrite.All
Directory.email
Directory.offline_access
Directory.profile

This procedure mirrors what the desktop demo at https://github.com/Azure-Samples/active-directory-b2c-dotnet-desktop sets up, with the single exception being that instead of a NodeJS sample app, I want my desktop app to use MS Graph. (This app registration works just fine if I add the endpoints for the sample app. But specifying the MS Graph scopes always returns an empty access ID.)

How can I get this to work?

Rob Perkins
  • 3,088
  • 1
  • 30
  • 51
  • This is a really good question and the answer is so frustrating and really to be honest seems like a hack of middleware. – Christian Matthew Sep 02 '22 at 19:27
  • @ChristianMatthew I know. The reference to embedded logins on your question literally instructs azure admins to hack the middleware by defining a custom policy, then "Use the – Rob Perkins Sep 15 '22 at 05:21

2 Answers2

4

Managing users through Graph API still seems to require usage of application permissions. So instead of adding delegated permissions to the app in B2C, you need to add application permissions, where you call the Graph API as the app, not on behalf of the user. The instructions in the docs explain the app registration in detail: https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started

You need to give this app application permissions to Graph API, not delegated permissions. Then use those app credentials purely to call Graph API. And you need to use the underlying Azure AD's token endpoint instead of your B2C policy token endpoint.

Since your app is a desktop app (a public client app), you'll need to do the Graph API interactions in a back-end service to which you can authenticate with a B2C token acquired on behalf of the user.

juunas
  • 54,244
  • 13
  • 113
  • 149
  • 1
    I know that, but it's not secure. Admin credentials in a desktop app. I might as well just encrypt a pair of guids and forget B2C altogether. (App level scopes work just fine in a B2C registration, btw) There is text on that page that claims that an interactive session is possible. But no demo. It's also where all the links that used to go to the AD Graph demos now go. It also prompts questions: Why take away instructions and access to *documentation* that worked before? And: Why put a UI in front of ppl implying something impossible is possible? Why aren't they clear that it can't be done? – Rob Perkins Jun 09 '20 at 06:09
  • Ah right, I guess app permissions work in a B2C registration now since they are now unified :) But yeah know you can't do that in a desktop app :\ Definitely add an issue on the documentation page if you find oddities like that by the way. The teams answer those pretty well usually. – juunas Jun 09 '20 at 07:08
  • 2
    Just means I have to build middleware. Since it interfaces with an ASP.NET Core site that participates in the B2C login flow, I have a use case and a place to put the app secret, but you have to know it'd be a lot easier if they wouldn't lead me on like that in documentation or move to hide the previous stuff. Especially with no guidance at all. – Rob Perkins Jun 09 '20 at 21:43
  • Would you mind editing your answer to reflect the app permissions correction? Then I'll mark it. – Rob Perkins Jun 09 '20 at 21:44
  • 1
    Solid. Thank you! I have left a bitter little comment with the docs team there, referencing this question. – Rob Perkins Jun 10 '20 at 17:46
4

Mass confusion here. You can definitely do what you are looking to do, except that this is all Azure AD functionality, not Azure AD B2C. So you are not looking to invoke any B2C user flow etc. B2C auths cannot get access to Microsoft APIs, only your own APIs.

  • AAD tenant - contains only AAD endpoints. It is a single token issuer
  • B2C tenant - contains both AAD and B2C token endpoints. There are two token issuers respectively

A B2C tenant contains:

  • AAD endpoint: login.microsoftonline.com THIS IS NOT BEING DEPRECATED
  • AAD B2C endpoint: tenantName.b2clogin.com+ B2C policyId parameter

Based on the authentication request, the request is routed to the two different token issuers.

The next key point:

  • AAD endpoints allow you to obtain tokens to your applications protected by an AAD Application Registration.
  • AAD endpoints allow you to obtain tokens to Microsoft APIs, since they are also protected by AAD on our side. Such as MS Graph API.
  • AAD endpoints allow client_credentials
  • B2C endpoints allow you to obtain tokens to your applications only protected by an AAD B2C Application Registration.
  • B2C endpoints do not allow client_credentials

You cannot use tenantName.b2clogin.com to obtain a token for MS Graph API, based on the above rule set.
This means a users B2C authentication cannot be used to authorize to AAD protected apps, or Microsoft APIs. (Eventhough the new App Reg experience allows assigning the permissions to MS Graph for B2C Application Registrations- we are looking to fix that).

When you use login.microsoftonline.com and don't provide any policy id parameters against a B2C tenant, you hit the AAD endpoints of the B2C tenant, again it works. You can get tokens to Microsoft Graph API for example, using the users context.

When you use tenantName.b2clogin.com and provide any policy id parameters against a B2C tenant, you hit the AAD B2C endpoints of the B2C tenant, now it will not work as you expected it to. Hopefully the above clarifies why. And since there is no deprecation of the AAD endpoint, you don't need to be using this domain name for this type of call.

The summary is, treat your scenario as a pure Azure AD scenario, as per this sample. You create an Application Registration for Accounts in this organizational directory only. when prompted for the type.

Jas Suri - MSFT
  • 10,605
  • 2
  • 10
  • 20
  • 1
    Not having it. The client credential flow works. And using the AAD endpoints in an interactive or implicit flow takes you through the live.com stuff, bypassing the B2C directory altogether. The question is about the implicit flow. And MS has clearly deprecated login.microsoftonline.com for B2C. https://azure.microsoft.com/en-us/updates/b2c-deprecate-msol/, and https://learn.microsoft.com/en-us/azure/active-directory-b2c/b2clogin – Rob Perkins Jul 02 '20 at 17:27
  • It takes you to Azure AD authentication endpoint, which if you enter a 'live' account will take you to live.com and authenticate you to the tenant, if that account exists inside it as an external account (B2B, not a B2C account). Otherwise you would enter a normal Azure AD account (@onmicrosoft.com), similar to the one you login to the Azure Portal to access/manage your AAD B2C tenant. That could also instead be a live account. Only these accounts are in scope for your scenario. B2C account are not in scope, it won't work with those. – Jas Suri - MSFT Jul 02 '20 at 18:38
  • Linking this as there is duplication. https://github.com/MicrosoftDocs/azure-docs/issues/56756. Deprecation 'issue' is explained there. – Jas Suri - MSFT Jul 02 '20 at 18:40
  • @JasSuri-MSFT I don't think you're realizing, and not saying it's your fault, the point of what it is we're trying to do. And why this issue may be so frustrating. Because it is almost 2023. Some of this stuff is a full 5 years later. I will write another Stack question to try and address what the use case is and perhaps if what you're saying will work. The main issue, which Rob brings up is that this is effectively middle ware at the point of trying to use context from a user to access graph api information that is in context to that user. This is the frustration. What is the proper way. – Christian Matthew Sep 02 '22 at 19:26