using flask-login with postgresql - postgresql

I'm working on a flask app that needs authentication. I've hooked up flask-login but it doesn't seem very graceful.
First flask-login needs to make sure the user exists:
#login_manager.user_loader
def load_user(id):
return User.query.get(id)
But you also need to use 'login_user' to create the user object
# Some code above
user = User.query.filter_by(email = form.email.data, password = form.password.data).first()
user.login_status = 1
db.session.commit()
login_user(objects.SignedInUser(user.id, user.email, user.login_status == LoginStatus.Active))
# Some code below
In the code above 'User' is a model for postgres and SignedInUser is just an object to be used for flask-login.
Does anyone have an example of flask-login used with postgres?

It looks like you might be misunderstanding what Flask-Login handles. It's there to keep track of everything about the user's session after you tell it authentication was successful (by calling login_user.) The user_loader callback only tells it how to reload the object for a user that has already been authenticated, such as when someone reconnects to a "remember me" session. The docs are not especially clear on that.
There should be no need to keep a flag in the database for the user's login status. Also, the code you included will raise an AttributeError if the credentials are incorrect (user = None).
Here's an example from a Flask-SQLAlchemy application. It uses an external authentication source and a wrapper for the SQLAlchemy User object, but the process is basically the same.
user_loader callback:
#login_manager.user_loader
def load_user(user_id):
user = User.query.get(user_id)
if user:
return DbUser(user)
else:
return None
User class (wrapper for SQLAlchemy object):
# User class
class DbUser(object):
"""Wraps User object for Flask-Login"""
def __init__(self, user):
self._user = user
def get_id(self):
return unicode(self._user.id)
def is_active(self):
return self._user.enabled
def is_anonymous(self):
return False
def is_authenticated(self):
return True
Login handler:
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
next = request.args.get('next')
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if authenticate(app.config['AUTH_SERVER'], username, password):
user = User.query.filter_by(username=username).first()
if user:
if login_user(DbUser(user)):
# do stuff
flash("You have logged in")
return redirect(next or url_for('index', error=error))
error = "Login failed"
return render_template('login.html', login=True, next=next, error=error)
Note that login fails if:
external auth fails
user query returns None (user does not exist)
login_user returns False (user.is_active() == False)
Logout
#app.route('/logout')
#login_required
def logout():
logout_user()
flash('You have logged out')
return(redirect(url_for('login')))

Related

Graphene JWT authentication

I'm using graphene-jwt to authenticate user but my user has to multiple object return
here
def mutate(cls, root, info, **kwargs):
result = super().mutate(root, info, **kwargs)
...
How can I add additional query to 'filter' more like User.object.filter(user_type=3)? cause currently my code is like this
except (MultipleObjectsReturned, JSONWebTokenError) as e:
users = models.User.objects.get(email=kwargs.get("email"), user_type_id=3)
result = cls(
user=users,
errors=[Error()],
account_errors=[],
token=get_token(users))
user = result.user
I do get token even my password is wrong, it should be failed when the password is wrong.
Thanks

Token based authentication in Play filter & passing objects along

I've written an API based on Play with Scala and I'm quite happy with the results. I'm in a stage where I'm looking at optimising and refactoring the code for the next version of the API and I had a few questions, the most pressing of which is authentication and the way I manage authentication.
The product I've written deals with businesses, so exposing Username + Password with each request, or maintaining sessions on the server side weren't the best options. So here's how authentication works for my application:
User authenticates with username/password.
Server returns a token associated with the user (stored as a column in the user table)
Each request made to the server from that point must contain a token.
Token is changed when a user logs out, and also periodically.
Now, my implementation of this is quite straightforward – I have several forms for all the API endpoints, each one of which expects a token against which it looks up the user and then evaluates if the user is allowed to make the change in the request, or get the data. So each of the forms in the authenticated realm are forms that need a token, and then several other parameters depending on the API endpoint.
What this causes is repetition. Each one of the forms that I'm using has to have a verification part based on the token. And its obviously not the briefest way to go about it. I keep needing to replicate the same kind of code over and over again.
I've been reading up about Play filters and have a few questions:
Is token based authentication using Play filters a good idea?
Can a filter not be applied for a certain request path?
If I look up a user based on the supplied token in a filter, can the looked up user object be passed on to the action so that we don't end up repeating the lookup for that request? (see example of how I'm approaching this situation.)
case class ItemDelete(usrToken: String, id: Long) {
var usr: User = null
var item: Item = null
}
val itemDeleteForm = Form(
mapping(
"token" -> nonEmptyText,
"id" -> longNumber
) (ItemDelete.apply)(ItemDelete.unapply)
verifying("unauthorized",
del => {
del.usr = User.authenticateByToken(del.usrToken)
del.usr match {
case null => false
case _ => true
}
}
)
verifying("no such item",
del => {
if (del.usr == null) false
Item.find.where
.eq("id", del.id)
.eq("companyId", del.usr.companyId) // reusing the 'usr' object, avoiding multiple db lookups
.findList.toList match {
case Nil => false
case List(item, _*) => {
del.item = item
true
}
}
}
)
)
Take a look at Action Composition, it allows you to inspect and transform a request on an action. If you use a Play Filter then it will be run on EVERY request made.
For example you can make a TokenAction which inspects the request and if a token has been found then refine the request to include the information based on the token, for example the user. And if no token has been found then return another result, like Unauthorized, Redirect or Forbidden.
I made a SessionRequest which has a user property with the optionally logged in user, it first looks up an existing session from the database and then takes the attached user and passes it to the request
A filter (WithUser) will then intercept the SessionRequest, if no user is available then redirect the user to the login page
// Request which optionally has a user
class SessionRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest[A](request)
object SessionAction extends ActionBuilder[SessionRequest] with ActionTransformer[Request, SessionRequest] {
def transform[A](request: Request[A]): Future[SessionRequest[A]] = Future.successful {
val optionalJsonRequest: Option[Request[AnyContent]] = request match {
case r: Request[AnyContent] => Some(r)
case _ => None
}
val result = {
// Check if token is in JSON request
for {
jsonRequest <- optionalJsonRequest
json <- jsonRequest.body.asJson
sessionToken <- (json \ "auth" \ "session").asOpt[String]
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if the token is in a cookie
for {
cookie <- request.cookies.get("sessionid")
sessionToken = cookie.value
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if its added in the header
for {
header <- request.headers.get("sessionid")
session <- SessionRepository.findByToken(header)
} yield session
}
result.map(x => new SessionRequest(x.user, request)).getOrElse(new SessionRequest(None, request))
}
}
// Redirect the request if there is no user attached to the request
object WithUser extends ActionFilter[SessionRequest] {
def filter[A](request: SessionRequest[A]): Future[Option[Result]] = Future.successful {
request.user.map(x => None).getOrElse(Some(Redirect("http://website/loginpage")))
}
}
You can then use it on a action
def index = (SessionAction andThen WithUser) { request =>
val user = request.user
Ok("Hello " + user.name)
}
I hope this will give you an idea on how to use Action Composition
The people at Stormpath has a sample Play application providing authentication via their Backend Service. Some of its code could be useful to you.
It uses username/password rather than tokens, but it should not be complex to modify that.
They have followed this Play Document:
https://www.playframework.com/documentation/2.0.8/ScalaSecurity.
The specific implementation for this is here:
https://github.com/stormpath/stormpath-play-sample/blob/dev/app/controllers/MainController.scala.
This Controller handles authentication operations and provides the isAuthenticated action via the Secured Trait (relying on play.api.mvc.Security). This operation checks if the user is
authenticated and redirects him to the login screen if he is not:
/**
* Action for authenticated users.
*/
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) =
Security.Authenticated(email, onUnauthorized) { user =>
Action.async { request =>
email(request).map { login =>
f(login)(request)
}.getOrElse(Future.successful(onUnauthorized(request)))
}
}
Then, each controller that needs authenticated operations must use the
Secured Trait:
object MyController extends Controller with Secured
And those operations are "wrapped" with the IsAuthenticated action:
def deleteItem(key: String) = IsAuthenticated { username => implicit request =>
val future = Future {
MyModel.deleteItem(request.session.get("id").get, key)
Ok
}
future.map(
status => status
)
}
Note that the deleteItem operation does not need a username, only the key. However, the authentication information is automatically obtained from the session. So, the business' API does not get polluted with security-specific parameters.
BTW, that application seems to have never been officially released so consider this code a proof of concept.

Pipeline workflow and variables

I have Facebook authentication working on my site, but I need the user to fill a profile form during his authentication. I have used an authentication pipeline to do so but whithout success. The pipeline is being called like it should, but the result is an error.
Let's say I need his mobile number - consider it does not come from Facebook.
Please consider:
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
mobile = models.IntegerField()
settings.py
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.mail.mail_validation',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details',
'myapp.pipeline.fill_profile',
)
pipeline.py
from myapp.models import Profile
from social.pipeline.partial import partial
#partial
def fill_profile(strategy, details, user=None, is_new=False, *args, **kwargs):
try:
if user and user.profile:
return
except:
return redirect('myapp.views.profile')
myapp/views.py
from django.shortcuts import render, redirect
from myapp.models import Perfil
def profile(request):
if request.method == 'POST':
profile = Perfil(user=request.user,mobile=request.POST.get('mobile'))
profile.save()
backend = request.session['partial_pipeline']['backend']
redirect('social:complete', backend=)
return render(request,'profile.html')
The profile.html is just a form with an input text box named 'mobile' and a submit button.
Then I get this error:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x03C2FB10>>": "Profile.user" must be a "User" instance.
Why can't I access the User instance since the user in auth_user table is already there (I suppose)?
Please, what's wrong with this?
You can't access the user in request.user because it's not logged in yet, the user will be logged in social complete view after the pipeline executed. Usually partial pipeline views will save the form data into the session and then the pipeline will pick it and save it. Also you can set the user id in the session in your pipeline and then pick that value in your view. For example:
#partial
def fill_profile(strategy, user, *args, **kwargs):
...
strategy.session_set('user_id', user.id)
return redirect(...)

How to update securesocial username after user is created

I'm trying to write an editUser page with the Secure Social plugin implemented in a Play Framework app. I'm having trouble staying logged in after the username is changed. The issue occurs when I press submit for the editUser form after changing the username. It goes to the login page and says "You need to log in to access that page." The desired behavior is to redirect to editUser page without needing to relogin. In the database everything is successfully updated. So that works, it just is no longer is logged in.
Below is my controller method for my "User" controller for the POST of the user update.
If anyone could help me out with this it would be greatly appreciated. Thanks.
// The form uses the following case class
case class AccountInfo(userName: String, firstName: String, lastName: String, email: Option[String])
def update(username: String) = SecuredAction { implicit request =>
this.editAccountForm.bindFromRequest.fold (
hasErrors = { info =>
Redirect(routes.Users.edit()).flashing(Flash(editAccountForm.data) +
("error" -> Messages("validation.errors")))
},
success = { info =>
DB.withSession { implicit s: Session =>
val uid = User.currentUser(request.user.id.id,providerId).get.uid
User.update(uid, info, providerId)
}
val message = Messages("user.update.success")
Redirect(routes.Users.edit()).flashing("success" -> message)
.withCookies(request.cookies.get("id").get)
}
)
}
By changing the username you are changing the values used to identify the user (username + provider id). What is happening is that on the next request SecureSocial is looking for the old username and since it can't find it in the database it just kicks you out.
What you should do besides updating the database is update the Authenticator stored for your current session. Something like:
SecureSocial.authenticatorFromRequest(request).map { authenticator =>
val newId = request.user.id.copy( id = userName )
Authenticator.save(authenticator.copy( userId = newId))
}
That should make it work. Also, you don't need to add the id cookie to your Redirect. SecureSocial does that for you.

how to use facebook api on a group

i created an application to test facebook api using Python, then, in the application, i created its Group, but, the problem that this Group dont know who i'm; it shows me that am the Admin, but when i try to publish something using Tornado, i get the error
GraphAPIError: (#210) User not visible
and this is because it seems that it deletes the cookie, because when am using the group's profile, then i cant see the GraphAPI since it dont know who is authentificated!
here is the code:
class MainHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
#tornado.web.authenticated
#tornado.web.asynchronous
def get(self):
self.facebook_request("/me/home", self.print_callback,access_token=self.current_user["access_token"])
a = self.current_user["access_token"]
self.graph = GraphAPI(a)
def print_callback(self, data):
self.graph.post_wall(self, "heloooooooo")
an sorry, because i dont get well the logic behind users Token, and Groups tokens? here i got a user Token? and because am the admin, i cant post!
EDIT: here are some snapshots i took from the application:
picture 1
picture 2
Update: i tried this:
def print_callback(self, me):
self.graph.post_wall(self, "helooooo", profile_id="267914489995838")
and got the error:
self.graph.post_wall(self, "helooooo", profile_id="267914489995838")
TypeError: post_wall() got multiple values for keyword argument 'profile_id'
and i used what is in the Tornado-Facebook-API
def post_wall(self, message, profile_id='me', body=None, **kwargs):
#XXX move to separate User class?
body = body or {}
body['message'] = message
self._make_request("{0}/feed".format(profile_id), method='POST',
body=body, **kwargs)
update2: here is the full code
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user_json = self.get_secure_cookie("user")
if not user_json: return None
return tornado.escape.json_decode(user_json)
class MainHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
#tornado.web.authenticated
#tornado.web.asynchronous
def get(self):
self.facebook_request("/me/accounts", self._on_accounts,
access_token=self.current_user["access_token"])
self.a = self.current_user["access_token"]
self.graph = GraphAPI(self.a)
def _on_accounts(self, account):
if account is None:
# Session may have expired
print "on accounts failed"
return
for acc in account["data"]:
if acc["id"] == "267914489995838":
print acc["access_token"]
self.facebook_request("/PAGE_ID/feed",
post_args={"message": "Test"},
access_token=acc["access_token"],
callback=self.async_callback(self._on_page_post))
def _on_page_post(self, post):
if not post:
# Post failed
return
class AuthLoginHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
#tornado.web.asynchronous
def get(self):
my_url = (self.request.protocol + "://" + self.request.host +
"/auth/login?next=" +
tornado.escape.url_escape(self.get_argument("next", "/")))
if self.get_argument("code", False):
self.get_authenticated_user(
redirect_uri=my_url,
client_id=self.settings["facebook_api_key"],
client_secret=self.settings["facebook_secret"],
code=self.get_argument("code"),
callback=self._on_auth)
return
self.authorize_redirect(redirect_uri=my_url,
client_id=self.settings["facebook_api_key"],
extra_params={"scope": "read_stream, publish_stream"})
def _on_auth(self, user):
if not user:
raise tornado.web.HTTPError(500, "Facebook auth failed")
self.set_secure_cookie("user", tornado.escape.json_encode(user))
self.redirect(self.get_argument("next", "/"))
class AuthLogoutHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
def get(self):
self.clear_cookie("user")
self.redirect(self.get_argument("next", "/"))
class PostModule(tornado.web.UIModule):
def render(self, post):
return self.render_string("modules/post.html", post=post)
Some clarifications:
A #200 error is a permission error. In this case, you don't have permissions to post somewhere.
There are two main types of tokens: user tokens and page tokens
You can add a group to an application's roles http://developers.facebook.com/blog/post/531/
As far as I know, there is no way to add an application to a group
So I think based on the Facebook id given you either want to do two things
Post to an application's timeline
Post to a group via an application
Posting to an application's timeline
This requires the application page access token, which you get from /me/accounts using the manage_pages and publish_stream permissions
self.facebook_request("/me/accounts", self._on_accounts,
access_token=self.current_user["access_token"])
def _on_accounts(self, account):
if account is None:
# Session may have expired
print "on accounts failed"
return
for acc in account["data"]:
if acc["id"] == "PAGE_ID":
print acc["access_token"]
self.facebook_request("/PAGE_ID/feed",
post_args={"message": "Test"},
access_token=acc["access_token"],
callback=self.async_callback(self._on_page_post))
def _on_page_post(self, post):
if not post:
# Post failed
return
See http://developers.facebook.com/docs/reference/api/application/ for more info
A full example can be seen at https://gist.github.com/3867203 (Which doesn't handle duplicate posting)