mirror of
https://github.com/simple-login/app.git
synced 2024-09-30 05:31:30 +02:00
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:
|
elif not user.activated:
|
||||||
return jsonify(error="Account not activated"), 400
|
return jsonify(error="Account not activated"), 400
|
||||||
|
|
||||||
|
return jsonify(**auth_payload(user, device)), 200
|
||||||
|
|
||||||
|
|
||||||
|
def auth_payload(user, device) -> dict:
|
||||||
ret = {
|
ret = {
|
||||||
"name": user.name,
|
"name": user.name,
|
||||||
"mfa_enabled": user.enable_otp,
|
"mfa_enabled": user.enable_otp,
|
||||||
@ -64,4 +68,4 @@ def auth_login():
|
|||||||
ret["mfa_key"] = None
|
ret["mfa_key"] = None
|
||||||
ret["api_key"] = api_key.code
|
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.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import User
|
from app.models import User, SocialAuth
|
||||||
from .login_utils import after_login
|
from .login_utils import after_login
|
||||||
from ...email_utils import can_be_used_as_personal_email, email_already_used
|
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 :)
|
# reset the next_url to avoid user getting redirected at each login :)
|
||||||
session.pop("facebook_next_url", None)
|
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)
|
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.email_utils import can_be_used_as_personal_email, email_already_used
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import User
|
from app.models import User, SocialAuth
|
||||||
from app.utils import encode_url
|
from app.utils import encode_url
|
||||||
|
|
||||||
_authorization_base_url = "https://github.com/login/oauth/authorize"
|
_authorization_base_url = "https://github.com/login/oauth/authorize"
|
||||||
@ -105,6 +105,10 @@ def github_callback():
|
|||||||
|
|
||||||
flash(f"Welcome to SimpleLogin {user.name}!", "success")
|
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
|
# The activation link contains the original page, for ex authorize page
|
||||||
next_url = request.args.get("next") if request.args else None
|
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.config import URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, DISABLE_REGISTRATION
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
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 app.utils import random_string
|
||||||
from .login_utils import after_login
|
from .login_utils import after_login
|
||||||
from ...email_utils import can_be_used_as_personal_email, email_already_used
|
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 :)
|
# reset the next_url to avoid user getting redirected at each login :)
|
||||||
session.pop("google_next_url", None)
|
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)
|
return after_login(user, next_url)
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,8 +102,10 @@ class AliasGeneratorEnum(enum.Enum):
|
|||||||
class User(db.Model, ModelMixin, UserMixin):
|
class User(db.Model, ModelMixin, UserMixin):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
email = db.Column(db.String(256), unique=True, nullable=False)
|
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)
|
name = db.Column(db.String(128), nullable=False)
|
||||||
is_admin = db.Column(db.Boolean, nullable=False, default=False)
|
is_admin = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
alias_generator = db.Column(
|
alias_generator = db.Column(
|
||||||
@ -156,11 +158,9 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||||||
def create(cls, email, name, password=None, **kwargs):
|
def create(cls, email, name, password=None, **kwargs):
|
||||||
user: User = super(User, cls).create(email=email, name=name, **kwargs)
|
user: User = super(User, cls).create(email=email, name=name, **kwargs)
|
||||||
|
|
||||||
if not password:
|
if password:
|
||||||
# set a random password
|
user.set_password(password)
|
||||||
password = random_string(20)
|
|
||||||
|
|
||||||
user.set_password(password)
|
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
||||||
# create a first alias mail to show user how to use when they login
|
# 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
|
self.password = password_hash
|
||||||
|
|
||||||
def check_password(self, password) -> bool:
|
def check_password(self, password) -> bool:
|
||||||
|
if not self.password:
|
||||||
|
return False
|
||||||
password_hash = bcrypt.hashpw(password.encode(), self.salt.encode())
|
password_hash = bcrypt.hashpw(password.encode(), self.salt.encode())
|
||||||
return self.password.encode() == password_hash
|
return self.password.encode() == password_hash
|
||||||
|
|
||||||
@ -351,6 +353,17 @@ class ResetPasswordCode(db.Model, ModelMixin):
|
|||||||
return self.expired < arrow.now()
|
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 >>>
|
# <<< 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):
|
def test_authorize_page_non_login_user(flask_client):
|
||||||
"""make sure to display login page for non-authenticated user"""
|
"""make sure to display login page for non-authenticated user"""
|
||||||
user = User.create("test@test.com", "test 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()
|
db.session.commit()
|
||||||
|
|
||||||
r = flask_client.get(
|
r = flask_client.get(
|
||||||
|
Loading…
Reference in New Issue
Block a user