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

Demo 4-WebApp-Your-API Application ID URI should be https pattern #336

Open
1 of 14 tasks
franva opened this issue Apr 18, 2020 · 17 comments
Open
1 of 14 tasks

Demo 4-WebApp-Your-API Application ID URI should be https pattern #336

franva opened this issue Apr 18, 2020 · 17 comments
Assignees
Labels
enhancement New feature or request

Comments

@franva
Copy link

franva commented Apr 18, 2020

Please provide us with the following information:

This issue is for a: (mark with an x)

- [ ] bug report -> please search issues before submitting
- [ ] feature request
- [x] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)

The issue was found for the following scenario:

Please add an 'x' for the scenario(s) where you found an issue

  1. Web app that signs in users
    1. with a work and school account in your organization: 1-WebApp-OIDC/1-1-MyOrg
    2. with any work and school account: /1-WebApp-OIDC/1-2-AnyOrg
    3. with any work or school account or Microsoft personal account: 1-WebApp-OIDC/1-3-AnyOrgOrPersonal
    4. with users in National or sovereign clouds 1-WebApp-OIDC/1-4-Sovereign
    5. with B2C users 1-WebApp-OIDC/1-5-B2C
  2. Web app that calls Microsoft Graph
    1. Calling graph with the Microsoft Graph SDK: 2-WebApp-graph-user/2-1-Call-MSGraph
    2. With specific token caches: 2-WebApp-graph-user/2-2-TokenCache
    3. Calling Microsoft Graph in national clouds: 2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph
  3. Web app calling several APIs 3-WebApp-multi-APIs
  4. Web app calling your own Web API 4-WebApp-your-API
  5. Web app restricting users
    1. by Roles: 5-WebApp-AuthZ/5-1-Roles
    2. by Groups: 5-WebApp-AuthZ/5-2-Groups
  6. Deployment to Azure
  7. Other (please describe)

Repro-ing the issue

Repro steps

  1. On the Active Directory Dotnet Native Aspnetcore V2 Demo 3, section

7 Select the Expose an API section

It asks to

Change the Application ID URI to the https pattern, check AzureADandPersonalMicrosoftAccount restrictions, (https://{tenant-domain}/{app-name}) and select Save and Continue.

but on the demo Active Directory Aspnetcore Webapp OpenIdConnect V2 4-WebApp-Your-API, section

6 Select the Expose an API section

It asks for

accept the proposed Application ID URI (api://{clientId}) by selecting Save and Continue

Since they both use Web Api as backend service to serve frontend(one as WPF client, one as Webapp), the configuration for Application ID URI should be same.

Why do they have 2 inconsistent configurations?

I believe, this Application ID URI should be one of them: https or api.

@jmprieur jmprieur added the enhancement New feature or request label Apr 20, 2020
@TiagoBrenck
Copy link
Contributor

Hi @franva, it depends. Is your API configured to support AzureADandPersonalMicrosoftAccount? If yes, then the https pattern should be used, otherwise, you can use either of them since you dont have any restrictions. To check those restrictions, please follow this doc.

@franva
Copy link
Author

franva commented Apr 21, 2020

hi @TiagoBrenck thanks for pointing out where the document is.
However, after looked at that doc, it makes me more confused.
Please look at the screenshot below:
image

Yes, I need to support both org and personal accounts. According to your answer, the Application ID URI should be the https pattern. But the referenced document does not mention https or api:// at all.

One more thing to point out, I can see Microsoft has poured tremendous efforts on documentations, I really appreciate that as it makes our lives easier. But as the more and more documents written, the new documents and the old ones are co-existing and there are so many documents there which confused me a lot.

Take the Microsoft Identity Platform as an example, all I need to do is to fetch emails from user's Hotmail using OBO flow, and I have a separated API project and a Reactjs project. I searched days and nights and found heaps of documents and in the end, the Javascript demo project doesn't even work in Firefox, only working in Edge. so some of documents are lack of love.

@TiagoBrenck
Copy link
Contributor

Hi @franva, I am sorry for causing even more confusion.

So, the doc statement: "urn:// schemes are not supported" is the restriction that affects api://<>. Then, you should use another pattern, like the https:<> one. We have this sample that is a WebAPI using OBO flow with personal accounts (hotmail, xbox live etc). It might be worthy exploring it.

About the documentation feedback, I will forward it to the docs team. Could you share the Javascript demo link for me please?

@franva
Copy link
Author

franva commented Apr 24, 2020

Thanks @TiagoBrenck I am still trying to find out an up-to-date example which fits my requirements.
Once again, I found this one On point 10, it still shows to use the api:// pattern, even Point 3 has already selected the AnyOrg+Personal Accounts.

There are so many documents, but none of them works for me. I'm lost in the sea of out-of-date documents.

What I want is Reactjs + AspNet Core WebApi to fetch user's emails and just found this repo:
Azure-Samples/ms-identity-javascript-react-spa-dotnetcore-webapi-obo#1

But it's empty.

Please help.

@TiagoBrenck
Copy link
Contributor

Hi @franva

I would like to apologize for a confusion about the MSA and api:// pattern. Due to a miscommunication, we told you that api:// is not supported for MSA however it is. We will update the sample to remove this miss information. It was actually a bug but it got fixed already.

Could you let me know if you manged to have your MSA scenario working?

Thanks

@franva
Copy link
Author

franva commented Apr 30, 2020

thanks @TiagoBrenck for the update.

Unfortunately, no. I have been stuck here for about 4 weeks.

I have put my question here:
https://stackoverflow.com/questions/61524182/msal-net-no-account-or-login-hint-was-passed-to-the-acquiretokensilent-call

If you could have a look, that would be great.

Thanks

@TiagoBrenck
Copy link
Contributor

TiagoBrenck commented Apr 30, 2020

Your scenario is on-behalf-of flow, so the web api configuration is different than the one in this repo.

Your WebAPI configuration on AzureAD should follow this: https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph#register-the-service-app-todolistservice.

Make sure to set knownClientApplications, and that your SPA app requests an access token for the Web API using the /.default scope.

Another observation is that [AuthorizeForScopes] only works for client apps (web mvc project). It won't work on your API action results.

@franva
Copy link
Author

franva commented May 1, 2020

thanks @TiagoBrenck for pointing out the correct example to follow.

I checked my app registration for my API project on Azure, it's almost identical to the one you provided(https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph#register-the-service-app-todolistservice) EXCEPT the Application ID URI. So it seems that all discussions come back to my first question :)
Should I use http:// pattern or the api:// patter? I am now totally lost >_<;

Why should I follow the dotnet-native example rather than the aspnetcore example, as in my case I am using Angular app, instead of the WPF client?

Also, in my Web Api and Client App(Angular), I am using the same Client Id as recommended by derisen at here, so for the client app, should I also follow the example you provided for its Azure registration?
If I keep using same Client Id, do I still need to set knownClientApplications to include the WebApi App ID?

I do not understand your last sentence : "It work on your API action results"
Did you want to say: it doesn't work on your API action results?

Once again, I appreciate your help.

@franva
Copy link
Author

franva commented May 1, 2020

I updated my SPA request to use:

"resourceScope": "https://papayee008.onmicrosoft.com/papayee008/.default"

And here is the configuration for the SPA
image

and here is the config for Web Api:
image
(Btw, I also tried
"GraphApiUrl": "https://graph.microsoft.com/v1.0"
same error.)

But the error persists.
image

@TiagoBrenck
Copy link
Contributor

Should I use http:// pattern or the api:// patter? I am now totally lost >_<;
Sorry about that. You can choose the one you prefer.

Why should I follow the dotnet-native example rather than the aspnetcore example, as in my case I am using Angular app, instead of the WPF client?
I recommended that one just because of the Web API configuration for OBO flow. You can ignore the WPF client part, and just study the web api project.

If I keep using same Client Id, do I still need to set knownClientApplications to include the WebApi App ID?
Sorry I didnt know you were using the same client ID. In that case, the example that I provided wont match your scenario

Did you want to say: it doesn't work on your API action results?
Ops I miss typed that and edited the answer. That attribute doesn't work on Web API action results.

I am a bit confuse with your scenario since I cant see the whole project with the configurations. I am assuming you have a separate project just for the web api, and from the posts you have on Stackoverflow, it seems that you are missing AddProtectedWebApiCallsProtectedWebApi configuration in your web api startup. That is why I keep pushing you to use the dotnet-native example to configure your Web API. Please compare your current web api configurations with the code there, and make the missing adjustments.

@franva
Copy link
Author

franva commented May 2, 2020

hi @TiagoBrenck

I think i finally have made some progress.
I found the git repository -> Microsoft.Identity.Web and read through its Readme and understood why you asked me to call AddProtectedWebApiCallsProtectedWebApi.
Before diving deeper, I'd like to clarify the name ->AddProtectedWebApiCallsProtectedWebApi

There are 2 ProtectedWebApi in this name. My understanding is, the 1st ProtectedWebApi is my own Web Api and the 2nd ProtectedWebApi is the downstream Microsoft Graph Api? Is my understanding correct?

Now, in the TodoListController, I added the following code:

    using Graph = Microsoft.Graph;
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
      readonly ITokenAcquisition tokenAcquisition;
      readonly WebOptions webOptions;

      private readonly TodoContext _context;

      public TodoListController(TodoContext context, ITokenAcquisition tokenAcquisition, IOptions<WebOptions> webOptionValue)
      {
          this.tokenAcquisition = tokenAcquisition;
          this.webOptions = webOptionValue.Value;
          _context = context;
      }

        [HttpGet("emails")]
      [AuthorizeForScopes(Scopes = new[] { Constants.ScopeUserRead, Constants.ScopeMailRead })]
      public async Task<IActionResult> Profile()
      {
          HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);

          var subject = string.Empty;
          try
          {
              // Initialize the GraphServiceClient. 
              Graph::GraphServiceClient graphClient = GetGraphServiceClient(new[] { Constants.ScopeUserRead, Constants.ScopeMailRead });

              var me = await graphClient.Me.Request().GetAsync();
              // Get user photo
              var messages = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
              subject = messages.First().Subject;
              return Ok(subject);
          }
          catch (System.Exception ex)
          {
              throw ex;
          }
      }

      private Graph::GraphServiceClient GetGraphServiceClient(string[] scopes)
      {
          return GraphServiceClientFactory.GetAuthenticatedGraphClient(async () =>
          {
              string result = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
              return result;
          }, webOptions.GraphApiUrl);
      }

Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            // Setting configuration for protected web api
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddProtectedWebApi(Configuration);

            services.AddProtectedWebApiCallsProtectedWebApi(Configuration).AddInMemoryTokenCaches();

            //services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { Constants.ScopeUserRead, Constants.ScopeMailRead })
            //    .AddInMemoryTokenCaches();

            services.AddOptions();
            services.AddGraphService(Configuration);

            // Creating policies that wraps the authorization requirements
            services.AddAuthorization();

            services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));

            services.AddControllers();
            
            // Allowing CORS for all domains and methods for the purpose of sample
            services.AddCors(o => o.AddPolicy("default", builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyMethod()
                       .AllowAnyHeader();
            }));
        }

The added code comes from here

On the Angular app side, I added this code:

TodoService.ts

  getEmails() {
    return this.http.get(this.url + 'emails');
  }

todo-view.component.ts

  getEmails(): void {
    this.service.getEmails().subscribe({
      next: (emails: any) => {
        alert(emails);
      },
      error: (err: any) => {
        console.log("error happened~!");
        console.log(err);
      }
    });
  }

todo-view.component.html, at any proper place, I added this code:
<button (click)="getEmails();">Get Emails</button>

Then when I launched the Angular app and signed in, and clicked the Get Emails button, I received a different error:

image

I inspected the exception and found there is no inner exception anymore. The error message is very generic and gives me no clue about what I could do.

With the given code here, I think you should be able to reproduce my issue.
Once again, thanks for your consistent update.

@franva
Copy link
Author

franva commented May 2, 2020

I added a reference to Microsoft.Identity.Web project so that I can debug inside it.

And I found that the code was trying to fetch value from cache but got null:
image

Then it cannot find an account:
image

Then throws error here:
image

But I have no idea how to get it fixed.

@TiagoBrenck
Copy link
Contributor

Cool, I think we are making progress. Your understanding about the AddProtectedWebApiCallsProtectedWebApi is correct.

Could you get what is null in this last screenshot? Is it the variable tokenUsedToCallTheWebApi?

@franva
Copy link
Author

franva commented May 5, 2020

hi @TiagoBrenck the "account" is null, you can see it from the second last screenshot.

Cheers,

Winston

@TiagoBrenck
Copy link
Contributor

@franva would you be able to have a screen sharing session with me, so I can help you troubleshoot this problem?

@franva
Copy link
Author

franva commented May 6, 2020

@TiagoBrenck thanks man, Yes, sure. I am based in Melbourne Australia, I can do screen sharing after work 6PM, or anytime in weekends. What is your preferred tool to use?
Team? Skype? Zoom?
I will add you there.

Thanks again~!

@franva
Copy link
Author

franva commented May 8, 2020

hi @TiagoBrenck thanks for your willingness to help.

I have tried, I cannot use my own Microsoft account to log into Team and cannot find you if I logged in with my company's account.

my email is [email protected]

Could you please add me on Skype? or any other means you prefer.

Thanks a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants