Development Information: Azure Registered Mobile Application, Azure hosted API, Visual Studio 2019, mobile application is built in Xamarin Forms targeting .Net-5 and API is built in Asp.Net targeting .Net-5.
Desired Workflow: User does Authorization code flow authentication interactive if AcquireTokenSilently fails, utilizes MS Authenticator. Once authenticated, the user gets an access token for Graph and for the custom hosted API.
What is working: I can do the MFA authentication piece using the MSAL library and an access token is received in the mobile application, but the token is only good for making calls to Graph. I need to be able to call the protected resource (hosted API) as well. My thoughts originally was that the JWT access token would work with the hosted API as well as Graph, but after reading more, I have read that you have to acquire two separate tokens as to prevent issues where two resources may have the same scope(s).
The problem I am facing is that I thought upon Authenticating there would be an easy way to get another resources access token without having to authenticate a second time. Isn't that the point of the OpenID authentication?? My last thought was to do an implicit authentication from the mobile app to the protected API using a client id, client secret, but I don't want to store a client secret in the mobile app for fear it may expose something sensitive to a user. I tried following the documentation of Microsoft and set up specific scopes for the hosted API and registered it in the Azure portal, but this didn't seem to have any affect on the authentication piece. I still have to authenticate against the API which isn't the desired result.
Is it possible to authenticate once for Graph and then knowing that the user authenticated correctly as they now have a valid token for Graph then somehow call this protected API without having to force them to do authentication again for the protected API and if so, how to do this without exposing any sensitive information (client secret) in the mobile application?
CODE
// Microsoft Authentication client for native/mobile apps
public static IPublicClientApplication PCA;
//Setting up the PublicClientApplicationBuilder
//OAuthSettings is a static class with required values (app id, tenant id, etc)
var builder = PublicClientApplicationBuilder
.Create(OAuthSettings.ApplicationId)
.WithTenantId(OAuthSettings.TenantId)
.WithBroker()
.WithRedirectUri(OAuthSettings.RedirectUri);
PCA = builder.Build();
//Scopes being used in initial authentication
//I tried adding to this with a custom scope, I added on the
//protected api and it caused an exception, so I didn't think
//I could use those together with the scopes for Graph
//Custom scope for the protected API is as follows:
//XXX.User.Common (where XXX is our company name)
public const string Scopes = "User.Read MailboxSettings.Read Calendars.ReadWrite";
try
{
var accounts = await PCA.GetAccountsAsync();
var silentAuthResult = await PCA
.AcquireTokenSilent(Scopes.Split(' '), accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException msalEx)
{
var windowLocatorService = DependencyService.Get<IParentWindowLocatorService>();
// Prompt the user to sign-in
var interactiveRequest = PCA.AcquireTokenInteractive(Scopes);
AuthUIParent = windowLocatorService?.GetCurrentParentWindow();
if (AuthUIParent != null)
{
interactiveRequest = interactiveRequest
.WithParentActivityOrWindow(AuthUIParent);
}
var interactiveAuthResult = await interactiveRequest.ExecuteAsync();
var accounts = await PCA.GetAccountsAsync();
//at this point, I have a valid Graph token, but I need to
//get a valid token for the protected API,
//unsure of how to do this last piece
}
AcquireTokenSilently
from the MSAL library passing in parameters(scopes, IAccount)
and it fails to find a token in the token cache as it hasn't been authenticated aganst the protected API. Can you give more direction as to how to acquire the second token without prompting the user to authenticate again? It looks like using the MSAL library is the suggested way, but the only other option I see is callingAcquireTokenInteractively
per the suggested flow, won't this cause the user to Authenticate again? – Ryan Wilson