Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Differentiate between sign up and sign in? #365

Open
MRB60 opened this issue May 23, 2021 · 1 comment
Open

Differentiate between sign up and sign in? #365

MRB60 opened this issue May 23, 2021 · 1 comment

Comments

@MRB60
Copy link

MRB60 commented May 23, 2021

Probably an old issue but due to some legal aspects I might want to make a difference between sign up/register and sign in/log in.
Is there a way to e,g. pass a client or consumer reference in the request(s) to provider which later can be accessed in @oauth_authorized.connect_via() ? This to know if request was issued from sign up or a sign in. I want to avoid automatic creation of a new user in case sign in was requested from the client.

At the moment I trigger the process for both cases with the same URL with no parameters.

@janpeterka
Copy link

janpeterka commented Mar 27, 2023

Hi, I was looking into this and didn't find a solution anywhere, so I came up with my own solution.

Main thing is creating three endpoints that only save value to session and redirect to blueprint.login. Callback logic then loads that and does it's thing:

from flask import flash
from flask_security import current_user, login_user, url_for_security
from flask_dance.contrib.google import make_google_blueprint
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage
from flask import redirect, url_for, session
from app import db
from .models import OAuth


def create_module(application):
    application.register_blueprint(blueprint, url_prefix="/oauth/")
    application


blueprint = make_google_blueprint(
    scope=["profile", "email"],
    storage=SQLAlchemyStorage(OAuth, db.session, user=current_user),
)


@blueprint.route("/google/login")
def auth_google_login():
    session["auth_type"] = "login"
    return redirect(url_for("google.login"))


@blueprint.route("/google/register")
def auth_google_register():
    session["auth_type"] = "register"
    return redirect(url_for("google.login"))


@blueprint.route("/google/add")
def auth_google_add_to_account():
    session["auth_type"] = "add"
    return redirect(url_for("google.login"))


# create/login local user on successful OAuth login
@oauth_authorized.connect_via(blueprint)
def google_logged_in(blueprint, token):
    auth_type = session.pop("auth_type", "login")

    if not token:
        flash("Failed to log in.", category="error")
        return False

    resp = blueprint.session.get("/oauth2/v1/userinfo")
    if not resp.ok:
        msg = "Failed to fetch user info."
        flash(msg, category="error")
        return False

    info = resp.json()
    email = info["email"]
    user_id = info["id"]
    name = info["name"]
    oauth = OAuth.query.filter_by(provider=blueprint.name, provider_user_id=user_id).first()  # fmt: skip

    if auth_type == "login":
        if not oauth:
            flash("not registered with Google, sorry")
            return redirect(url_for_security("login"))
        else:
            login_user(oauth.user)

    elif auth_type == "register":
        if not oauth:
            oauth = OAuth(
                provider=blueprint.name,
                provider_user_id=user_id,
                token=token,
                username=email,
            )

        # Create a new local user account for this user
        from app.services import UserManager

        user = UserManager.create_or_load_user(email=email, password="x", name=name)
        # Associate the new local user account with the OAuth token
        oauth.user = user
        # Save and commit our database models
        db.session.add_all([user, oauth])
        db.session.commit()
        # Log in the new local user account
        login_user(user)
        # flash("Successfully signed in.")

    elif auth_type == "add":
        if oauth and oauth.user == current_user:
            flash("You already connected google account.", "error")
            return redirect(url_for("MemberSettingView:index"))
        elif oauth:
            flash("You cannot use this google account.", "error")
            return redirect(url_for("MemberSettingView:index"))
        else:
            oauth = OAuth(
                provider=blueprint.name,
                provider_user_id=user_id,
                token=token,
                username=email,
            )

        oauth.user = current_user
        db.session.add(oauth)
        db.session.commit()

        flash("You can use Google to login to your account")
        return redirect(url_for("MemberSettingView:index"))

    # Disable Flask-Dance's default behavior for saving the OAuth token
    return False


# notify on OAuth provider error
@oauth_error.connect_via(blueprint)
def google_error(blueprint, message, response):
    msg = "OAuth error from {name}! message={message} response={response}".format(
        name=blueprint.name, message=message, response=response
    )
    flash(msg, category="error")

I can imagine this being implemented into blueprints directly - having three endpoints and three callbacks (or less based on config).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants