I have an MVC web site that I’d like to modify so instead of using forms to log the user in, it picks up their windows id then passes this to a local Active Directory using LDAP.
However, when I change IIS from Anonymous to Windows Authentication and change the code in Start.Auth.cs with the following (where LoginUser is the script that picks up the user and connects to AD)…
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/LoginUser")
});
…it causes a querystring is too long browser error …
http://localhost:80/UserAuthentication/Account/LoginUser?ReturnUrl=%2FUserAuthentication%2FAccount%2FLoginUser%3FReturnUrl%3D%252FUserAuthentication%252FAccount%252FLoginUser%253FReturnUrl%253D%25252FUserAuthentication%25252FAccount%25252FLoginUser%25253FReturnUrl%25253D%2525252FUserAuthentication%2525252FAccount%2525252FLoginUser%2525253FReturnUrl%2525253D%252525252FUserAuthentication%252525252FAccount%252525252FLoginUser%252525253FReturnUrl%252525253D% (etc)
I have placed [AllowAnonymous] above the LoginUser script so I’m really not sure why it won’t go into it (as this what looks like is happening again and again).
Any help would really really be most appreciated.
Updated to include LoginUser script:
I've included a stripped down version of it that just logs a user in.
[AllowAnonymous]
public async Task<ActionResult> LoginUser()
{
var status = await SignInManager.PasswordSignInAsync("ATHORNE", "Something123!", false, false);
return View();
}
I am using PasswordSignInAsync with a constant password because there would not be a password for Windows Authentication. If there is a better way, please let me know!
The View is the blank default view at the moment.
Typically I would override OnActionExcuting(ActionExecutingContext filterContext)
You can do this in a base Controller class or in an Action Filter
If IIS is set to deny Anonymous Access and enable Windows authentication then within the filter context you can access the users' identity via HttpContext.User.Identity.Name;
To be a bit safer you could write some code like
var user = (filterContext.HttpContext.User.Identity != null
&& filterContext.HttpContext.User.Identity.IsAuthenticated) ?
HttpContext.User.Identity.Name : null;
If username is not null then you have the currently logged on authenticated user, in the form domain\username. You can query Active Directory based on this to get any other information you need.
Related
I am using Identity Server 4 for authentication for both a website and WPF application. On the website, I want users to have the ability to check a Remember Me box when signing in, but I don't want that for the WPF application. I have the logic to disable that checkbox on the front end, but am having trouble in my controller. I have this function
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
LoginViewModel _vm;
_vm = await BuildLoginViewModelAsync(returnUrl);
//if(Client_id == "wpf") <- this is what I need help with
//{
// _vm.AllowRememberMe = false;
//}
return View(_vm);
}
This controller contains
private readonly IIdentityServerInteractionService mInteraction;
private readonly IClientStore mClientStore;
private readonly IAuthenticationSchemeProvider mSchemeProvider;
private readonly IEventService mEvents;
Any help would be appreciated
You can get the client id from the AuthorizationRequest returned from the IIdentityServerInteractionService as follows using your code snipet:
var context = await mInteraction.GetAuthorizationContextAsync(returnUrl);
_vm.AllowedRememberMe = context.ClientId != "wpf";
However, you would be better off placing this logic in your BuildLoginViewModelAsync method where the view model is constructed rather than setting the property after construction.
I don't believe the client_id is directly available from any IS4 constructs in the Login method. However, depending on your OIDC flow, it's likely that your client_id was passed as part of the "returnUrl" parameter. Look at your return URL and see if it's there.
For example, I have a spa website connecting to IS4 that shows a returnURL of:
https://localhost:8080/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DspaClient%26redirect_uri%3Dhttps...(long url continues)
You can see that it contains the "client_id" parameter with a value of "spaClient". Simply parse the returnUrl using your code of choice (e.g. RegEx) and extract the client_id from there. I don't have any WPF experience, so it may behave differently and not pass this parameter.
I have recently developed an ASP.net MVC web application which uses Azure B2C to authenticate users.
I have been asked to enable the Reset Password User flow to enable users to reset via self-service.
I created the user flow within the portal (using the correct identity provider and setting Reset password using email address) and added the code from the microsoft example here however every time I click reset password, it just directs me back to the login screen and it never reaches the reset password page.
When I click the forgot password link the method below is called , it steps through the code fine, but then loads the login page.
Reset Password code
public void ResetPassword(string redirectUrl)
{
// Let the middleware know you are trying to use the reset password policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Startup.PasswordResetPolicyId);
// Set the page to redirect to after changing passwords
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
The policy ID is correct in both azure and in the code as I step through and the values are all pulling through correctly (see below):
Policy ID string (as used above)
public static string PasswordResetPolicyId = ConfigurationManager.AppSettings["ida:ResetPasswordPolicyId"];
In Web.config where the policy is defined
<add key="ida:ResetPasswordPolicyId" value="B2C_1_UserApp_ResetPassword" />
I have provided all the code samples I have added for the reset function to work, the rest of the code is all included in the Microsoft Web App example.
Has anyone else experienced something similar? As I said previously, when you click the forgot password link it does exactly as it should and goes to the correct controller/method, but then goes back to the login screen.
Searching through my code, I found that the line
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(PasswordResetPolicyId));
was missing from ConfigureAuth. Once added this has fixed the issue.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager()
});
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
/////////////////
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(PasswordResetPolicyId));
}
We have a legacy system which is built on ASP.NET Mvc 4, now we would like to support Signal Sign On via Azure Active Directory for current users as well as new users. Since we have managed our own authentication workflow, ASP.NET Identity definitely does not fit in our case.
I have managed to build a demo which is working on OWIN OpenIdConnect middleware passive mode without using ASP.NET Identity. The below code works correctly:
app.SetDefaultSignInAsAuthenticationType("ExternalCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = AuthenticationMode.Passive,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
ClientId = ClientId,
Authority = Authority
// More code
});
And in ExternalLoginCallback action:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authManager = Request.GetOwinContext().Authentication;
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
//More code to convert to local identity
}
This case is really common even using other providers like Google, Facebook or Twitter. One thing I have not much clear is ExternalCookie, maybe I have missed the whole thing. My understanding is when external login is successfully, external cookie is used to store the external claim identity. And then we call:
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
In order to get the external claim identity and then convert external identity to local identity. I have a little bit confusion why we have to call SignOut external cookie in this case.
Also, I'm not sure whether External Cookie is a must when using external login, or do we have other ways around without using External Cookie.
Please someone give an explanation on this point.
To answer your last question, you change the name of cookie in startup.auth file where you configure external cookie -
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
You can use a string instead of DefaultAuthenticationTypes enum and directly specify the name of the cookie like -
app.UseExternalSignInCookie("myExternalCookie");
I've got an MVC4 project that I'm working on. When a user's login credentials are valid, I call FormsAuthentication.SetAuthCookie() to indicate that the user is logged in. (I have it wrapped in a class so I can mock the Interface for my unit tests.)
namespace FlashMercy.Shared.Security
{
using System;
using System.Web.Security;
public class Auth : IAuth
{
public void SetAuthCookie(string userId, bool remember)
{
FormsAuthentication.SetAuthCookie(userId, remember);
}
public void Signout()
{
FormsAuthentication.SignOut();
}
}
}
In the debugger, I can confirm that the .SetAuthCookie(userId, remember) line is executing, and userId is populated.
Then, I have a custom authorize attribute to check that the user is logged in:
namespace FlashMercy.Shared.Security
{
using System.Web.Mvc;
public class FlashMercyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/");
}
}
}
}
When I debug the application, the filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie. Also, filterContext.HttpContext.User.Identity.Name is empty. I'm not sure what I'm missing here.
Update
If you care to look at the whole source, it's available on GitHub: https://github.com/quakkels/flashmercy.
Problem with your code is that you are using FormsAuthentication, but you didn't add it to web.config. Your web.config should have such section:
<system.web>
<authentication mode="Forms"></authentication>
...
</system.web>
Based on this Mode Asp.Net understand what authentication mode it should use, e.g. Forms, Windows, etc. And without settings it to Forms value - FormsAuthenticationModule just ignores .ASPXAUTH cookie from the request.
PS. I've downloaded your code, and with correct authentication section in web.config it works fine and updates HttpContext.User.Identity.IsAuthenticated to true.
The problem is that you only set the authentication cookie but do not have anything that load it.
It's forms authentication that uses that cookie. So you either have to activate forms authentication or you'll have to load it yourself.
filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie.
This will always be the case if you do not redirect after SetAuthCookie(). The ASP.Net pipeline is in charge of authorizing the user (most of the time before we write code) in the AuthenticateRequest. Setting a Cookie does not update the current User.Identity, this requires code that has already been executed. Just make sure anytime you SetAuthCookie() you immediately redirect (server side is fine) to another URL (probably should anyway, its a good way to seperate logging in a user, and what they should do next SRP).
Before trying MVC5, I had a go at using OpenAM with MVC4, with slightly better results. I need to authenticate from an asp.net application using OpenAM, and don't fancy the Fedlet route - I can see anybody else who has ever tried that
So, this was my starting point. This shows how to use Google and Facebook, and it works a treat. This goes on to show how to use other providers. I'm using OpenAM with OAuth2, so created "OpenAMClient", inheriting from DotNetOpenAuth.AspNet.Clients.OAuth2Client, and registered this in AuthConfig.RegisterAuth:
OAuthWebSecurity.RegisterClient(new OpenAMClient("consumerKey", "consumerSecret"));
This is great, because it now appears automatically on the login page, and pretty much worked perfectly until I started to use it for authentication. In the GetServiceLoginUrl override, I constructed a path to my OpenAM server, appending the returnURL that had been generated by the application and passed in as a parameter:
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var response =
string.Format(
"http://localopenam.hibu.com:9080/openam_10.1.0/oauth2/authorize?client_id={0}&response_type=code&scope=email&redirect_uri={1}",
_consumerKey, returnUrl);
return new Uri(response);
}
This got me to my OpenAM server login page, but after authenticating, I got an error saying that the redirection URI wasn't acceptable. Debugging the code, I can see that the ReturnURL starts off in the ExternalLoginResult as "/Account/ExternalLoginCallback", but by the time it reaches GetServiceLoginUrl, it has become:
http://localhost:60448/Account/ExternalLoginCallback?__provider__=OpenAM&__sid__=12e299cbac474b60a935f946f69d04a8
OpenAM isn't having any of that, as the "sid" parameter is dynamic, and it doesn't seem to acccept wildcards - it won't allow the returnURL provided by OAuthWebSecurity.
As a workaround, I intercept the ReturnURL, and switch to a new AccountController method:
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var workingUrl = "http://localhost:60448/Account/OpenAMCallback";
var response =
string.Format(
"http://localopenam.hibu.com:9080/openam_10.1.0/oauth2/authorize?client_id={0}&response_type=code&scope=email&redirect_uri={1}",
_consumerKey, workingUrl);
return new Uri(response);
}
I add http://localhost:60448/Account/OpenAMCallback as a redirectURL in OpenAM, then added AccountController OpenAMCallback:
public ActionResult OpenAMCallback(string code)
{
Console.WriteLine(code );
//use the code to get the token, then user details etc
return RedirectToLocal(null);
}
This is great, because from here I get the access code, so I can make more requests for the token, get all the allowed user details, all of that kind of thing, but ... I'm jealous of the original ExternalLoginCallback method I've subverted away from, that all the other cool authentication servers use. I want to use OAuthWebSecurity.VerifyAuthentication and OAuthWebSecurity.GetOAuthClientData, but VerifyAuthentication is coming back as null, so that stops that party
I can use http://dotnetopenauth.net/ and do it by hand, but I'd rather use a framework so there's less to maintain. Am I missing something obvious, or is OAuthWebSecurity not really up to this so I should stick with dotnetopenauth?
thanks