How to configure Azure App Service (for Mobile) and B2C Authentication and access Azure SQL Database - azure

I have a xamarin.forms mobile App using Microsoft.WindowsAzure.MobileServices and Microsoft.Identity.Client. Using EasyAuth I successfully got the xamarin mobile app to post data to the AzureSQL tables linked via connection string in the App Service configuration section. I use the local and offline sync methods of MobileServiceClient. I then attempted to change to B2C authentication. I setup a Tenant and under this tenant registered a new App as a native client called "MobileB2C". Redirect URIs were added automatically. I then created the signinsignup UserFlows.
Back to the Azure App Service (Mobile) under Authentication section I added a provider and selected the B2C App, MobileB2C. I did not populate the "allowed token audiences" field and Azure automatically created Client secret setting name "MICROSOFT_PROVIDER_AUTHENTICATION_SECRET" and the issuer URL.
So when I run the xamarin mobile app I can login via azure B2C and I can see that the authResult returns the users correct info along with UserIdentifier,aud, iss, sub, oid etc.
Once authResult is returned the xamarin mobile then tries to use the sync methods of MobileServiceClient to save data to the AzureSQL table. Its at this point that it fails. When the line await mClient.SyncContext.PushAsync().ConfigureAwait(false); is hit an error occurs described as Microsoft.WindowsAzure.MobileServices.Sync.MobileServicePushStatus.CancelledByAuthentication. I continued to try and confirgure the Azure back end differently and now I no linger get the CancelledByAuthentication error but instead get Microsoft.WindowsAzure.MobileServices.Sync.MobileServicePushStatus.CancelledByNetworkError.
The relevant xamarin mobile app code to implement the authentication and AzureSQL table update is as follows;
private B2CAuthenticationService()
{
// default redirectURI; each platform specific project will have to override it with its own
var builder = PublicClientApplicationBuilder.Create(B2CConstants.ClientID)
.WithB2CAuthority(B2CConstants.AuthoritySignInSignUp)
.WithIosKeychainSecurityGroup(B2CConstants.IOSKeyChainGroup)
.WithRedirectUri($"msal{B2CConstants.ClientID}://auth");
// Android implementation is based on https://github.com/jamesmontemagno/CurrentActivityPlugin
// iOS implementation would require to expose the current ViewControler - not currently implemented as it is not required
// UWP does not require this
var windowLocatorService = DependencyService.Get<IParentWindowLocatorService>();
if (windowLocatorService != null)
{
builder = builder.WithParentActivityOrWindow(() => windowLocatorService?.GetCurrentParentWindow());
}
_pca = builder.Build();
}
public async Task<UserContext> SignInAsync()
{
UserContext newContext;
try
{
// acquire token silent
newContext = await AcquireTokenSilent();
}
catch (MsalUiRequiredException)
{
// acquire token interactive
newContext = await SignInInteractively();
}
return newContext;
}
private async Task<UserContext> SignInInteractively()
{
AuthenticationResult authResult = await _pca.AcquireTokenInteractive(B2CConstants.Scopes)
.ExecuteAsync();
var newContext = UpdateUserInfo(authResult);
UserSingleton.Instance.UserId = newContext.UserIdentifier;
return newContext;
}
THe xamarin mobile app adds a record to the local database and then RefreshItemsAsync begins the synchronisation to the AzureSQL.
await azureService.AddUserSurveyAsync(newSurvey).ConfigureAwait(false);
await azureService.RefreshItemsAsync(true).ConfigureAwait(false);
It is at the PushAsync line below that the the code fails.
public async Task InitializeAsync()
{
using (await initializationLock.LockAsync())
{
if (!isInitialized)
{
mClient = new MobileServiceClient(https://mobileservice.azurewebsites.net);
// Define the offline store.
mStore = new MobileServiceSQLiteStore("mobile3.db");
mStore.DefineTable<UserSurvey>();
await mClient.SyncContext.InitializeAsync(mStore, new MobileServiceSyncHandler()).ConfigureAwait(false);
UserSurveyTable = mClient.GetSyncTable<UserSurvey>();
isInitialized = true;
}
}
}
public async Task RefreshItemsAsync(bool syncItems)
{
if (syncItems)
{
await SynchronizeAsync().ConfigureAwait(false);
}
}
public async Task SynchronizeAsync()
{
await InitializeAsync().ConfigureAwait(false);
IReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;
if (!CrossConnectivity.Current.IsConnected)
return;
try
{
await mClient.SyncContext.PushAsync().ConfigureAwait(false);
await UserSurveyTable.PullAsync("usersurveys", UserSurveyTable.CreateQuery()).ConfigureAwait(false);
}
catch (MobileServicePushFailedException error)
{
if (error.PushResult != null)
{
foreach (var result in error.PushResult.Errors)
{
await ResolveError(result);
}
}
}
}
What is wrong with the Azure back end configuration or perhaps I'm missing code as I can't understand how the xamarin mobile app can then attempt to communicate with the Azure Mobile App Service and AzureSQL as I don't send any token with those lines of code for PushAsync etc or perhaps this is abstracted away?
Here are images of the exceptions;
enter image description here
enter image description here

As promised, here is the succinct version of AAD authentication. For your purposes, B2C authentication is the same as AAD authentication.
There are two application definitions at play here - one for the mobile application (which basically says "this person is authenticated"), and one for the service (which says "a token authenticated for this mobile application can access this service"). So, you create an application ID for your mobile application, and an application ID for your service, and then you configure the service application ID to accept the mobile application.
The "WPF" tutorial for Azure Mobile Apps gives the general overview, although it's for WPF instead of Xamarin. The pieces you need are all the same.
The "WPF" tutorial here: https://learn.microsoft.com/en-us/azure/developer/mobile-apps/azure-mobile-apps/quickstarts/wpf/authentication

Related

How to authenticate our .NET console application against SharePoint online if we have `DisableCustomAppAuthentication` set to true

We have the following:-
SharePoint online tenant recently created
Windows server 2019
.NET console application which have some code that integrates with SharePoint online
The .NET console application runs on schedule basis using windows task scheduler.
now previously on old tenants i authenticate my code using this method by passing the ClientID and Client Secret:-
static void Main(string[] args)
{
string siteUrl = "https://***.sharepoint.com/sites/CustomerServiceKB/";
string clientId = "******";
string clientSecret = "*****";
using (ClientContext context = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(siteUrl, clientId, clientSecret))
{
but on our newly created tenant we can not authenticate our code using the above method, because we have the DisableCustomAppAuthentication set to true.. now we do not want to modify this property.
So our question is; if we have the DisableCustomAppAuthentication set to true (ans we do not want to set it to false), then how we can authenticate our console application? which is hosted inside our windows server and which runs on schedule basis using tasks scheduler ?
So the DisableCustomAppAuthentication property was brought in (and was set to true by default) to support the deprecation of the Azure Access Control Service (ACS). The modern way to authenticate custom apps in Sharepoint tenants is to register them in the Azure AD.
Before moving on, consider the pros and cons of switching to the new authentication scheme. Mainly, the ACS enables users to granularly control site-level permissions for the application authentication, but Azure AD app registration makes the set of running applications transparent to the administrators. If you want to stay with ACS, just set the DisableCustomAppAuthentication to false.
Now, if you decided to move on with registering the application in the Azure ID, here are the steps to follow:
Log in to the Azure portal and navigate to the Azure Active Directory.
Register the application in the Azure AD portal. Here's a guide on how to do it. Obtain the application (client) ID on the application overview page.
Set all the necessary permissions, confirm them as a global administrator (or ask the administrator for confirmation).
Configure the authentication. Choose the authentication option: whether you want the application to authenticate itself in the Microsoft IAM via a cryptographic certificate or a client secret.
Obtain your Azure Active Directory tenant ID (not to be confused with the Sharepoint Online tenant ID). Here's how to do in in the Azure AD; there's also a hacky way.
Now use the client ID (from step 2), the authentication option (step 4), and the AAD tenant ID (step 5) to authenticate and run your application:
using Microsoft.Identity.Client;
[..]
private static async Task<string> GetToken()
{
string applicationId = "client-id";
string tenantId = "aad-tenant-id";
bool isUsingClientSecret = <true or false>;
IConfidentialClientApplication app;
if (isUsingClientSecret)
{
string secret = "secret";
app = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithClientSecret(secret)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.Build();
}
else
{
string certificateLocation = "certificate-file";
X509Certificate2 certificate = ReadCertificate(certificateLocation);
app = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithCertificate(certificate)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.Build();
}
var scopes = new[] { "https://***.sharepoint.com/.default" };
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
return authenticationResult.AccessToken;
}
static async Task MainAsync(string[] args)
{
string site = "https://***.sharepoint.com/sites/CustomerServiceKB";
string token = await GetToken();
using (ClientContext context = new ClientContext(site))
{
context.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + token;
};
Web web = context.Web;
context.Load(web);
context.ExecuteQuery();
}
}
static void Main(string[] args)
{
try
{
AsyncContext.Run(() => MainAsync(args));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw;
}
}

How to enable Windows Authentication with in-process IIS hosting under IdentityServer4?

My ASP.Net Core MVC app accesses a .Net Core API through IdentityServer. It works fine on IIS server running in-process with Entity Framework based identity store. Now I am trying to enable Windows Authentication and getting stuck here.
What I tried is following the identityserver doc section "Windows Authentication" - I added the code below to the ConfigureServices of my IdentityServer's Startup.cs
// configures IIS in-proc settings
services.Configure<IISServerOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
I also enabled the Windows Authentication in IIS for my API app
The part of the doc that I am confused about is "You trigger Windows authentication by calling ChallengeAsync on the Windows scheme". It doesn't mention where you do that. I am assuming it is in identityserver and I put the code in the Login method of the AccountController of the identityserver as bellow.
/// <summary>
/// Entry point into the login workflow
/// </summary>
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
// trigger Windows authentication by calling ChallengeAsync
await ChallengeWindowsAsync(returnUrl);
// build a model so we know what to show on the login page
var vm = await BuildLoginViewModelAsync(returnUrl);
if (vm.IsExternalLoginOnly)
{
// we only have one option for logging in and it's an external provider
return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
}
return View(vm);
}
private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl)
{
// see if windows auth has already been requested and succeeded
var result = await HttpContext.AuthenticateAsync("Windows");
if (result?.Principal is WindowsPrincipal wp)
{
// we will issue the external cookie and then redirect the
// user back to the external callback, in essence, treating windows
// auth the same as any other external authentication mechanism
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action("Callback"),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", "Windows" },
}
};
var id = new ClaimsIdentity("Windows");
// the sid is a good sub value
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));
// the account name is the closest we have to a display name
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
// add the groups as claims -- be careful if the number of groups is too large
var wi = wp.Identity as WindowsIdentity;
// translate group SIDs to display names
var groups = wi.Groups.Translate(typeof(NTAccount));
var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
id.AddClaims(roles);
await HttpContext.SignInAsync(
IdentityServerConstants.ExternalCookieAuthenticationScheme,
new ClaimsPrincipal(id),
props);
return Redirect(props.RedirectUri);
}
else
{
// trigger windows auth
// since windows auth don't support the redirect uri,
// this URL is re-triggered when we call challenge
return Challenge("Windows");
}
}
What I expect to happen, if everything goes well, is that the authentication happens automatically (without a login box?) because the "Challenge" call will require the client side (the browser) to send in Windows identity info and a token will be issued based on that.
It doesn't seem to work that way now - I am getting an Unauthorized error from API when starting the MVC app:
Am I doing that in the wrong place? Or am I missing something else?

How to obtain the token returned from Azure AD B2C in ASP Core 2.0?

I have used Visual Studio's latest New Project wizard to create a ASP Core 2.0 Web page (Razor Pages) that uses Individual Accounts as my authentication option. I have created an Azure AD B2C tenant and validated that it works properly.
When I run the web application that was created by the wizard and click Log In in the upper right, it redirects to my Azure AD B2C site, and I can properly login.
After login, the callback url goes to the endpoint configured in my user secrets:
...
"CallbackPath": "/signin-oidc",
...
That all seems to work properly. I understand that the Azure AD B2C portal sends a token back to the above /signin-oidc callback path and stores it.
How can I retrieve the value of that token?
I've been following all of the Azure AD B2C guides, but not all of them have been updated to ASP Core 2.0, and none of them seem to use the code generated from the 15.4 VS wizard such:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("AzureAdB2C", options))
.AddCookie();
services.AddMvc();
}
Note: the .AddAzureAdB2C(...)
None of the B2C samples are using this so its difficult for me to follow.
My end goal is to get the token and use that in a strongly-typed set of API classes I generated from Swagger using Autorest which require the token.
The best way to do this is outlined in the Azure AD B2C .Net Core sample, specifically the branch for Core 2.0.
In the normal model/flow, your application will get an id_token and an authorization code but not a token. The authorization code needs to be exchanged for a token by your middle tier. This token is what you'd then be sending over to your web API.
The way to do this involves the following:
Ensure your middle tier is requesting id_token+code for your primary policy (you don't want to do this for your edit profile or password reset policies). From the sample's OpenIdConnectOptionsSetup.cs#L77:
public Task OnRedirectToIdentityProvider(RedirectContext context)
{
var defaultPolicy = AzureAdB2COptions.DefaultPolicy;
if (context.Properties.Items.TryGetValue(AzureAdB2COptions.PolicyAuthenticationProperty, out var policy) &&
!policy.Equals(defaultPolicy))
{
context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.ToLower().Replace(defaultPolicy.ToLower(), policy.ToLower());
context.Properties.Items.Remove(AzureAdB2COptions.PolicyAuthenticationProperty);
}
else if (!string.IsNullOrEmpty(AzureAdB2COptions.ApiUrl))
{
context.ProtocolMessage.Scope += $" offline_access {AzureAdB2COptions.ApiScopes}";
// -----------------------------
// THIS IS THE IMPORTANT PART:
context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.CodeIdToken;
// -----------------------------
}
return Task.FromResult(0);
}
Exchange the code for a token. From the sample's OpenIdConnectOptionsSetup.cs#L103-L124:
public async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
// Use MSAL to swap the code for an access token
// Extract the code from the response notification
var code = context.ProtocolMessage.Code;
string signedInUserID = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, context.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, AzureAdB2COptions.ApiScopes.Split(' '));
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
catch (Exception ex)
{
//TODO: Handle
throw;
}
}
You can then use this token elsewhere in your code to call an API. From the sample's HomeController.cs#L45-L57:
var scope = AzureAdB2COptions.ApiScopes.Split(' ');
string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, this.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), AzureAdB2COptions.Authority, false);
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdB2COptions.ApiUrl);
// Add token to the Authorization header and make the request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
The ASP.NET Core team has created 2 excellent documents on this process.
Cloud authentication with Azure Active Directory B2C in ASP.NET Core
Use Visual Studio to create an ASP.NET Core web app configured to use the Azure AD B2C tenant for authentication
Cloud authentication in web APIs with Azure Active Directory B2C in ASP.NET Core
Use Visual Studio to create a Web API configured to use the Azure AD B2C tenant for authentication

Getting Unauthorized from from Azure Web API

I created a basic project using Visual Studio 2015 Update 3 for Web API (nothing custom, bare bone) and deployed it to Azure (Free Account) following the instruction here.
Then I created a Console client with the following code.
public static async Task<bool> ReadValues()
{
try
{
// Authenticate the user and get a token from Azure AD
//AuthenticationResult authResult = await AuthContext.AcquireTokenSilentAsync(Resource, ClientId);
AuthenticationResult authResult = AuthContext.AcquireToken(Resource, ClientId, RedirectUri);
// Create an HTTP client and add the token to the Authorization header
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
//"Bearer"
authResult.AccessTokenType
, authResult.AccessToken);
// Call the Web API to get the values
var requestUri = new Uri(WebApiUri, "api/values");
Console.WriteLine("Reading values from '{0}'.", requestUri);
HttpResponseMessage httpResponse = await httpClient.GetAsync(requestUri);
Console.WriteLine("HTTP Status Code: '{0}'", httpResponse.StatusCode.ToString());
//Console.WriteLine("HTTP Header: '{0}'", httpClient.DefaultRequestHeaders.Authorization.ToString());
if (httpResponse.IsSuccessStatusCode)
{
//
// Code to do something with the data returned goes here.
//
var s = await httpResponse.Content.ReadAsStringAsync();
Console.WriteLine(s);
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(httpResponse.ReasonPhrase);
}
return (httpResponse.IsSuccessStatusCode);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
It works fine when I run the WEB API locally from the Visual Studio in debug, but when I deploy it to the Azure, it returns Unauthorized.
Few common things that I might get asked:
I do receive a valid bearer token
I have created the App registrations in the Azure AD for bot hthe WEB API and the client
The client and WEB API are using the correct redirect, resource uri
The account I am using to login is the same as the one used to create the Azure account and it has full privileges in the domain/AD/API
On the API side, this is whole of the startup.auth.cs
using System.Configuration;
using System.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security.ActiveDirectory;
using Owin;
using WebApi;
[assembly: OwinStartup("default", typeof(Startup))]
namespace WebApi
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
}
}
What else should I check?
Other references
https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-3-developing-native-client-applications/
Thanks for help from Juunas who provided me with a working copy, I was able to narrow down the cause. When I attached a debugger to the Azure instance of the Web API I was able to see a exception for Bad Audience. On trying to retrace my steps, I found that while deployment from Visual Studio, I was selection Enterprise Authentication in settings that was causing the web.config to change in way that lead to the problem. Not selecting that option, I was able to access the API through bearer token.

How to enable App Service Mobile App SSO for UWP

I am building a Universal Windows Platform (UWP) app that uses the Azure App Service Mobile App backend as well as the user's OneDrive account. I have 2 requirements for authentication:
If the user is logged in to their UWP device with a Microsoft account (e.g. Windows 10) then I don't want them to be presented with a login prompt (i.e. Single Sign On, re-using their Microsoft account credentials).
I want to have a single authentication event across Azure & OneDrive, i.e. the user authorises once and I re-use that token for both services.
I did this in Windows Phone 8 with an Azure Mobile Service by logging in with the Live SDK and then passing the returned token to the MobileServiceClient.LoginAsync() method, however I can't get this to work in UWP with an Azure Mobile App. When I call that same method I receive a 401 Unauthorised response.
I have associated my UWP app with the store and set up the
application at the Microsoft Account Developer Centre, including
adding the redirect URI from the Azure Mobile App.
I have set up the Azure App Service Mobile App, including adding the
Client ID & Secret from the Microsoft Account Developer Centre.
I have tried numerous ways to retrieve the token, including the
OnlineIdAuthenticator, WebAuthenticationCoreManager and
WebAuthenticationBroker. None has worked so far.
I currently use the following code in a class LiveAuthenticationService to retrieve an access token:
public async Task<bool> LoginAsync()
{
AccessToken = null;
bool success = false;
OnlineIdAuthenticator onlineIdAuthenticator = new OnlineIdAuthenticator();
EventWaitHandle waithandle = new ManualResetEvent(false);
OnlineIdServiceTicketRequest serviceTicketRequest = new OnlineIdServiceTicketRequest(scopes, "DELEGATION");
UserIdentity result = await onlineIdAuthenticator.AuthenticateUserAsync(serviceTicketRequest);
if (!string.IsNullOrWhiteSpace(result?.Tickets[0]?.Value))
{
currentUserId = result.SafeCustomerId;
AccessToken = result.Tickets[0].Value;
success = true;
waithandle.Set();
}
else
{
await logger.LogErrorAsync("Error signing in to Microsoft Live",
new Dictionary<string, string> { { "errorCode", result?.Tickets[0]?.ErrorCode.ToString() } });
}
waithandle.WaitOne(10000); //10 second timeout
return success;
}
And then this to attempt to login to my Azure Mobile App with that token, which uses LiveAuthenticationService from above:
private async Task RefreshUserIdAndAccessToken()
{
try
{
var tcs = new TaskCompletionSource<MobileServiceUser>();
var authService = new LiveAuthenticationService();
await UiDispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
try
{
await authService.LoginAsync();
var jsonAuthenticationToken = JObject.Parse(#"{""authenticationToken"": """ + authService.AccessToken + #"""}");
tcs.SetResult(await mobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, jsonAuthenticationToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
var user = await tcs.Task;
currentUserId = user.UserId;
AccessToken = user.MobileServiceAuthenticationToken;
}
catch (Exception ex)
{
await logger.LogExceptionAsync(ex,
Constants.LOGGING_DATAKEY_REFRESHACCESSTOKENFAILURE,
currentUserId);
currentUserId = null;
AccessToken = null;
}
}
As stated this results in a 401 Unauthorised response from Azure. I have run Fiddler and the request seems to be correct, the expected authentication token is included in a JSON payload with the request.
UPDATE
One thing I can see is that the token issued by the code above is almost 900 characters long, all in the form YnElFkAAcK8bRSQab/FK+PT5n/wA4CPU..., while the token issued if I let Azure Mobile App handle the authentication, i.e. call MobileServiceClient.LoginAsync() without passing a token, is only about 350 characters long and in the form hbGciOi.eyJmdWWxsIiwiRGJn... (notice the period towards the beginning).
This issue is really causing me problems now. I can't release the app without the authentication working and I can't figure out how to fix it. Any help will be appreciated.
This was a tough one for me to solve as I was facing this problem too.
The most important part is the OnlineIdServiceTicketRequest the request should look like this:
var mobileServicesTicket = new OnlineIdServiceTicketRequest("https://yourmobileservice.azure-mobile.net/", "JWT");
Note that we are specifying your endpoint and also requesting a JWT token instead of delegation. This will get the 350ish character token you were looking for.
Here is a full code sample of what I'm doing:
public async Task<bool> LoginAsync()
{
var authenticator = new Windows.Security.Authentication.OnlineId.OnlineIdAuthenticator();
var mobileServicesTicket = new Windows.Security.Authentication.OnlineId.OnlineIdServiceTicketRequest("https://yourendpoint.azure-mobile.net/", "JWT");
var ticketRequests = new List<OnlineIdServiceTicketRequest>() { mobileServicesTicket };
var authResult = await authenticator.AuthenticateUserAsync(ticketRequests, CredentialPromptType.PromptIfNeeded);
if ((authResult.Tickets.Count == 1) && (authResult.Tickets[0].ErrorCode == 0))
{
var accessToken = authResult.Tickets[0];
var res = await _mobileServiceClient.LoginWithMicrosoftAccountAsync(accessToken.Value);
return true;
}
else
{
return false;
}
}
_mobileServiceClient is injected into the class and is a reference to Microsoft.WindowsAzure.MobileServices.MobileServiceClient object within the WindowsAzure.MobileServices library.
I actually ended up writing a blog article about this problem here http://jshapland.com/single-sign-on-with-azure-mobile-services-in-a-uwp-app/

Resources