I have a very basic Single Sign On app built on VS 2015 using MVC and Web Forms. It is supposed to be a simple proof of concept and is based on some code found here and here which are essentially the same things. I've finally gotten it all converted to use .Net 4.5 but when running it on my local server it throws a 404 with no debug information.
The 404 itself wasn't initially a surprise as I was supposed to be able to change the url to one of the secure pages (for instance /WebSecApp1) which would redirect me back to the signon page but no matter what I put as the url I get the 404.
I've also tried changing the urls in the code so that they contain the port numbers for the localhost but that doesn't work either.
It was suggested to me that the RouteConfig.cs could be the culprit but I don't see how that could be since I'm calling a single page with no parameters.
I know this is kind of lite on details but does anyone have any suggestions?
Yes this looks like a routing issue as you also thought it to be. Routing is essential for web api too .Pls see https://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection. Does your api request look like this
GET http://localhost:34701/api/products/1?version=1.5&details=1
You do have to mention the port in the request.
While the routing that Arathy mentioned above was partially to blame, the real problem turned out to be relatively simple. In my case simply selecting Properties->Web for each of offending pages and setting "Override application root URL" to checked fixed the whole problem.
I am currently trying to use Swagger UI to interact with my OData enabled Web API.
Versions for the bits and bobs are as follows:
.Net 4.5.2,
Web API 2.2,
NuGet package Microsoft.AspNet.OData 6.0.0 for OData 4.0 endpoints,
Swashbuckle 5.3.2,
Swashbuckle.Core 5.3.2,
Swashbuckle.OData 3.0.0.
My application has a SwaggerConfig.cs and a WebApiConfig.cs as expected. And I have followed many of the instructions in this article, this article and a few others on the web.
When I launch my API through Visual Studio, I initially get a new browser tab with the local host URL. If I append the word "/swagger" (no quotes) at the end of that URL, I get a nice Swagger UI in the same tab. This tab shows the name of of my API but not much else. I don't see any of the individual URL interfaces for the Actions exposed by my ODataController derived Controller. Moreover, underneath the displayed name of the URL, all I have is [ base url: , api version: v1 ] where "v1" is coming in from SwaggerConfig.cs (same with the API name).
I know I can talk to the API from Swagger UI because I can arrive at the "Get" action of the Controller by modifying the displayed URL appropriately. The actions created were the standard Get, Put, Post, Patch, Delete ones. But I cannot see any of these in the Swagger UI.
This is because I am new to this and obviously have missed out or miscoded stuff to link everything correctly. I have made very little change to SwaggerConfig.cs and left most of the things as they were when it was created. The significant change I have made was adding this line:
c.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, c, GlobalConfiguration.Configuration));
Based on this info, if anyone can guide me in the direction of what pieces could be missing or developed incorrectly, I would greatly appreciate it. If you had any further questions for me, I would be happy to respond in the comments.
It turns out that because I had been recreating the application, Entity Framework was no longer creating the database for me since it was trying to use a name it had already recently used (a known EF bug). So I refractored the name of the database to something else. After this, things were plain sailing.
This negates my comment: "I know I can talk to the API from Swagger UI because I can arrive at the "Get" action of the Controller by modifying the displayed URL appropriately. The actions created were the standard Get, Put, Post, Patch, Delete ones. But I cannot see any of these in the Swagger UI."
Clearly this was me not understanding the issue clearly. But by going through the steps again one by one, I was able to find the problem and come to the desired place :)
I have an API endpoint that shall export some data as an Excel xlsx file. The URL I try to get is like this: localhost/service/resources/123.xlsx (because I think using this extension is a nice way to do it).
But it always gives me a 404 Not Found.
The controller has a route prefix: [RoutePrefix("resources")] and the route attribute on the action is: [Route("{id:int}.xlsx")]
Even when I changed the route to [Route("123.xlsx")] it would not find it. I also tried a regex constraint [Route("{key:regex(\\d+\\.xlsx}")] with no luck.
I suspect it may be grabbed by static file handler, but the web config already has the setting
<modules runAllManagedModulesForAllRequests="true"/>
I believe this should work, because it worked for a very similar route in a previous project, using the old routing setup and not attribute routing. That project was not hosted using OWIN, but is that the problem here?
How can I make this work? I need this route for both GET and PUT.
Btw, localhost/service/resources/123 will return a different representation of the resource.
Info: Hosted in IIS 7.5 using OWIN 3.0 and Web API 5.2.2
I found out here that adding this line of code
app.UseStageMarker(PipelineStage.MapHandler);
after
app.UseWebApi()
in the Startup class made it work!
(I don't actually understand exactly what it does, but hey, who cares)
I have added ASP.NET WEB API Help Documentation to an existing Project.
Its working to an extent, as I can browse to localhost/help and I can see the Account Controller API that comes with ASP.NET MVC.
I can also see one other API controller that I have built, and its Methods.
However I have since added another 5 API controllers, and WEB API Help is failing to generate the documentation for these.
I have enabled these settings in APP_Start/HelpPageConfig of API help:
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
config.SetActualRequestType(typeof(string), "Values", "Get");
config.SetActualResponseType(typeof(string), "Values", "Post");
And I also have enabled XML Documentation Output in the Project Properties/Build Section as mentioned in this post:
Creating Web API Help Pages
So how can I get the documentation to Automatically generate for the other 5 controllers?
Any ideas are vary welcome, and thank you in advance.
Just figured it out, so ill post an answer - hopefully it helps someone else.
I simply needed to register the Controller routes specifically in the App_Start\WebAPIConfig.cs file, and the Help Generation has resumed generating.
I've created a new MVC5 Web Application, and when I try to login with Google or Facebook, the ExternalLoginCallback Action in the AccountController is called, but GetExternalLoginInfoAsync() always returns null:
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
Because it's always null, it just redirects back to the login page and the process starts over. How can I fix this?
To get OWIN Google login to work properly on a standard Visual Studio 2013, ASP.Net MVC5 site, I had to:
Setup a Google OpenId account at https://console.developers.google.com/project
Set the callback URL there to blah/signin-google. Important notes on things you don't need to do:
You don't need to use HTTPS for Google to redirect back; you can even redirect back to plain http://localhost, no problem.
You don't need to setup anything for the redirect URL - no routes, Controller Actions or special permissions in Web.Config. The redirect URL is always /signin-google and OWIN handles this behind the scenes for you.
As an example, if your site was me.com, you might have these 3 callback URLs in the Google Developer Console:
http://localhost:53859/signin-google
http://test.me.com/signin-google
https://me.com/signin-google
The first one including whatever port number VS gave you for your project.
Enable the Google+ API. This is one hidden b**** of a gotcha and is the root cause of the problem in the question here - if you don't do this, it's easy to miss that the Request to /account/ExternalLoginCallback includes &error=access_denied, and that's because Google said no to a permissions request OWIN made for the user's Google+ basic profile. I can't tell whose fault this is, Google's or Microsoft's.
To enable the Google+ API in the Developers Console, click APIs on the left, hunt for Google+, click that and hit Enable. Yes you really do need to do that. You're hosed if you don't do that.
Add the ClientId and ClientSecret Google gave you in the Developers Console to Startup.Auth, but improve the code in the process to explicitly use OAuth2, and explicitly ask for the user's email address:
var google = new GoogleOAuth2AuthenticationOptions()
{
ClientId = "123abc.apps.googleusercontent.com",
ClientSecret = "456xyz",
Provider = new GoogleOAuth2AuthenticationProvider()
};
google.Scope.Add("email");
app.UseGoogleAuthentication(google);
That's it. That finally got it working.
Just want to reiterate one more time, there are a LOT of answers about this and issues like it where OWIN/Google isn't working, and nearly all of them are wrong for the current VS2013/MVC5/OWIN template.
You don't need to modify Web.Config at all.
You don't need to create any special Routes whatsoever.
You should not attempt to point /signin-google to a different place, or use a different callback URL, and you definitely shouldn't attempt to tie it directly to /account/externallogincallback or externalloginconfirmation, because those are both separate from /signin-google and necessary steps in the OWIN/Google process.
OK, I found out why it's null. You have to enable Google + API in the Google console. Also make sure the secret key is not concatenated with a space at the end after you paste it to your code. Why can't they return a normal error? I don't know.
It seems that Nuget package Microsoft.Owin.Security.Facebook version 3.0.1 no longer works with Facebook Login.
Update this package to the pre-release 3.1.0 version, you can use the following:
Install-Package Microsoft.Owin.Security.Facebook -Pre
As others correctly mentioned, most of the time that's because you do not have permission to the Google+ API so here is how to get permission for a project in Google API Manager to Google+ API
Step 1. Select You Project from the top combobox and go to Dashboard > Enable API
Step 2: Search for Google plus and select it
Step 3: Enable it!
if you return to dashboard for that project you can see the list of enabled API's for that project at the bottom
I got it to work by simply updating all the nugget package in the application and it worked.
I know it's silly, but after a long struggle, restarting IIS solved the issue for me.
This solved my problem:
Enable the Google+ API. This is a gotcha and is the root cause of the problem in the question here - if you don't do this, it's easy to miss that the Request to /account/ExternalLoginCallback includes &error=access_denied, and that's because Google said no to a permissions request OWIN made for the user's Google+ basic profile. I can't tell whose fault this is, Google's or Microsoft's.
To enable the Google+ API in the Developers Console, click APIs on the left, hunt for Google+, click that and hit Enable.
I did the following to get it working.
Logon to the developer portal, locate your application and do the following.
App details > App centered Listed Platforms > Select Yes for website
I ran in to this issue today and it turned out that I defined the remote cookie after I assigned the providers.
Make sure you place...
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
before...
app.UseFacebookAuthentication(
appId: "",
appSecret: "");
I wanted to contribute to this one also. I just recently got this working. I had the problem with the GetExternalLoginInfoAsync returning null but only in production.
After a lot of searching I finally found my answer it was simply a problem with my database. In production I had set the wrong connection string so it would not connect properly but it was basically silent about it. The only thing that happened was GetExternallLoginInfoAsync returned null. So check you database connection string if this happens!
Also on a sidenote, the only thing that was needed to get this working was:
Set up a project in the Google console
Enable Google+ API
Copy your client id and client secret to the Startup.Auth.cs file.
You do not have to enable HTTPS, you do not have to create custom routes. But make sure your database is working properly!
For those who are experiencing this problem for Web Api. Other solutions doesnt help AuthenticationManager.GetExternalLoginInfoAsync(); returns always null even google plus api is enabled.
use this custom function to get logininfo. obviously Microsoft has a bug for GetExternalLoginInfoAsync when requesting over web api.
private async Task<ExternalLoginInfo> AuthenticationManager_GetExternalLoginInfoAsync_WithExternalBearer()
{
ExternalLoginInfo loginInfo = null;
var result = await Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
if (result != null && result.Identity != null)
{
var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
if (idClaim != null)
{
loginInfo = new ExternalLoginInfo()
{
DefaultUserName = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", ""),
Login = new UserLoginInfo(idClaim.Issuer, idClaim.Value)
};
}
}
return loginInfo;
}
Although the answers above are all good, in my instance none of these worked - I'd checked and double checked the Google settings and agree with Chris Moschini that there's a lot of misleading info.
For me it was a 'doh moment when I realised that my Out of Process state service was not started! No errors (as a login was the first thing I was attempting after a reboot where the state service is set to manual start-up on the machine) just a Null from GetExternalLoginInfoAsync
Hope this helps someone else out.
After much searching and head scratching as well as following numerous red herring answers here on Stackoverflow I eventually went through all my options on my Google dev console and discovered a little blue [Enable] button on the Google+API overview page. I clicked this and hey presto it worked. Forget all the baloney you read about callback url and route configs, OWIN overrides the google default /signin-google redirect uri in any case and sends you back to ExternalLoginCallback. Just stick with the default implementation all will be good so long as you enable your Google+API.
It is true that you are going to need the Google plus Enabled. The big thing for me was the project URL. Open the properties window (View -> Properties Window) in VS and then right click the project and select properties. In small properties window copy your SSL URL, and then in the larger properties window select the Web tab and paste that URL in the Project URL.
Fixed the issue for me.
See in greater detail: https://learn.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
For me I was migrating and old but working .NET 4.6.1 MVC website to core 1.1. Work stopped before I could get it working, when I picked it back up, I was then migrating to 2.x.
My problem was that the callback from Google was met with a 404 from my site. I thought it was supposed to hit AccountController.ExternalLoginCallback so I added a [Route(...)] to it and sure enough, Google's callback hit the action.
This then hit the null returned in this line (what kind of maniac returns a null?)
var externalLoginInfo = await this.SignInManager.GetExternalLoginInfoAsync();
I reverse engineered it to find under the hood its ultimately getting the handler for ExternalScheme which for my was the cookies handler!
It all seemed wrong and I felt somehow that the middleware was supposed to just intercept the callback URI so I removed my [Route(...)] and the 404 problem came back.
I then found that I need to add this during startup.
applicationBuilder.UseAuthentication();
This solves the 404 but gives another issue.
No authenticationScheme was specified, and there was no DefaultSignInScheme found.
By adding a default scheme here, I resolve the error above.
serviceCollection.AddAuthentication(IdentityConstants.ExternalScheme)
.AddGoogle(googleOptions => Configuration.Bind("OAuth2:Providers:Google", googleOptions))
.AddExternalCookie();
Now AccountController.ExternalLoginCallback is invoked again by some magic but I am back to the null return value.
I added this code above the offending line, which is essentially what is happening under the hood (looking at Microsoft's code on GitHub). Interestingly, h is of type CookieAuthenticationHandler and a has all my claims and information from Google inside!
var authHandler = this.HttpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
var h = await authHandler.GetHandlerAsync(this.HttpContext, IdentityConstants.ExternalScheme);
var a = await h.AuthenticateAsync();
var externalLoginInfo = await this.SignInManager.GetExternalLoginInfoAsync();
Digging into GitHub and copy pasting internal code its running into my controller, I can see that it's failing to find ClaimTypes.NameIdentifier in my claims, this is the ProviderKey used later.
Hmm....
Concerned I was using old 1.x AccountController code with newer 2.x identity bits I did find some samples that still use this stuff, and some samples that use Razor Pages for it all, too. I'll continue with what I have.
So I'm next going to investigate mapping additional Google user JSON payload items into the claims. I think if my Google account ID (numeric, I guess) was mapped then everything would work.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/additional-claims?view=aspnetcore-2.2
Final Fix
I finally resolved the issue by adding a "claim action" to pull my Google identifier out of the JSON coming back from Google!
serviceCollection.AddAuthentication(IdentityConstants.ExternalScheme)
.AddGoogle(googleOptions =>
{
Configuration.Bind("OAuth2:Providers:Google", googleOptions);
googleOptions.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub", "string");
})
.AddExternalCookie();
The sub field contains what eventually ends up in the nameidentifier claim and then into the ProviderKey that the AccountController wants.
In my case the solution was update Nuget package Microsoft.Owin.Security and Microsoft.Owin.Security.Google
All of the other answers didn't solve this for me, so if your in the same boat then make sure your registration controller action has the RequireHttps attribute:
// GET: /Account/LoginRegister
[AllowAnonymous]
[RequireHttps]
public ActionResult LoginRegister()
{
return View(new RegisterLoginViewModel());
}