The Simple Login/Logout Management for Responder¶
Powered by Yamato Nagata. Responder-Login provides simple user session management. Github – Simple example
The basic idea is based on Flask-Login
Features¶
- Simple login/logout management
- Provides easy Login_Required/Login_Prohibited decorating.
- Storing user object in session.
Configuring Application¶
Before you use Responder_Login, you have to make instance of LoginManager
import responder
from responder_login import LoginManager
api = responder.API()
lm = LoginManager()
lm.init_app(api) # or you can do lm = LoginManager(api)
How It Works¶
To make this work, You have to provide LoginManager.user_loader
callback.
callback is used to specify user object with the data which stored in the session. This should take unicode
and return the instance of user object. If no object found, please return None
. Then, the instance of AnonymousUserMixin
object will be an returned.
@lm.user_loader
def load_user(user_id):
return User.get(user_id)
Custom User Object¶
This section is quoted from Flask-Login documentation The class that you use to represent users needs to implement these properties and methods:
- is_authenticated
- This property should return
True
if the user is authenticated, i.e. they have provided valid credentials. (Only authenticated users will fulfill the criteria oflogin_required
.) - is_active
- This property should return
True
if this is an active user - in addition to being authenticated, they also have activated their account, not been suspended, or any condition your application has for rejecting an account. Inactive accounts may not log in (without being forced of course). - is_anonymous
- This property should return
True
if this is an anonymous user. (Actual users should returnFalse
instead.) - get_id()
- This method must return a
str
that uniquely identifies this user, and can be used to load the user from theLoginManager.user_loader
callback. Note that this must be aunicode
- if the ID is natively anint
or some other type, you will need to convert it tostr
.
To make implementing a user class easier, you can inherit from UserMixin
,
which provides default implementations for all of these properties and methods.
(It’s not required, though.)
Login Example¶
We can use LoginManager.login_user
to log users in.
for example.
@api.route("/login")
async def login(req, resp):
if req.method == "get":
# this returns the form let the users
# input and send server some data to specify User Object
resp.html = """
Login<br>
<form action="/login" method="post">
<input name="name"><label>name</label></input><br>
<input name="age"><label>age</label></input><br>
<button type="submit">submit</button>
</form>"""
else:
data = await req.media(format="form")
user = UserObject(name=data["name"], age=data["age"])
# We made an UserObject from user's data
lm.login_user(user)
# this make users log in.
resp.html = f"""
you are now logging in as <br>
name: {user.name},
age: {user.age}"""
and then, LoginManager.logout_user()
to log out.
like this
@api.route("/logout")
def logout(req, resp):
user = lm.current_user
lm.logout_user()
resp.html = f"""logged out the user.<br>
name: {user.name}<br>
age: {user.age}"""
But in case if user isn’t logged in, above code will raise AttributeError
when find name
or age
in of user.
So, LoginManager
provides LoginManager.login_required
decorator. like below.
@api.route("/logout")
@lm.login_required
def logout(req, resp):
user = lm.current_user
lm.logout_user()
resp.html = f"""logged out the user.<br>
name: {user.name}<br>
age: {user.age}"""
and, If you want to make some page you don’t want logged in user to get in,
you can set LoginManager.login_prohibited
decorator.
Customizing¶
LoginManager(api=None)¶
Initialize LoginManager
.:code:api must be an instance of Responder.API
. api
can be None or not provided. But, you have to LoginManager.init_api()
LoginManager.init_api(api)¶
Set Responder.API
with given api
.
@LoginManager.user_loader¶
This decorates callback to set it LoginManager._user_callback
callback must take one argument and return instance of User object or None
.
@LoginManager.login_required¶
The decorator which decorates Responder.route
callback.
If user want to access decorated route but he/her must log in, this will call LoginManager._unauthorized_callback
if it’s provided. That can be set by decorating callback with LoginManager.unauthorized_handler
which takes Request
and Response
. If LoginManager._unauthorized_callback
isn’t provided, this will redirect to LoginManager.config["LOGIN_REQUIRED_ROUTE"]
if it’s set. If not, return LoginManager.config["LOGIN_REQUIRED_MESSAGE"]
@LoginManager.unauthorized_handler¶
This decorates callback to set it LoginManager._unauthorized_callback
@LoginManager.login_prohibited¶
The decorator which decorates Responder.route
callback.
If user want to access decorated route but he/her must log out, this will call LoginManager._authorized_callback
if it’s provided. That can be set by decorating callback with LoginManager.authorized_handler
which takes Request
and Response
. If LoginManager._authorized_callback
isn’t provided, this will redirect to LoginManager.config["LOGIN_PROHIBITED_ROUTE"]
if it’s set. If not, return LoginManager.config["LOGIN_PROHIBITED_MESSAGE"]
@LoginManager.authorized_handler¶
This decorates callback to set it LoginManager._authorized_callback
LoginManager.login_user(user)¶
user
must be an instance of user object. Set cookies with Response.set_cookie()
about account data, each value is below:
- key :
LoginManager.config[COOKIE_NAME]["ACCOUNT"]
- value :
user.get_id()
- expires : if
LoginManager.config["COOKIE_REMEMBER_ME"]["ACCOUNT"]
(Defaults toTrue
), True,datetime.datetime.now()
+LoginManager.config["COOKIE_DURATION"]
. Otherwise,None
- max_age : if
LoginManager.config["COOKIE_REMEMBER_ME"]["ACCOUNT"]
(Defaults toTrue
), True,LoginManager.config["COOKIE_DURATION"].total_seconds()
. Otherwise,None
- secure :
LoginManager.config["COOKIE_SECURE"]
Defaults toFalse
- httponly :
LoginManager.config["COOKIE_HTTPONLY"]
Defaults toFalse
aboutis_fresh
:
- key :
LoginManager.config[COOKIE_NAME]["IS_FRESH"]
- value :
1
- expires : if
LoginManager.config["COOKIE_REMEMBER_ME"]["IS_FRESH"]
(Defaults toFalse
),True
,datetime.datetime.now()
+LoginManager.config["COOKIE_DURATION"]
. Otherwise,None
- max_age : if
LoginManager.config["COOKIE_REMEMBER_ME"]["IS_FRESH"]
(Defaults to True),True
,LoginManager.config["COOKIE_DURATION"].total_seconds()
. Otherwise,none
- secure :
LoginManager.config["COOKIE_SECURE"]
Defaults toFalse
- httponly :
LoginManager.config["COOKIE_HTTPONLY"]
Defaults toFalse
LoginManager.logout_user()¶
This log users out by setting cookie that expires
and max_age
are 0
LoginManager.current_user¶
This returns instance of user object by searching instance by LoginManager._user_callback
. If no user found (callback returned None
), this will return LoginManager.anonumous_user
LoginManager.is_fresh¶
Return If the user logging in is logged in current session(not using Remember me). If logged in current session, returns True
. If not, False
. This returns False
if user is not logged in.
LoginManager.config¶
- COOKIE_NAME : The dictionary of cookie name. keys are`”ACCOUNT”` and
"IS_FRESH"
. Defaults to{"ACCOUNT": "account", "IS_FRESH": "fresh" }
- COOKIE_REMEMBER_ME : The dictionary of setting whether each cookie is Remember-me. keys are`”ACCOUNT”` and
"IS_FRESH"
. Defaults to{"ACCOUNT": True, "IS_FRESH": False }
- COOKIE_DURATION : The amount of time before the cookie expires, as a
datetime.timedelta object
. Defaults todatetime.timedelta(60)
- COOKIE_SECURE : Restricts the “Remember Me” cookie’s scope to https. Defaults to
False
- COOKIE_HTTPONLY : Prevents the “Remember Me” cookie from being accessed by client-side scripts. Defaults to
False
- LOGIN_REQUIRED_ROUTE : The route redirect to if user is not logged in and tried to access endpoint decorated with
LoginManager.login_required
. Defaults toNone
- LOGIN_REQUIRED_MESSAGE : The default message to display when users need to log in. Defaults to
"Please log in to access this page."
- LOGIN_PROHIBITED_ROUTE : The route redirect to if user is logged in and tried to access endpoint decorated with
LoginManager.login_prohibited
. Defaults toNone
- LOGIN_PROHIBITED_MESSAGE : The default message to display when users need to log out. Defaults to
"Please log out to access this page."
UserMixin¶
The simple mixin to make user object.
class UserMixin:
@property
def is_active(self):
return True
@property
def is_authenticated(self):
return True
@property
def is_anonymous(self):
return False
def get_id(self):
try:
return self.id
except AttributeError:
raise NotImplementedError('No `id` attribute. override `get_id` or set `id` attribute')
def __eq__(self, other):
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
AnonymousUserMixin¶
The user mixin for not logged in users
class AnonymousUserMixin(UserMixin):
@property
def is_authenticated(self):
return False
@property
def is_active(self):
return False
@property
def is_anonymous(self):
return True
def get_id(self):
return None
Indices and tables¶
In End¶
Sorry for my poor English. I want you to join us and send many pull requests about Doc, code, features and more!!