Merge pull request #94 from simple-login/social-login
Social login improvements
This commit is contained in:
commit
4358b2791b
|
@ -45,6 +45,10 @@ def auth_login():
|
|||
elif not user.activated:
|
||||
return jsonify(error="Account not activated"), 400
|
||||
|
||||
return jsonify(**auth_payload(user, device)), 200
|
||||
|
||||
|
||||
def auth_payload(user, device) -> dict:
|
||||
ret = {
|
||||
"name": user.name,
|
||||
"mfa_enabled": user.enable_otp,
|
||||
|
@ -64,4 +68,4 @@ def auth_login():
|
|||
ret["mfa_key"] = None
|
||||
ret["api_key"] = api_key.code
|
||||
|
||||
return jsonify(**ret), 200
|
||||
return ret
|
||||
|
|
|
@ -14,7 +14,7 @@ from app.config import (
|
|||
)
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import User
|
||||
from app.models import User, SocialAuth
|
||||
from .login_utils import after_login
|
||||
from ...email_utils import can_be_used_as_personal_email, email_already_used
|
||||
|
||||
|
@ -143,4 +143,8 @@ def facebook_callback():
|
|||
# reset the next_url to avoid user getting redirected at each login :)
|
||||
session.pop("facebook_next_url", None)
|
||||
|
||||
if not SocialAuth.get_by(user_id=user.id, social="facebook"):
|
||||
SocialAuth.create(user_id=user.id, social="facebook")
|
||||
db.session.commit()
|
||||
|
||||
return after_login(user, next_url)
|
||||
|
|
|
@ -9,7 +9,7 @@ from app.config import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, URL, DISABLE_REGI
|
|||
from app.email_utils import can_be_used_as_personal_email, email_already_used
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import User
|
||||
from app.models import User, SocialAuth
|
||||
from app.utils import encode_url
|
||||
|
||||
_authorization_base_url = "https://github.com/login/oauth/authorize"
|
||||
|
@ -105,6 +105,10 @@ def github_callback():
|
|||
|
||||
flash(f"Welcome to SimpleLogin {user.name}!", "success")
|
||||
|
||||
if not SocialAuth.get_by(user_id=user.id, social="github"):
|
||||
SocialAuth.create(user_id=user.id, social="github")
|
||||
db.session.commit()
|
||||
|
||||
# The activation link contains the original page, for ex authorize page
|
||||
next_url = request.args.get("next") if request.args else None
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from app.auth.base import auth_bp
|
|||
from app.config import URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, DISABLE_REGISTRATION
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import User, File
|
||||
from app.models import User, File, SocialAuth
|
||||
from app.utils import random_string
|
||||
from .login_utils import after_login
|
||||
from ...email_utils import can_be_used_as_personal_email, email_already_used
|
||||
|
@ -128,6 +128,10 @@ def google_callback():
|
|||
# reset the next_url to avoid user getting redirected at each login :)
|
||||
session.pop("google_next_url", None)
|
||||
|
||||
if not SocialAuth.get_by(user_id=user.id, social="google"):
|
||||
SocialAuth.create(user_id=user.id, social="google")
|
||||
db.session.commit()
|
||||
|
||||
return after_login(user, next_url)
|
||||
|
||||
|
||||
|
|
|
@ -102,8 +102,10 @@ class AliasGeneratorEnum(enum.Enum):
|
|||
class User(db.Model, ModelMixin, UserMixin):
|
||||
__tablename__ = "users"
|
||||
email = db.Column(db.String(256), unique=True, nullable=False)
|
||||
salt = db.Column(db.String(128), nullable=False)
|
||||
password = db.Column(db.String(128), nullable=False)
|
||||
|
||||
salt = db.Column(db.String(128), nullable=True)
|
||||
password = db.Column(db.String(128), nullable=True)
|
||||
|
||||
name = db.Column(db.String(128), nullable=False)
|
||||
is_admin = db.Column(db.Boolean, nullable=False, default=False)
|
||||
alias_generator = db.Column(
|
||||
|
@ -156,11 +158,9 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||
def create(cls, email, name, password=None, **kwargs):
|
||||
user: User = super(User, cls).create(email=email, name=name, **kwargs)
|
||||
|
||||
if not password:
|
||||
# set a random password
|
||||
password = random_string(20)
|
||||
if password:
|
||||
user.set_password(password)
|
||||
|
||||
user.set_password(password)
|
||||
db.session.flush()
|
||||
|
||||
# create a first alias mail to show user how to use when they login
|
||||
|
@ -241,6 +241,8 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||
self.password = password_hash
|
||||
|
||||
def check_password(self, password) -> bool:
|
||||
if not self.password:
|
||||
return False
|
||||
password_hash = bcrypt.hashpw(password.encode(), self.salt.encode())
|
||||
return self.password.encode() == password_hash
|
||||
|
||||
|
@ -351,6 +353,17 @@ class ResetPasswordCode(db.Model, ModelMixin):
|
|||
return self.expired < arrow.now()
|
||||
|
||||
|
||||
class SocialAuth(db.Model, ModelMixin):
|
||||
"""Store how user authenticates with social login"""
|
||||
|
||||
user_id = db.Column(db.ForeignKey(User.id, ondelete="cascade"), nullable=False)
|
||||
|
||||
# name of the social login used, could be facebook, google or github
|
||||
social = db.Column(db.String(128), nullable=False)
|
||||
|
||||
__table_args__ = (db.UniqueConstraint("user_id", "social", name="uq_social_auth"),)
|
||||
|
||||
|
||||
# <<< OAUTH models >>>
|
||||
|
||||
|
||||
|
|
50
migrations/versions/2020_022722_75093e7ded27_.py
Normal file
50
migrations/versions/2020_022722_75093e7ded27_.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 75093e7ded27
|
||||
Revises: e3cb44b953f2
|
||||
Create Date: 2020-02-27 22:26:25.068117
|
||||
|
||||
"""
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '75093e7ded27'
|
||||
down_revision = 'e3cb44b953f2'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('social_auth',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('created_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=False),
|
||||
sa.Column('updated_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('social', sa.String(length=128), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='cascade'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('user_id', 'social', name='uq_social_auth')
|
||||
)
|
||||
op.alter_column('users', 'password',
|
||||
existing_type=sa.VARCHAR(length=128),
|
||||
nullable=True)
|
||||
op.alter_column('users', 'salt',
|
||||
existing_type=sa.VARCHAR(length=128),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('users', 'salt',
|
||||
existing_type=sa.VARCHAR(length=128),
|
||||
nullable=False)
|
||||
op.alter_column('users', 'password',
|
||||
existing_type=sa.VARCHAR(length=128),
|
||||
nullable=False)
|
||||
op.drop_table('social_auth')
|
||||
# ### end Alembic commands ###
|
|
@ -39,8 +39,9 @@ def test_construct_url():
|
|||
def test_authorize_page_non_login_user(flask_client):
|
||||
"""make sure to display login page for non-authenticated user"""
|
||||
user = User.create("test@test.com", "test user")
|
||||
client = Client.create_new("test client", user.id)
|
||||
db.session.commit()
|
||||
|
||||
client = Client.create_new("test client", user.id)
|
||||
db.session.commit()
|
||||
|
||||
r = flask_client.get(
|
||||
|
|
Loading…
Reference in a new issue