I have an AAD registered enterprise application set up as single tenant, allowing external guest users to log in. It's set to use ID tokens only (not access tokens) and public client flows are disabled.
The app registration is set to use delegated API permissions for User.Read, and the consent flow is engaged when an external user accepts their invitation after user setup. The app never tries to query graph api for anything other than the user's own profile information.
The resource tenant is using default settings in AAD for "External identities". Guest user access restrictions are set to allow guest users limited access to properties and membership of directory objects.
When a member user in the resource tenant logs in, they can query the graph api successfully retrieving their personal profile details.
When a guest user (B2B) from another AAD tenant logs in, they can successfully log in and view a page in the web app, but when they try to make a call to get their own profile information, graph.microsoft.com is returning a 403 error. The exception is
ServiceException: Code: Authorization_RequestDenied Message: Insufficient privileges to complete the operation..
I've come at this by using the "Me" call and trying to grab the specific user with the id correlating to the oid in the token and the "ext" syntax of the guest UPN. Either attempt generates the above exception, but examples of the calls are below:
await graphClient.Me.Request().Select(u => new { u.DisplayName, u.JobTitle, u.OfficeLocation, u.City, u.CompanyName }).GetAsync();
await graphClient.Users[{oid}].Request().GetAsync();
I've looked at the tokens for a member user and a guest user in jwt.ms. I can see that in the guest user's token, the proper scope for User.Read is there. Additionally, the tenant id is the resource tenant, and the oid in the token is the guid for the guest user in the resource tenant. The appid is correct in the token as well.
So external guests are able to log in to the app just fine, are shipping out a token to graph, but just getting a 403 for the trouble. I did notice that in the token, the upn and email fields are using the guest user's email address rather than the real UPN on the resource tenant ([userEmail]#ext#@myDomain.com), but I think I read that it doesn't matter as the token contains the correct oid (for external user in the resource tenant) and that's what's authoritative.
Any help or thoughts would be appreciated. More than happy to answer questions.
EDIT: I’ll add that for the API permissions, admin consent was granted on the resource tenant on behalf of the org.


