I've been looking for a way to integrate the Azure ACS home realm discovery page into our ASP.Net MVC5 app login page rather than use the default one hosted on ACS itself.
What I want is something like what is suggested here:
http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/#comment-126567
I’m building an MVC 5.1 on .Net 4.5.1 Azure Web Role which needs to authenticate users from multiple corporate identity providers – some with AAD, some with ADFS – and the list will grow over time. Initially this has been simple enough to set-up with Azure ACS as the federation provider. It presents a home realm discovery (HRD) page and the flow works.
Things become complicated when I try and follow the instructions for adding the HRD into my login page directly (following the instructions given in the ACS application integration pages using the Home Realm Discovery Metadata Feed and some example HTML + JS). I am able to present the HRD buttons as I would like however I am struggling with wiring the sign in process into the OWIN WsFederation setup, which in my application currently only knows about the ACS WsFederationMetadataUrl.
I’ve currently got this in my ConfigureAuth method:
app.UseWsFederationAuthentication(wsFederationOptions: new WsFederationAuthenticationOptions()
{
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = ".com";
return Task.FromResult(0);
}
},
MetadataAddress = CloudConfigurationManager.GetSetting("Authentication.WsFederationMetadataUrl"),
Wtrealm = CloudConfigurationManager.GetSetting("Authentication.Realm"),
AuthenticationMode = AuthenticationMode.Passive,
});
Where .com is the domain of one of the IdPs in my ACS which happens to be our own AAD. But this doesn’t work. Can this scenario be made to work? I found one relevant stack post which talks about skipping the home realm discovery with Ws-Federation OWIN Middleware and allows for the Whr parameter to be set via a user action but so far I haven’t been able to get it to work with the Whr hard coded.
Skipping home realm discovery with Ws-Federation OWIN Middleware
I’ve changed the buttons from the example HTML+JS so that they post to the /Account/ExternalLogin controller action and get into the OWIN pipeline that way:
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string homeRealm, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, homeRealm, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
rather than use a cookie and navigate to the login page at the chosen home realm (as the boiler plate HTML+JS from the ACS does) :
// Sets a cookie to remember the chosen identity provider and navigates to it.
function IdentityProviderButtonClicked() {
SetCookie(this.getAttribute("name"));
window.location = this.getAttribute("id");
return false;
}
Related
I have an angular application that uses asp.net core Web API. I need to add external authentication for google and facebook sign in so that my web and mobile application can use this web api to authenticate users. I have gone through the documents and tutorials provided for this
like
this one and also this
but none of them are helping me because my web application is on Angular 5.
My actual problem is how to get URL that redirects me to Google or facebook sign in page.
Mostly all of them use scafolded Asp.net mvc application where there is a controller: AccountController with Methods ExternalLogins
GET /api/Account/ExternalLogins?returnUrl=%2F&generateState=true
and they say the response will be a json like this:
[{"name":"Facebook",
"url":"/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1",
"state":"QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1"}]
This JSON is exactly what I need because i need a URL like that to open facebook or google login page. The links above use scaffolded projects and every code written there already that works great for Asp.net web application. But in my case, I have started the web api from scratch and my web application is Angular 5. Also if I tried a scafolded web api project but I could not find any controller called AccountController, nor any Method called ExternalLogins to get the JSON that contains provider name and login page url. It looks like the way its done has been changed in .net core 2.
The first link above has shown two ways to include GoogleAuthentication in startup class file for Asp.net core 1 and Asp.net Core 2. So there must be something I am missing.
I am using Asp.net core 2.
I have already got Api keys and secrets and added to startup class file.
I hope I am clear enough.
Please tell me if I need to elaborate more.
Please help. Thanks.
You need to create Challenge for Google Authentication in one of your Controller in API and redirect from angular app to that API Controller.
Controller Method
[HttpGet("ExternalLogin")]
public IActionResult ExternalLogin(string provider)
{
if (provider == null)
{
return null;
}
var properties = new AuthenticationProperties { RedirectUri = "www.mydomain.com/api/auth/callback" };
var response = Challenge(properties, provider);
return response;
}
component method called on click of google sign in button
externalLogin(provider:string){
this.document.location.href = this.baseAPIUrl + 'auth/externallogin?provider='+provider;
}
My applications needs to support Google+ login only. No internal users in a database or anything extra. I was able to configure the stuff so that the following action is hit by Google with identity information:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ExternalLoginInfo loginInfo = await HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// what do I do here ?????????
}
My understanding is that I need to somehow store claims returned by Google into a session cookie but I don't know what to do. Should I convert ExternalLoginInfo into IPrincipal first? How do I have the principal instantiated on the subsequent requests?
My initialization code is:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// bla-bla-bla
app.UseGoogleAuthentication(googleAuthOptions);
If you don't want any of the Identity tables, I don't think Identity is a good fit for you. It persists the claims and logins in the database, and I don't think there's much you can do about that.
In this one narrow scenario, it is actually probably better if your roll-your-own. Just follow the Google API docs and implement their login manually. You're essentially outsourcing all the authentication at this point, so there aren't a whole lot of security implications from not relying on a more standard authentication framework like Identity. You may even be able to find a Nuget package that just does Google Auth.
You don't need to do anything.
They authenticated on the client side (which gave them a cookie), then
You set up Owin middleware correctly for google on your backend.
This has taken care of everything.
I believe that the Owin middleware does not go and automatically register users into a database for you.
Depending on the scopes you asked for on the front end, it will be able to give you some details of the user which are in the principle. (MS converts them to Claims for you).
For my company, I have to make a POC to check if we can use wsFederation authentication for our project, which has a MVC app, some webapi controllers, and some signalR hubs, all in differents projects.
We'd also like to use the OWIN authentication middleware in both the client apps and the Identity provider app.
I use Thinktecture Identity Server v2 as the Identity provider for a start (but we'll have to develop ou own at some point).
For the MVC app, it's pretty straight forward and it works fine, using a SAML2 token.
But now things get a bit more complicated as I'd like an authenticated user on the web app to be able to call a controller method from the web api app (which is different of the MVC one, remember), using ajax calls.
I've read many things about delegating and actAs tokens, but I'm a bit lost and don't where or how to start this part. Also, i can't find anything about delegation using OWIN authentication.
So my first question is : is it possible to achieve this ?
And then : could someone point me in the right direction?
I followed Vittorio Bertocci's instructions when I was working on it.
http://www.cloudidentity.com/blog/2013/01/09/using-the-jwt-handler-for-implementing-poor-man-s-delegation-actas/
A couple of notes about it, where it says JWTSecurityTokenHandler, it is now JwtSecurityTokenHandler. It is a small typo, but it is a good way to loose 15 minutes if you are not aware of it.
I was also not able to use the X509 FindByThumbprint section either. I think that I did not have my local certificate registered properly. Once I am at work tomorrow I will post what I had to change in order to get it to work.
Dominick Baier (http://leastprivilege.com/) also does a course on pluralsight called WebApi v2 Security that does a great job talking about how to inject into the security pipeline and setting up the web api project to handle this.
As another option, you could replace the TokenValidationHandler class that Vittorio uses with the Microsoft.Owin.Security.Jwt package and implement the following code in the Startup.cs file.
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { ConfigurationSettings.AppSettings["ida:Realm"] },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(
ConfigurationSettings.AppSettings["ida:ValidIssuer"],
ConfigurationSettings.AppSettings["ida:SymmetricKey"])
},
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = context =>
{
var identity = context.Ticket.Identity;
return System.Threading.Tasks.Task.FromResult<object>(null);
}
}
});
Hi I am trying to get a hang of how the new authentication mechanism works in MVC5 in the SPA template and I seem to be left confused.My end goal is to create an API that will be exposed to a SPA , iOS , Android and Windows Phone clients
Here is what I understand:
I understand that somehow at startup the class decorated with:
[assembly: OwinStartup(typeof(WebApplication1.Startup))]
is magicly calling ConfigureAuth method:
Inside this method I have 3 lines of code and inside the startup class constructor I have initialized the OAuth authentication options:
static Startup(){
PublicClientId = "self";
UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
OAuthOptions = new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
}
The first two lines in ConfigureAuth seem to set my application and external application to use cookies for storing authentication state, while the third seems to state that it is using bearer tokens for my application.
From what limited knowledge I have so far about mobile devices native apps do not understand cookies and I should use tokens for authentication.
If that is the case shouldn't the externalSignIn be set to Bearer tokes instead of external cookie?
While debugging I also noticed that in the OAuthProvider the authentication type is actually set to bearrer tokens.If that is the case what does this line of code actualy do:
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Some clarification to how this works would be grattely appreciated I could only find information online that shows me how tu use external logins.
It seems to me that the MVC 5 SPA Template is a demonstration of what is possible more than a commitment to a particular best practice.
I have found that removing the line app.UseCookieAuthentication(new CookieAuthenticationOptions()); has no effect on the SPA at all because, as is typical with SPAs, all HTML needed is retrieved anonymously and all authentication is, thereafter, done on any subsequent requests for data. In this case data would be retrieved from WebAPI endpoints and protected with Bearer Tokens.
I don't know why it has been done this way. There are a number of other areas like this where two different concerns are a bit muddled. for example the traditional Global.asax MVC Application_Start is still in place but the newer OWIN Startup mechanism is also present. There is no reason why everything in Application_Start (Filter / Route / Bundle registration, etc.) couldn't have been handled in OWIN Startup.
There are other issues too. If you turn on External Auth (e.g. with Google) and then reduce the AccessTokenExpireTimeSpan, you'll find that when the Token has expired your SPA presents a 'Authorization has been denied for this request.' message. In other words, there is no mechanism in place for Token refreshes. This is not immediately apparent out of the box because the Access Token timeout is set to 14 days, which is rather insecure when considering Cross-Site Request Forgery attacks and the like. Furthermore, there is no enforcement of a transport security mechanism, such as SSL. Tokens are not inherently secure and need to be secured in transport to prevent CRSF attacks and data being extracted en route.
So, MVC 5 SPA is good as a demo, I think, but I wouldn't use it in production. It shows what the new OWIN Middleware can do but it is no substitute for a comprehensive knowledge of Token-based security.
I have a ServiceStack project running an API at api.mydomain.com. An admin project in the same solution is hosted at admin.mydomain.com. Login/Logout is already handled by the admin application, but I want to make sure the user is authenticated (and sometimes check permissions as well) on my api calls. I'm using forms authentication across projects so the auth cookie is available to my api project.
Here's my web.config authentication tag in the api project:
<authentication mode="Forms">
<forms protection="All" loginUrl="home/denied" slidingExpiration="true" timeout="60" defaultUrl="home/denied" path="/" domain="mydomain.com" name=".myAuth"></forms>
</authentication>
Based on this Authentication and authorization post, I added an [Authenticate] attribute to a service method, expecting it to pass/fail based on the value of IsAuthenticated. However, it redirects to 'home/denied' everytime, regardless of whether the auth cookie is present. (I confirmed this by subclassing AuthenticateAttribute and examining the OriginalRequest... The cookie set when I logged in using the admin app is present and req.OriginalRequest.IsAuthenticated is true.)
Why is my request being redirected, and how do I properly utilize the existing auth credential set in the admin app?
EDIT: Here's the solution I came up with. It simply requires an IPrincipal Identity to pass authentication.
public class AuthenticateAspNetAttribute : RequestFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
SessionFeature.AddSessionIdToRequestFilter(req, res, null); //Required to get req.GetSessionId()
using (var cache = req.GetCacheClient())
{
var sessionId = req.GetSessionId();
var session = sessionId != null ? cache.GetSession(sessionId) : null;
var originalRequest = (System.Web.HttpRequest) req.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
if (!identity.IsAuthenticated)
AuthProvider.HandleFailedAuth(new BasicAuthProvider(), session, req, res);
}
}
}
On the Authentication and autorization post you reference it reads:
ServiceStack's Authentication, Caching and Session providers are
completely new, clean, dependency-free testable APIs that doesn't rely
on and is devoid of ASP.NET's existing membership, caching or session
provider models.
Meaning it's completely separate and has nothing to do with the ASP.NET's existing Authentication providers. i.e. The client needs to make an explicit call to the /auth service to authenticate with ServiceStack web services.
See the SocialBootstrapApi example demo project for an example of an MVC Website that uses and shares ServiceStack's Authentication providers between MVC Controllers and ServiceStack web services.