Goal: I'm trying to get a Ruby on Rails application to send me emails whenever a user fails to log into OmniAuth. I want the e-mail to include (1) the username entered in the form, and (2) an MD5 hash of the password field.
Obstacle: OmniAuth returns a POST after a successful login, and a GET after an authentication failure. The "success" POST includes the username and a filtered password, but the "fail" GET does not include these two parameters.
So I guess my question is "Can I make OmniAuth return the parameters I want? If not, how can I make Rails remember the form data after it gets POST'ed to OmniAuth?"
I emailed the OmniAuth team and they gave me the solution below (thank you so much!):
You can do custom failure handling by adding an on_failure action.
OmniAuth.config.on_failure = Proc.new { |env| #do stuff }
https://github.com/intridea/omniauth/blob/master/lib/omniauth/failure_endpoint.rb
is the default failure endpoint as an example
So I added the following in config/initializers/omniauth.rb:
OmniAuth.config.on_failure = Proc.new{|env|
myLog = ActiveSupport::TaggedLogging.new(Logger.new("log/omniauth_log.txt"))
myLog.tagged("OmniAuth", "ENV") { myLog.info "Failed login attempt - username: #{env["rack.request.form_hash"]["username"]}, password: #{env["rack.request.form_hash"]["password"]} "}
OmniAuth::FailureEndpoint.new(env).redirect_to_failure}
...and it records the username and password correctly. All that's left to do is encrypt the password.
If you want to display everything that's going on, you can log #{env.inspect} itself. It's a very large hash though (that also contains smaller hashes), so maybe log #{env.inspect} once and pick out the fields relevant to your task.
Related
I am attempting to create Reset Password functionality using Djoser. I am successfully hitting my API's auth/users/reset_password/ endpoint, which is then sending an email as expected. But the problem is occurring in the content of the email. It is sending a redirection link to my api, rather than to my frontend.
Please note, any <> is simply hiding a variable and is not actually displayed like that
Here is an example of what the email looks like:
You're receiving this email because you requested a password reset for your user account at <api>.
Please go to the following page and choose a new password: <api>/reset-password/confirm/<uid>/<token>
Your username, in case you've forgotten: <username>
Thanks for using our site!
The <api> team
The goal with this email is to send the user to the /reset-password/confirm/ url on my frontend, not on my api, which is currently occurring.
Here are my DJOSER settings:
DJOSER = {
'DOMAIN': '<frontend>',
'SITE_NAME': '<site-name>',
'PASSWORD_RESET_CONFIRM_URL': 'reset-password/confirm/{uid}/{token}',
}
The expected behavior is for the DOMAIN setting to alter the link that is being placed in the email, but it is not. I can't seem to find reference to this particular problem within the docs.
Any help here would be greatly appreciated, thanks.
I figured it out:
Due to Djoser extending the package django-templated-mail, the variables DOMAIN and SITE_NAME have to override django-templated-mail setting rather than Djoser's setting. So, you have to pull variables specific to django-templated-mail out of the Djoser variable.
The working setup actually looks like:
DOMAIN = '<frontend>',
SITE_NAME = '<site-name>',
DJOSER = {
'PASSWORD_RESET_CONFIRM_URL': 'reset-password/confirm/{uid}/{token}',
}
I'm building REST API.
I have following structure
GET /user/{id} - get
POST /users - Create user
PUT /users/{id} - Update user
DELETE /users/{id} - Delete user
The problem is following. As I got from many tutorials/articles - it is bad practice to use action in URL. But what to do with such actions like:
check email (is unique)
recover user by email
?
Assume user registration. User submits form ( POST /users ) and I need to check if email is unique. Do I need to do it in same api method and return validation errors as response body?
Or do I need to create something like
POST /users/email
And what about user recovering by email? Where do I need to do it? Because recover is verb
POST /users/recover
I'm not sure, that I'm doing it right and I can't find correct explanation for that situation.
Validating the e-mail and registering the user
If you want, you can have an endpoint to check whether an e-mail is already registered or not. It's up to your requirements. So, you can have something as following and then send the e-mail which will be validated in the request payload:
POST /users/email/validation
{
"email": "mail#example.com"
}
The endpoint above can be invoke, for example, when the user completes the e-mail field of your account registration form.
However, do not forget checking if the e-mail is already registered when creating a user:
POST /users
{
"firstName": "John",
"lastName": "Doe",
"email": "mail#example.com",
"password": "123456"
}
If an e-mail is already registered, you could consider returning a 409 Conflict status code and a payload that includes enough information for a user to recognize the source of the conflict.
Recovering the password
I'm unsure if this is your requirement, because of this I posted a comment asking for clarification. I presume you are trying to recover the password of a user, assuming the user has no more access to their account.
If so, you could have an endpoint as following and then send the e-mail of the user in the request payload:
POST /users/password/recovery
{
"email": "mail#example.com"
}
Then your server can send a link with a token to the e-mail specified in the payload. Only send the e-mail if the e-mail specified in the payload is registered in your application, of course.
The link should take the user to a page where they will enter the new password and, when submitting, an endpoint to replace the password of the user will be invoked, sending the token and the new password to the server:
PUT /users/password?token=SomeValueGoesHere
{
"password": "654321"
}
I'm trying to use loopback angular SDK to login but instead I'm getting back a 401 unauthorized response.
User.login({ rememberMe: true }, {email: $scope.user.email, password: $scope.user.password})
.$promise
.then(function() {
var next = $location.nextAfterLogin || '/';
$location.nextAfterLogin = null;
$location.path(next);
})
.catch(function(x) {
$scope.authError = 'Wrong Credentials';
});
};
Also i can see the user with the given credentials in the datasource that I've defined for the User model.
So basically, I have a boot script that creates a default user
User.create([{
username: 'admin',
email: 'xxxx#gmail.com',
password: 'admin'
}], on_usersCreated);
And I also have created a custom User model that extends the built-in User model.
Why am I getting a 401 unauthorized response when trying to login? I have even tried to login via the explorer, but with no success.
Update:
I debugged the build-in User model and the login method; and it seems that it cannot find the user with the given email. I can see it though in the mongodb database.
Inspect the ACL records of User model, starting you app in debug mode and viewing console: DEBUG=loopback:security:acl slc run.
I don't see anything wrong with your code. Are you sure the values you're passing into the function are correct? Try logging $scope.user.email and $scope.user.password before calling User.login.
See my new example here: https://github.com/strongloop/loopback-getting-started-intermediate/blob/master/client/js/services/auth.js#L6-L15
The tutorial part is not complete, you can look around the source code to get an idea of what you're doing different.
I am trying to create users from a POST request sent from curl/httpie commands. Users are created in the User Model, but the password is stored in raw string format. This is what i do.
http POST http://127.0.0.1:8000/user/ username=taco password=123
This creates a User with the following credentials.
Now when I enter my admin site, and click on the details of user created. The password shows like this.
Invalid password format or unknown hashing algorithm.
Raw passwords are not stored, so there is no way to see this user's password, but you can change the password using this form.
I have automatic token creation system on user post_save. Token is also created. but when i put.
http POST http://127.0.0.1:8000/obtain/ username=taco password=123
url***/obtain goes to views.obtain_auth_token imported from rest_framework.authtoken.views from which I receive the token for the specified User.
but I get a error saying..
"non_field_errors": [
"Unable to log in with provided credentials."
]
I basically want to signup(create) a user from terminal(http/curl) and obtain their token from "/obtain"
No worries It so happened that you cant post password in raw string.
so by capturing the .username and .password from the query paramenter. I wrote a a create user code on my view , and set the password using .setpassword().
I would like the feature for the sign in box to have the username and password automatically filled in if the user has previously been on the site and logged in successfully before. I see this implemented on many sites so I figured theres a way to do this without creating a security risk.
EDIT: According to a post this is a browser feature and should not be implemented in code because its never safe to store password anywhere.
Edited the code to reflect a new direction where Im only storing the username. However, Im not sure what to look for to see if its working. I tried to login then logout, then go to login screen again but username box still blank when the view loads in. Not sure if its the code or Im testing it the wrong way.
Login POST:
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Json(new { ok = true, message = "Login successful." });
}
}
return Json(new { ok = false, message = "The username or password you entered is invalid. Please try again." });
}
Login GET:
[HttpGet]
public ActionResult Login(string path)
{
LoginModel model = new LoginModel();
HttpCookie authCookie = System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName.ToString()];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null & !authTicket.Expired)
{
model.UserName = authTicket.UserData;
}
}
return PartialView(path, model);
}
There are couple of issues with your code. The first one is that you are adding the cookie to the Request object instead of adding it to the Response => Response.Cookies.Add(authCookie);.
The second issue is that you are creating a non-persistent cookie meaning that it will only live through the browser session. Once the user closes the browser it will be gone forever because it was never stored on the client computer. In order to create a persistent cookie you need to specify an expiration date for it which will obviously correspond for how long this cookie will be persisted on the client computer. For example if you wanted to remember for 5 days:
HttpCookie authCookie = new HttpCookie("authCookie", "cookieValue")
{
Expires = DateTime.Now.AddDays(5)
};
Another issue is that you are storing only the MD5 hash of the password inside the cookie and you expect to be able to decrypt it later with FormsAuthentication.Decrypt which is not possible. This method can decrypt values that were encrypted with the Encrypt method.
And the biggest problem of them all is the security: you should never be storing any password related stuff anywhere. The username should suffice. Browsers offer the possibility to remember passwords for given site. I would recommend you using this functionality instead of doing what you are doing.
Another possibility is to emit a persistent authentication cookie when the user logs in, so that even if he closes the browser he will be remembered as authenticated for the validity period you specified in this authentication cookie.
you don't need the actual password for the user to make sure he logged in previously. You shouldn't even save the actual password in the database. You usually only save a salted hash of that password. When the user logs in, you create a hash for that password and compare it with the hash stored in the database. If they match, he entered the correct password.
As for storing login information in a cookie, i'd then store a salted hash of that password hash in the cookie. Upon GET, you just create a salted hash of the stored password hash and compare it with the one from the cookie. If they match, the login cookie is valid. That way you never actually store any user password anywhere.
To make this more secure, the login page should be secured by SSL, otherwise the password would at least once be transmitted unencrypted.
If you're using SQL, you can generate a random number when the user first logs on. Store this number in the Users table, and also as a cookie on the user's machine. The user can then authenticate by comparing the random number with that stored on the server.
It's a good idea to timestamp the login though, so it expires after a set period of time.