Azure Function App: Authentication Breaks Development Portal - azure

I've added Azure Active Directory Authentication to my function app, but as soon as I set "Action to take when request is not authenticated" to "Login with Azure Active Directory", the development interface for the function app yields this message:
Error:
We are unable to reach your function app. Your app could be having a temporary issue or may be failing to start. You can check logs or try again in a couple of minutes.
Session Id: 23a5880ec94743f5a9d3ac705515b294
Timestamp: 2016-11-16T08:36:54.242Z
Presumably adding the authentication requirement breaks access to the function app in some fashion... though I am able to make changes in the code editor, and they do take effect, I no longer see updates in the log panel: no compilation output messages, for example.
Does anyone know a work-around for this?
So far, I've tried just leaving the auth option to "Allow anonymous requests (no action)" and using this following code:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var user = "Anonymous";
var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;
if (claimsPrincipal != null && claimsPrincipal.Identity.IsAuthenticated)
{
user = claimsPrincipal.Identity.Name;
log.Info($"Hello {user}");
}
return req.CreateResponse(HttpStatusCode.OK, "Hello " + user);
}
However, this (rightly) doesn't redirect to the authentication provider... I would prefer to have the app take care of all that gunge for me, but if doing so means I can't see compilation messages / log messages, it makes it pretty hard to see what's going on.

Nathan,
Unfortunately, this is a limitation at the moment and we're tracking it here: https://github.com/projectkudu/AzureFunctionsPortal/issues/794
Your approach, to allow anonymous and validate in the function is what we recommend at the moment. To extend your workaround, you can add the following code to initiate a login redirect when you detect an anonymous user (the code below assumes you are using AAD).
else
{
log.Info("Received an anonymous request! Redirecting...");
var res = req.CreateResponse(HttpStatusCode.Redirect);
res.Headers.Location = new Uri(req.RequestUri, $"/.auth/login/aad?post_login_redirect_uri={req.RequestUri.AbsolutePath}&token_mode=session");
return res;
}
We understand that isn't ideal and appreciate your patience while we work to improve this.
Thanks!

Related

Microsoft Graph returning null account even after passing a valid account ID

I am encountering a weird issue with Microsoft Graph on an integration that was built a few years back.
This issue started happening a few months back. After I sync a Microsoft Account and provide email and calendar read/write access, everything works fine for some time. I am able to retrieve emails and calendar events. However, after some time, I notice that when a call is made to GetAccountAsync with a valid AccountID, null is returned. This is causing AcquireTokenSilent to fail with the following error:
Error Code: user_null
Error Message: No account or login hint was passed to the AcquireTokenSilent call.
I have also noticed that this happens under the following scenarios:
When the WebJob (console app) is run every 15 minutes, I encounter this issue
To narrow down the root cause, I have deleted the WebJob to see if the issue occurs on the web app. It looks like the issue starts to occur after an hour or so even without the web job running.
I have upgraded to the latest version of MSAL and implemented 4.46.1.0 version of Microsoft.Identity.Client. I am using .NET Framework 4.8 and this is a .NET MVC 5 app.
Here's my code:
public async Task<string> GetAccessTokenAsync()
{
string accessToken;
UserExternalApp.Scope = string.IsNullOrWhiteSpace(UserExternalApp.Scope) ? "" : UserExternalApp.Scope;
// Load the app config from web.config
var microsoftScopes = UserExternalApp.Scope.Replace(' ', ',').SplitAndTrim(new char[] { ',' }).ToList();
var accountID = UserExternalApp.ExternalUserAccountID;
var app = ConfidentialClientApplicationBuilder.Create(ClientID)
.WithRedirectUri(DefaultRedirectUrl) // https:\//mywebsite.com
.WithClientSecret(Secret)
.Build();
app.AddDistributedTokenCache(services =>
{
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
try
{
var account = await app.GetAccountAsync(accountID);
var query = app.AcquireTokenSilent(microsoftScopes, account); // This is where the error is thrown
var acquireTokenSilent = await query.ExecuteAsync();
accessToken = acquireTokenSilent.AccessToken;
}
catch
{
// This is the error thrown:
// Exception Type: MsalUiRequiredException
// Error code: user_null
// Exception Details: No account or login hint was passed to the AcquireTokenSilent call.
throw;
}
return accessToken;
}
I know the token is persisted on my SQL Server:
I think the MSAL uses an in memory token cache by default, Once the client logins, authentication information will be stored in cookie(if cookie has not been disabled). Even your web application restarts, the client will keep logged in.
To solve this, you can use custom Token cache serialization in MSAL.NET:https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet.
Hope this helps.
I would like to share the resolution to this problem in case if it helps someone in the future. I feel that this is a Microsoft Bug that was introduced during one of their many upgrade process as this code went from working to broken without any change from our end. Here are the steps I took:
While exchanging the code for a token after user authentication, I retrieved and saved Account.HomeAccountId.Identifier, Account.HomeAccountId.ObjectId and TenantId for the account.
I implemented my own version of IAccount.
Instead of calling await app.GetAccountAsync(accountID), I used my implementation of IAccountand initialized it with the data I saved in Step 1.
I used this account to call app.AcquireTokenSilent(microsoftScopes, account).
And that's it! No error was thrown once this was done!

Azure web hook error: The 'code' query parameter provided in the HTTP request did not match the expected value

I have created a c# based web hook in an azure function app, based on Adrian Halls excellent book on github.io
The web hook and app is running successfully when tested in portal.
When i call the webhook from my controller i can see i have the correct parameters and uri. But for some reason my function app never enters my method and give me an error saying:
The 'code' query parameter provided in the HTTP request did not match the expected value
My problem is that i do have my code query parameter in the request.
Basically i just want to trigger the webhook when a new todoitem is inserted in my database.
Anybody know what could be the problem?
Code:
Call from api controller to webhook method in backend
// POST tables/TodoItem
public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
{
TodoItem current = await InsertAsync(item);
Webhook.SendAsync<TodoItem>(new Uri(WebhookUri), current);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
Webhook method in backend
public static async Task<HttpStatusCode> SendAsync<T>(Uri uri, T data)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = uri;
var response = await httpClient.PostAsJsonAsync<T>("",data);
return response.StatusCode;
}
Function in azure
#r "Newtonsoft.Json"
using System;
using System.Net;
using Newtonsoft.Json;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("Webhook triggered");
string jsonContent = await req.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
log.Info($"Created New Todo ({data.Text}, {data.Complete})");
return req.CreateResponse(HttpStatusCode.OK);
}
Had the same issue today. Go to the Manage panel of your Azure Function. There you can copy the "default" key which works
Anders,
There are indeed some issues with the key management UI on the portal and those are being addressed (you can track one that was likely impacting you here
A workaround, at the moment, is to make sure you're using the appropriate key by opening the "keys" panel for the function and selecting the "default" function key, using that as the code.
There is also an API you can use to request the keys directly from the runtime, here's an example of invoking that API to retrieve the keys for a given function:
https://<functionappname>.azurewebsites.net/admin/functions/<functionname>/keys?code=<your admin key>
This must be a bug in azure functions app.
I created a couple of web hook functions more to see if i could hit one of those.
No success same error as before.
But then i went back to my old function and suddenly it worked. I don't know why one of my new web hooks i tested on is a copy of the old one and it is still not working. Maybe an azure functions expert know more about this.

Azure App Service - Some WebAPI methods throw OWIN Exception

I have an Azure Mobile App that has some methods that generate 500 errors but does not record any exceptions in Application Insights and no exceptions are thrown inside my code. I have been able to determine that normal TableController methods work fine, but custom methods do not. Also, I can remote debug the code and watch it finish executing without any exceptions being thrown. It should also be noted that I did not have this problem when this project was a Mobile Service. Here is an example method that fails:
private readonly MobileServiceContext context; //Autofac injection
private readonly IUserHelper userHelper; //Autofac injection
[HttpGet, Route("api/Site/{id}/Users")]
public async Task<HttpResponseMessage> Users(string id)
{
var userId = await userHelper.GetUserIdAsync(User, Request);
var query = context.UserSiteMaps.Include(x => x.User).Where(map => map.SiteId == id);
var auth = query.FirstOrDefault(x => x.UserId == userId && x.IsAdmin);
if (auth != null)
{
return Request.CreateResponse(HttpStatusCode.OK, query.Select(map => map.User));
}
return Request.CreateUnauthorizedResponse();
}
The deepest error log that I have been able to obtain is the detailed error page from IIS:
Module __DynamicModule_Microsoft.Owin.Host.SystemWeb.OwinHttpModule, Microsoft.Owin.Host.SystemWeb, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_9de2321b-e781-4017-8ff3-4acd1e48b129
Notification PreExecuteRequestHandler
Handler ExtensionlessUrlHandler-Integrated-4.0
Error Code
0x00000000
I haven't been able to generate a more detailed error message and I have no idea what Owin is upset about here since other method return requests just fine. Am I doing something I shouldn't?
Any help would be greatly appreciated!
Update : Here is the full error message that I have been able to get.
I have also been able to narrow the cause down a bit. If I replace the query.Select(map => map.User) object in the response with a simple string, it returns that string without complaint. However, if I stringify the response myself and pass that in, I get 500s again. Could it be some serializer setting problem?
The best way to track down the issue is to turn on exception stack traces for you app and to turn on logging on your Mobile App backend.
See Server Side Logging in the Mobile Apps wiki and Enable diagnostics logging for web apps in Azure App Service. You can also remote debug your service to see the exact error, see Remote debugging .NET server SDK.

Call to Action with Authorize(Roles="Customer, Business") returns 500 error for those roles

I have several actions in my MVC site that recently started returning authentication errors when I call them when logged in with accounts that have the authorized roles. An example below.
[HttpGet]
[Authorize(Roles = "Customer, Business")]
public async Task<ActionResult> ShowNotifications(bool unViewedOnly = true) {
var userId = User.Identity.GetUserId<int>();
var notifications = await _notifications.GetByUserIdAsync(userId, unViewedOnly);
return (Request.IsAjaxRequest())
? PartialView("Notifications/NotificationsModal", notifications)
: PartialView("Notifications/_Notifications", notifications);
}
I have not made any changes to the functions or the javascript that calls them in quite some time. I did recently update Microsofts Identity NuGet packages. Has anyone else seen this issue and does anyone have any idea how to fix it other than rolling back the Identity updates. I have no idea which library would have caused the problem. I am far from a security expert.
A typical response:
Key Value
X-Responded-JSON {"status":401,"headers":{"location":"http:\/\/localhost:53033\/Account\/Login?ReturnUrl=%2FShared%2FShowNotifications%3FunViewedOnly%3DTrue%26modal%3DTrue%26X-Requested-With%3DXMLHttpRequest%26_%3D1426264188044"}}

SignalR - Can't create url for HubConnection

I started to work with SignalR on Visual Studio 2012, so far I got the basic hang of it, I guided myself through this example (You can browse the code in the page). I decided I wanted to add a REST service to said example, so I added a basic service to it and it worked.
The next step I wanted to take was to add a communication between the service and SignalR, so according to what the example showed to me, I only needed to create a HubConnection through the url in my project (in this case, the example uses the url http:localhost:4200). You can check the WorkerRoleHubConfiguration class, it has a method that has the next line:
return RoleEnvironment.GetConfigurationSettingValue("GUI_URL");
Where GUI_URL is http:localhost:4200.
In my service class then I just added a method with the following:
var url = RoleEnvironment.GetConfigurationSettingValue("http://localhost:4200");
try
{
HubConnection _connection = new HubConnection(url);
IHubProxy _hub = _connection.CreateProxy("SiteMonitR");
_hub.Invoke("displayResult");
}
catch (Exception ex)
{
error = ex.ToString();
}
But that throws an exception, this one.
I don't undertand why I can get the url in the same way the example does, as I'm doing everything as it's done on the Server class.
The goal I'm trying to achieve is that when an endpoint is accesed and something changes in my system, SignalR notifies to the clients connected to it.
I hope anyone can help me understand what's wrong with my work.
EDIT
I'm adding my ServiceConfiguration.Local.cscfg, my ServiceConfiguration.Cloud.cscfg and ServiceDefinition.csdef files as reference here, I think the problem should be around there but to be honest I got no idea as to why this isn't working.
EDIT 2
I'm getting the following exception at this line var url = RoleEnvironment.GetConfigurationSettingValue("http://localhost:4200");
The exception is:
SEHExcetion occurred. External component has thrown an exception.
The URL is for a GUI - it has to be a web interface for signalr to negotiate the hub connection. In the example, the hub (server) sends updates to connections coming from the configured URL - again a web interface (html page).
The logic to communicate needs to reside in the Server class and be called from the worker role. For example, after making a call to your service in the worker role, then call server.DoSomething("message") to invoke a message to the server. THat code would look something like:
public Class Server
{ ...
public void DoSomething(string message)
{
_hub.Invoke("doSomething", message);
}
...
}
Then in Server.Run() add:
// whenever a DoSomething is called
_hub.On<string>("doSomething", (message) => _hub.Invoke("doSomething", message));
And in SiteMonitRNotificationHub
public class SiteMonitRNotificationHub : Hub
{
...
public void DoSomething(string address)
{
Clients.doingSomething(address);
}
...
}
Finally in the controller script in the web gui:
c.siteMonitorHub
...
.on('doingSomething', function (message) {
c.doSomething(message);
})
and...
this.doSomething= function (message) {
// do something in your web page with message
};

Resources