I have implemented Azure Mobile App and Xamarin.Forms Client application. I want user to login using facebook from Phone and also want to fetch user's profile data. For this I have implemented the additional call/method into API controller in Azure Mobile App. I have followed steps and put the code as per your article but somehow get following error message when I run the Mobile App on localhost or trying to publish
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('') found multiple controllers defined with the same name but differing namespaces, which is not supported. The request for 'Home' has found the following matching controllers:
Microsoft.Azure.Mobile.Server.Controllers.HomeController Microsoft.WindowsAzure.Mobile.Service.Controllers.HomeController
I understand this is related config settings. I have following code in place
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
app.UseWebApi(config);
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
If I remove the default configuration from above then exception message go away but in that case I don't see the app getting hosted properly i.e. it is showing blank page in browser instead of ready page shown once app is hosted properly.
What steps you followed, could you please show the link?
That exception is routing-related and very common, and can be fixed - for example, by use of areas. A lot of manuals are available, for example, here - http://blog.falafel.com/duplicate-controller-names-aspnet-mvc-areas/ .
You have added two different SDKs - one for Azure Mobile Services v1 and the other for Azure App Service Mobile Apps (which can be considered v2). You need to remove the reference to the older one.
Use the appropriate SDK for the service you are using, and delete the other one.
Related
I have a Chrome Extension that needs to authenticate the user. Once authenticated, I will send that user's email to my server running in Docker and then log them in. I am having trouble getting the token. Here is the code:
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
if (chrome.runtime.lastError) {
currentSessionAccessToken=token;
alert(chrome.runtime.lastError.message);
//alert("you need to have a gmail account"); //ubuntu
return;
}
currentSessionAccessToken=token;
var x = new XMLHttpRequest();
x.open('GET', 'https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=' + token);
x.onload = function() {
if (x.readyState=200)
{
var data=this.responseText;
jsonResponse = JSON.parse(data);
photo = jsonResponse.picture;
szName=jsonResponse.name;
email=jsonResponse.email;
x.abort(); //done so get rid of it
send_to_backend(request, sender, sendResponse);
};
}
x.send();
}
The problem is that I am not getting back an access token. The backend (at this time) is also on my laptop (localhost) but in a docker container. I don't have an SSL cert for my localhost and I am wondering if that is the issue? I am never getting a token so I never get to send it with the XMLHttpRequest, and thus I never get a ReadyState=200. Any idea what is wrong?
Did you register your app for Google OAuth API access and designate the oauth field in the manifest?
From the documentation on user auth:
Copy key to your manifest
When you register your application in the Google OAuth console, you'll provide your application's ID, which will be checked during token requests. Therefore it's important to have a consistent application ID during development.
To keep your application ID constant, you need to copy the key in the installed manifest.json to your source manifest. It's not the most graceful task, but here's how it goes:
Go to your user data directory. Example on MacOs: ~/Library/Application\ Support/Google/Chrome/Default/Extensions
List the installed apps and extensions and match your app ID on the apps and extensions management page to the same ID here.
Go to the installed app directory (this will be a version within the app ID). Open the installed manifest.json (pico is a quick way to open the file).
Copy the "key" in the installed manifest.json and paste it into your app's source manifest file.
Get your OAuth2 client ID
You need to register your app in the Google APIs Console to get the client ID:
Login to the Google APIs Console using the same Google account used to upload your app to the Chrome Web Store.
Create a new project by expanding the drop-down menu in the top-left corner and selecting the Create... menu item.
Once created and named, go to the "Services" navigation menu item and turn on any Google services your app needs.
Go to the "API Access" navigation menu item and click on the Create an OAuth 2.0 client ID... blue button.
Enter the requested branding information, select the Installed application type.
Select Chrome Application and enter your application ID (same ID displayed in the apps and extensions management page).
Once you register your app you need to add something like this to your manifest:
"oauth2": {
"client_id": "YOUR_CLIENT_ID",
"scopes": ["scope1", ...]
}
Turns out that in order to get "identity" working you must publish to the Google WebStore. The reason I stayed away from that is that it often takes weeks to get a site reviewed. I have had that experience in the past. I haven't really nailed down the new URL that will be using and wanted to get the system working before I did that. Now that I submitted for Review, I guess I have some time, and will "dummy up" the steps needed (ie authentication) to continue the development work. Thanks Micah for pointing out the manual. This led to me realizing that there is no way to get "identity" working without getting approval from Google.
My api is hosted on Azure as an App Service with an Azure App Gateway in front of that.
I have set the webhosturl in my startup and that is working as when I view the metadata page, i see the links pointing to the correct location. And those links work. However when I navigate to the base url for my api, it redirects me to the app service url.
Here is a snippet of my startup...
SetConfig(new HostConfig
{
WebHostUrl = "https://api-dev.hsawaknow.net/link/",
DefaultRedirectPath = "/metadata",
DebugMode = AppSettings.Get(nameof(HostConfig.DebugMode), false)
});
Please see the links below and see the differences.
https://api-dev.hsawaknow.net/link/metadata
vs
https://api-dev.hsawaknow.net/link/
You will get an https error as I am using a self signed cert, until I get this figured out. I have seen other posts that say to make this change and that it works, but not for me.
Please help!
I have this figured out. There were a couple things that I had to do.
First thing I had to do was setup the forwarded headers middleware to recognize and process the correct headers coming from the Azure Application Gateway.
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHostHeaderName = "X-ORIGINAL-HOST";
options.ForwardedHeaders = ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor;
});
This allowed my site to work with links to the correct pages without setting the WebHostUrl. The only caveat about using the Azure App Gateway is that it uses X-ORIGINAL-HOST instead of the standard X-FORWARDED-HOST.
Next, I had to set the DefaultRedirectPath on the HostConfig dynamically based on settings in appsettings.json. In the case of the Azure App Gateway, my public url was https://api-dev.hsawaknow.net/link/, I had to set the redirect to /link/metadata, instead of just metadata, because of how the host header was getting set in the previous step.
It took a few tries, but this configuration works well, when hosting on Azure App Services fronted with an Azure Application Gateway.
Kudos to the mythz for the quick response, which pointed me in the right direction.
Enable the Request Logger so you can see what requests ServiceStack receives.
Does it work when not specifying a WebHostUrl?
I have a legacy static website that is just plain HTML and simple JavaScript for UI effects. There is no server side code, api, config files or anything in this website - just raw HTML files, CSS, pictures, etc.
The website will not be hosted in Azure. It will be on a local IIS server. If I pull the web site into Visual Studio, the "Configure Azure AD Authentication" wizard shows:
An incompatible authentication configuration was found in this project
().
How can I secure simple HTML files using Azure AD?
The Visual Studio "Configure Azure AD Authentication" wizard is intended for ASP.Net Web Apps and Web APIs.
In your case, what you are building is considered a "Single Page Application" or SPA. Even though you might have multiple pages, this term also applies to client side only web apps with no backend code.
For this, you should follow the Azure AD Javascript Single Page Application sample.
The gist of it is that you should us ADAL.js like shown in this sample's app.js, along the lines of:
// Configure ADAL
window.config = {
instance: 'https://login.microsoftonline.com/',
tenant: '[Enter your tenant here, e.g. contoso.onmicrosoft.com]',
clientId: '[Enter your client_id here, e.g. g075edef-0efa-453b-997b-de1337c29185]',
postLogoutRedirectUri: window.location.origin,
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
};
var authContext = new AuthenticationContext(config);
// Check For & Handle Redirect From AAD After Login
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
$errorMessage.html(authContext.getLoginError());
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
// Check Login Status, Update UI
var user = authContext.getCachedUser();
if (user) {
//Do UI for authenticated user
} else {
//Show UI for unauthenticated user
}
// Register NavBar Click Handlers
$signOutButton.click(function () {
authContext.logOut();
});
$signInButton.click(function () {
authContext.login();
});
Note: There's also a Angular SPA sample.
The solution posted by Saca pointed me in the right direction, but adding the JS to every page was not a valid solution for me. There were thousands of HTML files, lots with no common JS file I could tack that ADAL code into. I would have had to find a way to insert that JS on all those pages.
My first solution was simply creating a normal .NET MVC app with the proper auth configured. Then I simply loaded this legacy content via an iFrame. This worked but was limiting for the users.
As Fei Xue mentioned in another comment, the next solution involved scrapping the iFrame but routing all requests for static files through a controller. Using this as a reference for understanding that: https://weblogs.asp.net/jongalloway/asp-net-mvc-routing-intercepting-file-requests-like-index-html-and-what-it-teaches-about-how-routing-works
The above solutions worked. However, eventually this app ended up as an Azure App Service and I simply turned on authentication at the app service level with just the pure html files.
please kindly help me out with my attempt to implement client side authentication for a xamarin forms aplication i am developing. i have followed every single tutorial on how to integrate Azure active directory into xamarin when using azure mobile services. the error is always thrown at the point of calling loginAsync. on futher investigation using the azure log i found out that the error was coming from the easyauthmodule. please help like i said i have followed every single tutorial on this issue and i have been on it now everyday for the past one week
please find my code below
try
{
AuthenticationContext ac = new AuthenticationContext(authority);
ac.TokenCache.Clear();
AuthenticationResult ar = await ac.AcquireTokenAsync(resource, clientId, new Uri(returnUri), new PlatformParameters(this));
JObject payload = new JObject();
payload["access_token"] = ar.AccessToken;
// DataRepository.DefaultManager.CurrentClient.Logout();
user = await DataRepository.DefaultManager.CurrentClient.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,payload);
}
catch (Exception ex)
{
CreateAndShowDialog(ex.Message, "Authentication failed");
}
EasyAuth is incompatible with Azure Mobile Services. Are you sure you are using the right service moniker?
Make sure you are using the following NuGet for Azure Mobile Apps: https://www.nuget.org/packages/Microsoft.Azure.Mobile.Client/
EasyAuth is only available in Azure App Service. You need to configure the App Service Authentication / Authorization module. Assuming you have already integrated ADAL into your Xamarin app and have an access token from ADAL, your code is pretty close. However, I've found that configuration of AAD for mobile apps is complex. So I wrote a couple of blog posts about it.
Here is the server flow edition: https://shellmonger.com/2016/04/04/30-days-of-zumo-v2-azure-mobile-apps-day-3-azure-ad-authentication/
Here is the client flow edition: https://shellmonger.com/2016/04/06/30-days-of-zumo-v2-azure-mobile-apps-day-4-adal-integration/
Both are using Cordova as a mobile client, but the configuration of the service is identical. The client details (aside from the obvious language differences) are similar as well.
I have an ASP.NET Web API service that runs on a web server with Windows Authentication enabled.
I have a client site built on MVC4 that runs in a different site on the same web server that uses the HttpClient to pull data from the service. This client site runs with identity impersonation enabled and also uses windows authentication.
The web server is Windows Server 2008 R2 with IIS 7.5.
The challenge I am having is getting the HttpClient to pass the current windows user as part of its authentication process. I have configured the HttpClient in this manner:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
My understanding is that running the site with identity impersonation enabled and then building the client in this manner should result in the client authenticating to the service using the impersonated identity of the currently logged in user.
This is not happening. In fact, the client doesn't seem to be authenticating at all.
The service is configured to use windows authentication and this seems to work perfectly. I can go to http://server/api/shippers in my web browser and be prompted for windows authentication, once entered I receive the data requested.
In the IIS logs I see the API requests being received with no authentication and receiving a 401 challenge response.
Documentation on this one seems to be sparse.
I need some insight into what could be wrong or another way to use windows authentication with this application.
Thank You,
Craig
I have investigated the source code of HttpClientHandler (the latest version I was able to get my hands on) and this is what can be found in SendAsync method:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Now if you check within your code the value of SecurityContext.IsWindowsIdentityFlowSuppressed() you will most probably get true. In result the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user).
There are two possible ways out of this. If you have access to yours server aspnet_config.config, you should set following settings (setting those in web.config seems to have no effect):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
If you can't change the aspnet_config.config you will have to create your own HttpClientHandler to support this scenario.
UPDATE REGARDING THE USAGE OF FQDN
The issue you have hit here is a feature in Windows that is designed to protect against "reflection attacks". To work around this you need to whitelist the domain you are trying to access on the machine that is trying to access the server. Follow below steps:
Go to Start --> Run --> regedit
Locate HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 registry key.
Right-click on it, choose New and then Multi-String Value.
Type BackConnectionHostNames (ENTER).
Right-click just created value and choose Modify.
Put the host name(s) for the site(s) that are on the local computer in the value box and click OK (each host name/FQDN needs to be on it's own line, no wildcards, the name must be exact match).
Save everything and restart the machine
You can read full KB article regarding the issue here.
I was also having this same problem. Thanks to the research done by #tpeczek, I developed the following solution: instead of using the HttpClient (which creates threads and sends requests async,) I used the WebClient class which issues requests on the same thread. Doing so enables me to pass on the user's identity to WebAPI from another ASP.NET application.
The obvious downside is that this will not work async.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
The reason why this is not working is because you need double hop authentication.
The first hop is the web server, getting impersonation with Windows authentication to work there is no problem. But when using HttpClient or WebClient to authenticate you to another server, the web server needs to run on an account that has permission to do the necessary delegation.
See the following for more details:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Fix using the "setspn" command:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(You will need sufficient access rights to perform these operations.)
Just consider what would happen if any server was allowed to forward your credentials as it pleases... To avoid this security issue, the domain controller needs to know which accounts are allowed to perform the delegation.
To impersonate the original (authenticated) user, use the following configuration in the Web.config file:
<authentication mode="Windows" />
<identity impersonate="true" />
With this configuration, ASP.NET always impersonates the authenticated user, and all resource access is performed using the authenticated user's security context.