I am developing an application, using VS 2010 and MVC4(Razor). I am stuck with the Url parameters. I am able to implement LogIn method. Once I validate the user I want him to redirect to other page, so I am using
RedirectToAction("UserAction","User",new{userID = "",password=""});
But the issue is as RedirectToAction usses HTTTPGet all the url parameters userID and the password are visible.
How do I invoke RedirectToAction with HTTPPost.
Any help will be highly appreciated.
Here is my Login.cshtml
#using (Html.BeginForm("SignIn", "Login", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Log in Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { id = "UserName" })
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { id = "Password" })
#Html.ValidationMessageFor(m => m.Password)
</li>
</ol>
<input type="submit" value="Log in"/>
</fieldset>
}
And here is my Login Controller
bool IsValidUser = ValidateEachUser(oLoginModel.
UserName,oLoginModel.Password);
if (IsValidUser)
return (RedirectToAction("UserDetails", "User", new { userID = userID,
password =
password }));
else
return View("Login");
Thanx and Regards
The solution is like #anaximander mentioned. Upon successful log in, save the user details in an encrypted cookie (e.g. FormsAuthenticationTicket) that gets submitted with every request, then you can verify the credentials on every action call by decrypting the cookie. If you want to implement your own verification protocol you can even implement your own AuthorizeAttribute and either decorate all the methods with it manually or register it globally in Global.asax (inside RegisterGlobalFilters).
You should really think though about whether you really need to keep the password. The fact that the user request contains your encrypted cookie can be used as an indication that the user has been authenticated. If you want to make sure that the user is authorized to run a certain action method, the user id should be enough. This would save you having to store the password and recheck it at every call before checking if the user is authorized to run the method.
TempData - is answer on your question:
TempData["userID "] = "some";
TempData["password"] = "some";
RedirectToAction("UserAction","User");
This might work. If it doesn't add a comment :)
if you want to a method HTTPPOST put the head of method like:
[HTTPPOST]
RedirectToAction(your parameters){}
This started as a couple of comments, but I think there's now enough to be worth making it an answer.
The best way to identify the user is probably with an encrypted cookie containing an identifying token - NOT their password - so that you can check who the user is. The cookie is sent with every request, silently, with no extra work. If you need it, you can check it. An action filter is a neat way to do this.
ASP.NET MVC 4 has a perfectly good Forms auth system built in that works exactly like this. There's plenty of tutorials out there; this one is a decent example. It's pretty straightforward to implement, and you can override a few parts of the workings to use your own database, your own user model, etc. The user provides credentials, you check them, and the system gives the user a cookie. The [Authorize] filter checks for that cookie and uses it to decide whether the user is allowed to do what they're trying to do. There's also scope for roles-based authorisation, but if you don't need that you can ignore it.
Whatever you do, you do NOT want to be keeping the user's password ANYWHERE. If you end up using your own database to store users, don't keep the password in there - look up something like bcrypt, use it to hash the password with a randomised salt, and store that along with the salt value. To check a user's login, you hash what they provided with the salt you have, and see if the output matches the hash you saved. The shorter the time that password remains in the system, the narrower the window where an attacker could get hold of it.
Related
Goal
Given a company e-mail and a corresponding password, I need to programatically login to login.microsoftonline.com and access the Office 365 dashboard (office.com). The image shows the user flow where I try to find out the respective endpoints.
Research results
This is what I found out about what endpoints are called and how. Note that endpoints might differ if you don't use a company account.
Assumptions:
Always follow redirects.
Collect cookies along the way and pass them with every subsequent request.
In the bodies of the requests, I only include parts that I found relevant. I could have left out important parts that I'm not aware of.
GET login.microsoftonline.com
Follow the redirects. The resulting HTML contains a config json object wrapped in //<![CDATA[ and //]]>. Search for "sFT": and copy the value. Search for "sCtx": and copy the value. Search for "canary": and copy the value. Search for "sessionId": and copy the value.
POST https://login.microsoftonline.com/common/GetCredentialType?mkt=en-US
I don't think this endpoint is essential. I still include it here.
Send a JSON body as follows:
{
"username": "<your-company-email>",
"flowToken": "<your-sFT-token>"
}
POST https://login.microsoftonline.com/common/login
Send as form data in the body:
login:<your-company-email>
passwd:<your-password>
flowToken:<your-sFT-token>
type:11
ctx:<your-sCtx-token>
canary:<your-canary-token>
hpgrequestid:<your-session-id>
POST https://login.microsoftonline.com/kmsi
"kmsi" stands for "keep me signed in". This might be an endpoint that is not called if you don't use a company mail for login.
Send as form data in the body:
LoginOptions:3
type:28
ctx:<your-sCtx-token>
flowToken:<your-sFT-token>
canary:<your-canary-token>
hpgrequestid:<your-session-id>
Calling these endpoints in the order displayed here, I am able to successfully retrieve these cookies:
From login.microsoftonline.com (10 cookies):
ESTSAUTH
ESTSAUTHLIGHT
ESTSAUTHPERSISTENT
ESTSSC
buid
ch
esctx
fpc
stsservicecookie
x-ms-gateway-slice
From office.com (1 cookie):
MUID
From www.office.com (2 cookies):
OH.DCAffinity
OH.SID
POST https://www.office.com/landing
Send as form data in the body. ??? is indicating that I don't know where this data is coming from.
code:<a code token ???>
id_token:<an id token ???>
state:<a state token ???>
session_state:<a session state token ???>
This endpoint seems to be crucial since it returns the following cookies:
OhpToken
UserIndex
OhpAuth
AjaxSessionKey
userid
GET https://www.office.com/
The cookies that were set during the previous request are sent in the request headers here. This is why the office.com/landing seems to be crucial. However, I can't figure out how the form data is constructed for that body, e.g. code, id_token etc.
Maybe related question?
Note that I've seen this question on stack overflow but I didn't find it useful and don't think it relates to my question.
Why do I need all this?
The main goal is to login users automatically from another tool (SSO). E-Mail and password is given. My approach is to login using these endpoints programatically on the server-side, collect the necessary cookies for subsequent logins, send them to the client. The client uses those cookies to access office.com and see the dashboard immediately without having to login.
A better approach? Note that I have access to the admin center (Microsoft) and to Azure Direct. If you know a much simpler approach to this solution, I'm glad to get to know it. Most of the articles I've read are concerned with how to use Azure to login to another service if you're logged in to Microsoft. But I need the opposite: being logged in to some service and accessing the Microsoft Office 365 dashboard automatically.
What you are trying to do is completely wrong - why to reverse-engineer frontend app and one day to find that everything changed?
There is official REST API to ms outlook online and authentication mechanism via Azure AD - exactly to let third-party apps to work with MS online apps.
Try to checkout this:
https://learn.microsoft.com/en-us/graph/overview-major-services
and this:
https://learn.microsoft.com/en-us/graph/azuread-identity-access-management-concept-overview
POSTing to https://login.microsoftonline.com/kmsi will return a html with all the values you need:
<html>
<head>
<title>Working...</title>
</head>
<body>
<form method="POST" name="hiddenform" action="https://admin.microsoft.com/landing">
<input type="hidden" name="code" value="0.AVwAAmogKpXYg0-1rfltzzT6ZQYAAAAAAPEPzgAAAAAAAAABAAA.AgABAAIAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P-gWiQqeCHo-9FEKAxJ1WYio4IwurbBrT2hB561ujjKXXdH08Yoqwrn7KlDJ2Ybp0SP7VNeX0v0313oQy9u184OF9SUmmPCM9AqRp8cW_Oh9AhenJEP8ZThY680N5XdQ_xvTaxCdyu0G2rMld7Yp-fnmKxQsr3UrdysQW6qe9mEXX_IsUYecF46BYO2kh7XsLGLXDDdm1ZJa46G_wAN00fYPmxgH4dlsauqK0URhVxVFZrws3yuPWTJEn5VNhL2Z2cUdsFfBEAFdHDrjOujdxzJKbfqln2GqLcNP_3LdgHKx-atrIM7JXJfp_oJeKCwXwvK6tUa4bhvEotIeGhES_l_0kxOZQDIbBMU2yUoBYrn17fxUmTAOt-HpeYRQFYr4bymdVnRsfMINZCSbD-lOaW6oh-cvWYpxqbq8ZZ3tZ7OJzZKetSNtAwplcUjZZchysueXy3-t7u2nr8k27jrSe2DudpGcn1GY25kkGQyz-SiqVm70RsKT9Fb6lxoSm7I8zpAWfFnLZJtxYhddHRx4tA521wAXoXOhBZyc5I7_gZYk2a50QcxeJI0K88mXxyPNvndN4F8eCtRYp1X53LSFgs4XyJ2UzWo9LXsWZ_77Fz7Ivlz2n4AEZXVZlE_PnqYylRdOWDV2NdpBhgFB53geIEuiX9t4JVl0o8TdpPrYjgsiqGmtbg3ZO4J4c_CDLOzBBJemdpAEnkukRVKHt-ZcXKwMfWHjWmEi0p6Ji1GI92f0UO_ZTpUdXScKC4UQsPrWAU" />
<input type="hidden" name="id_token" value="[TOKENVALUE]" />
<input type="hidden" name="state" value="OpenIdConnect.AuthenticationProperties=78qNGNcn8xtuLU4P5T0Dz5QMprE4YNYcSnczbXtwxwZL-dMfErfi6EuAkBJw0mFDmV_sQY7Q3av60KFahCPqhj6hzPR_JtBiYotBBoHd0zSKjqM7HgdD_QMhKZKReePeYsiTgLZtXAcNmzRBdjtpeflCa4TTRQY7tqOvN2kOUZY" />
<input type="hidden" name="session_state" value="16f47f33-cf2e-4300-9a9b-72276bd518f0" />
<input type="hidden" name="correlation_id" value="1a7d67ed-2db0-44ea-a351-f9d70ff14229" />
<noscript>
<p>Script is disabled. Click Submit to continue.</p><input type="submit" value="Submit" />
</noscript>
</form>
<script language="javascript">
document.forms[0].submit();
</script>
</body>
</html>
You must extract the values of each input and use it on the https://www.office.com/landing. Do not forget to encode the state value
Question: How can I enforce existing users to set up 2FA in .Net Core 3.1 Identity?
I have seen a couple of answers here already, but I have issues with them as follows:
Redirect user to set up 2FA page on login if they do not have it set up. Problem with this is that the user can simply jump to a different url to avoid this, therefore it is not actually enforced.
Have some on executing filter that checks if the user has 2FA enbaled or not and if not redirect them to MFA set up page. The issue I have with this is that on every single navigation the server must go to the database to check whether the user has this field enabled, thus creating a significant performance hit on each request. I know one trip to the database may not sound like much but I have worked with applications where this was the norm and other things used this method, causing a pile up of pre action db queries. I want to avoid this kind of behavior unless absolutely necessary.
My current idea is to on login:
Check the users credentials but NOT log them in
userManager.CheckPasswordAsync(....)
If the credentials pass, check if the user has 2FA enabled or not. If they do, continue through login flow, if not:
Generate a user token:
userManager.GenerateUserTokenAsync(.......)
and store this along with the username in a server side cache. Then pass a key to the cached items with a redirect to the 2FA setup page, which will not have the [authorize] attribute set, allowing users not logged in to access it.
Before doing anything on the 2FA set up page, retrieve the cached items with the provied key andverify the token and username:
userManager.VerifyUserTokenAsync(......)
If this doesn't pass, return Unauthorized otherwise continue and get the current user from the supplied UserName in the url that was passed via a cache key. Also dump the cached items and key so that should the url be snatched by a dodgy browser extension it can't be used again.
Continue to pass a new cache key to new user tokens and usernames to each 2FA page to authenticate the user as they navigate.
Is this an appropriate use of user tokens? And is this approach secure enough? I'm concerned that having the user not logged in presents security issues, but I think it is necessary in order to avoid the previously mention problem of going to the database on every request to check 2FA, as with this method trying to navigate away will just redirect to login.
I implemented this via a Filter Method
I have a BasePageModel which all my pages inherit
public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
if (!User.Identity.IsAuthenticated)
{
await next.Invoke();
return;
}
var user = await UserManager.GetUserAsync(User);
var allowedPages = new List<string>
{
"Areas_Identity_Pages_Account_ConfirmEmail",
"Areas_Identity_Pages_Account_ConfirmEmailChange",
"Areas_Identity_Pages_Account_Logout",
"Areas_Identity_Pages_Account_Manage_EnableAuthenticator",
"Areas_Identity_Pages_Account_ResetPassword",
"Pages_AllowedPageX",
"Pages_AllowedPageY",
"Pages_Privacy"
};
var page = context.ActionDescriptor.PageTypeInfo.Name;
if (!user.TwoFactorEnabled && allowedPages.All(p => p != page))
{
context.Result = RedirectToPage("/Account/Manage/EnableAuthenticator", new { area = "Identity" });
}
else
{
await next.Invoke();
}
}
I then changed both the Disable2fa and ResetAuthenticator pages to redirect to the main 2fa page
public IActionResult OnGet() => RedirectToPage("./TwoFactorAuthentication");
And removed the reset/disable links from that page
I chose to implement a more modern and OAuth friendly solution (which is inline with .Net Core Identity).
Firstly, I created a custom claims principal factory that extends UserClaimsPrincipalFactory.
This allows us to add claims to the user when the runtime user object is built (I'm sorry I don't know the official name for this, but its the same thing as the User property you see on controllers).
In here I added a claim 'amr' (which is the standard name for authentication method as described in RFC 8176). That will either be set to pwd or mfa depending on whether they simply used a password or are set up with mfa.
Next, I added a custom authorize attribute that checks for this claim. If the claim is set to pwd, the authorization handler fails. This attribute is then set on all controllers that aren't to do with MFA, that way the user can still get in to set up MFA, but nothing else.
The only downside with this technique is the dev needs to remember to add that attribute to every non MFA controller, but aside from that, it works quite well as the claims are stored in the users' cookie (which isn't modifiable), so the performance hit is very small.
Hope this helps someone else, and this is what I read as a base for my solution:
https://damienbod.com/2019/12/16/force-asp-net-core-openid-connect-client-to-require-mfa/
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/mfa?view=aspnetcore-5.0#force-aspnet-core-openid-connect-client-to-require-mfa
I am working on an asp.net mvc core web application for users' registration within our system, and we have the following scenario:-
1) when a user access the web application, they enter their registration number
2) then they click on submit >> and fill the registration form.
3) we want to track the users who enter step number one , but did not complete the registration.
so since i am tracking users' actions, so i enable the GDPR inside my application by following these steps https://learn.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-2.2 . and using the following code, i am able to check if the user accept the privacy alert or not:-
var consentFeature = HttpContext.Features.Get<ITrackingConsentFeature>();
var canTrack = consentFeature.CanTrack ;
and if the canTrack return true (the user accept the privacy alert) then i will track the users' action, while if the canTrackreturn false (the user did not accept the privacy alert) i will not track the user actions.. so am i going things correctly?
First things first: I'm not a lawyer.
Being GDPR-compliant means a lot more than "just" acquiring tracking consent from your users (although it's not an easy task as it is).
If you are collecting the consent and only do the tracking based on it, that's one (prominent) aspect; depending on your business/organization, you may also need to have a privacy policy and support consent revocation – as the user might want to withdraw their consent at any time. In addition, there might be other requirements to fulfil.
There is an abundance of resources at your disposal should you want to read more about it, and decide which are the next steps to take. But I would suggest talking to an expert who could help you on the matter. You could even hire someone at Fiverr or UpWork.
The question of whether or not that is compliant with GDPR is one you won't find an answer to on SO, but as for your question on whether or not you are using the consent mechanism in ASP.NET core correctly, can be answered.
First, I suggest you read up on the bigger picture here, here is an article from MS:
https://learn.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-3.1
In essence, you need something like this to handle the consent banner.
#{
var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
var showBanner = !consentFeature?.CanTrack ?? false;
var cookieString = consentFeature?.CreateConsentCookie();
}
#if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show" role="alert">
Use this space to summarize your privacy and cookie use policy. <a asp-page="/Privacy">Learn More</a>.
<button type="button" class="accept-policy close" data-dismiss="alert" aria-label="Close" data-cookie-string="#cookieString">
<span aria-hidden="true">Accept</span>
</button>
</div>
<script>
(function () {
var button = document.querySelector("#cookieConsent button[data-cookie-string]");
button.addEventListener("click", function (event) {
document.cookie = button.dataset.cookieString;
}, false);
})();
</script>
}
The key point here is that it is your responsibility to present the user with a consent dialog and report back the response. The ASP.NET framework will help web developers track user consent status and there is a standardized API for managing this, which is essential since this will enable adhering to user consent even in third party middleware etc.
Which brings us to the next point, it is your responsibility to ensure that all your code as well as any third party code you pull in follow this. If you have any third party dependencies you need to check the documentation for those (or possibly use the web browser's debug tools to verify) that it does not store cookies nor send off request to third parties.
Any javascript libraries such as Google Analytics or whatever telemetry you use, also need to take this into account. This you have to solve on the JavaScript side. Check the documentation. Here is a checklist for Google Analytics.
For Application Insights, there is the window.appInsights.config.isCookieUseDisabled property that you may need to set. Looks like automatically adhering to the ASP.NET core consent tracking is still an open issue.
So in short, looks like you are on the right track, but you likely need to do more.
So I have a ember-rails app set up with an authentication API and I can successfully (or not) authenticate with ember auth and everything works. However, the authentication only persists for the duration of the current application session. If I reload the page or enter the URL, I must re-authenticate.
Is there some setting or configuration necessary to make the auth token longer lived? I'm not necessarily talking about "Remember Me" functionality, so much as making a single session a bit hardier.
My basic code:
Auth Object:
App.Auth = Em.Auth.create
currentUser: null
signInEndPoint: '/users/sign_in'
signOutEndPoint: '/users/sign_out'
tokenKey: 'auth_token'
tokenIdKey: 'user_id'
Sign in View:
App.AuthSignInView = Ember.View.extend
templateName: 'auth/sign_in'
email: null
password: null
submit: (event, view) ->
event.preventDefault()
event.stopPropagation()
StripfighterEmber.Auth.signIn
data:
email: #get 'email'
password: #get 'password'
Auth Template:
<form class="form-inline">
{{view Ember.TextField class="input-small" placeholder="Email" valueBinding="view.email"}}
{{view Ember.TextField type="password" class="input-small" placeholder="Password" valueBinding="view.password"}}
<button type="submit" class="btn btn-inverse btn-small">Sign in</button>
</form>
ember-auth dev here.
Your use case is essentially a default-on "remember me", but with a very short duration. You could still enable rememberable (with auto-recall on), and just "silently opt-in" to this feature, i.e. make your server return a remember cookie as long as the sign in is valid.
The reason behind this is that, from an ember app's point of view, there is no way of differentiate between a browser restart, a url change, and a refresh. All these represent an app restart. From this, ember needs to restore the app state from saved information (cookies, localStorage) and passed information (url). The authentication session is no different. The rememberable module saves the auth token in the former (you pick, cookie or localStorage); the urlAuthenticatable module lets you pass in auth info in the url.
As Mike has said, you could also roll your own, but I would advise you to take advantage of existing functionalities ("other people's efforts").
Is there some setting or configuration necessary to make the auth token longer lived? I'm not necessarily talking about "Remember Me" functionality, so much as making a single session a bit hardier.
You could save the auth token to a cookie or local storage. but I would not recommend it. That's what the remember-me functionality built into ember-auth is for. http://ember-auth.herokuapp.com/docs
All , After I read This Post, I had some idea of implement user login and authentication in Asp.net MVC4. I hope someone can help to review this. Any comments or other solutions are welcome. Thanks.
Firstly,I would use a viewbag variable to determine whether it is a authenticated request in the view.
the code should be something like:
<% if (ViewData["longined"] == "success")
{ %>
//display something shows user already logged in.
<%}%>
Then Implement a global filter to check authenticated user info which can be retrieve from session. If this user info does exist in session,That means the user doesn't log in. otherwise should set
ViewData["longined"] == "success".
In the login action and view . I can fill the user name and password in the view, and post to an Action to validate them. If success I would store the user info in the session.
The last thing need to do is registering global filter in Global.asax. thanks.