VickTree
VickTree

Reputation: 909

firebase auth not connecting to the appengine project to log users in

Originally i had an appengine web app with users. I wanted to migrate the auth to firebase. I followed the tutorial (https://cloud.google.com/appengine/docs/standard/python/authenticating-users-firebase-appengine) and have the basic components set up. But when i click log-in, it asks for me to sign in but does not log me into the web app. In the firebase console, i connected the project i had with appengine. Now, the user is resisted in the firebase database. But i dont want that.

Does firebase auth not automatically connect to app-engine and varify/register users? What is the additional configurations i need to make. The project is a python backend application.

My Orginal Google Login Handler

class GoogleSigninHandler(NoahSiteHandler):
""" Google sign-in & register redirect """

#@decorator.oauth_aware
@decorator.oauth_required
def get(self):
    try:
        user = None
        login = None
        is_new_account = False
        is_new_link = False


        http = decorator.http()
        google_user = plus_service.people().get(userId='me').execute(http=http)
        logging.info("user data: %s" % google_user)
        if google_user:
            google_id = google_user.get('id')
            displayName = google_user.get('displayName')
            email = None
            emails = google_user.get('emails', [])
            if len(emails) > 0:
                email = emails[0].get('value')
            username = google_user.get('name', {}).get('givenName') or google_user.get('displayName')
            if email and not username:
                username = email.split('@')[0]
            image_url = None
            image_data = google_user.get('image')
            if image_data:
                is_default_image = image_data.get('isDefault', True)
                if not is_default_image:
                    image_url = image_data.get('url')
                    if image_url:
                        image_url = image_url.replace('sz=50', 'sz=200')

            if google_id:
                profile_key = 'https://www.google.com/profiles/%s' % google_id

                login = NoahLogin.get_by_key_name(profile_key)
                if login and login.user:
                    user = login.user
                    logging.info('logging existing user in')
                else:
                    if not login:
                        logging.info('creating new login with key: %s' % profile_key)

                        is_new_link = True
                        login = NoahLogin(
                                    key_name=profile_key,
                                    provider='Google',
                                    email=email,
                                    preferredUsername=username,
                                    displayName=displayName,
                                    photo=image_url
                                )

                    if self.session.has_key('me'):
                        user = NoahUser.smart_get(self.session['me'])
                        if user:
                            login.user = user
                            logging.info('adding google link to noahuser: %s based on session cookie' % login.user.nick)
                            login.put()

                    if not user:
                        #Search for legacy user based on email
                        logging.info('searching for legacy user with email %s', email)

                        #then by email
                        if email:
                            legacy_matches = NoahUser.all().filter('email =', email).fetch(1)
                            if len(legacy_matches) > 0:
                                #legacy Google user
                                logging.info('linking google accout to user with email: %s' % email)
                                login.user = legacy_matches[0]
                                user = login.user

                    if not user:
                        logging.info('creating new user account from google')

                        is_new_account = True
                        is_new_link = True
                        source = None
                        ua = self.request.headers['User-Agent']
                        if ua.find('Android') != -1:
                            source = 'android'
                        elif ua.find('Darwin') != -1:
                            source = 'iphone'
                        else:
                            source = 'web'

                        user = NoahUser(
                                    source=source,
                                    email=email,
                                    nick=unique_username_from(username or displayName)
                        )
                        user.put()

                    login.user = user
                    login.put()
                    taskqueue.add(url='/task/avatar/fetch', params = {'login': login.key()})

        if user and is_new_account:
            Event.new_user(user)
            user.deferred_update(source='new_account')
            sync_user_with_campaign_monitor(user)

        if user:
            self.session['me'] = str(user.key())
            self.request.registry['current_user'] = user

        if is_new_account:
            exit_url = self.url_for('index', new_account='1')
        elif is_new_link:
            exit_url = self.url_for('my_settings')
        else:
            exit_url = self.url_for('index')

        logging.info('redirecting to: %s' % exit_url)
        self.redirect(exit_url)

    except client.AccessTokenRefreshError:
      self.redirect(self.url_for('signin_google'))

Upvotes: 0

Views: 328

Answers (1)

Niklas Rosencrantz
Niklas Rosencrantz

Reputation: 26655

I had a similar problem. Users were already registered with my appengine app and a custom user model (originally from webapp2). I could then add Firebase authentication and then match the credentials from Firebase with my user model. The following is my login template if it can help you (it uses flask framework with the python 3 runtime):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>FirebaseUI</title>

    <!-- The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-analytics.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-firestore.js"></script>

    <script>
        // Your web app's Firebase configuration
        var firebaseConfig = {
            apiKey: "0123456789apikey",
            authDomain: "myappname.firebaseapp.com",
            databaseURL: "https://myappname.firebaseio.com",
            projectId: "myappname",
            storageBucket: "myappname.appspot.com",
            messagingSenderId: "01234567890",
            appId: "1:01234567890:web:21ee025bb5fb02b11337",
            measurementId: "F-ZZ34N242F8"
        };
        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        firebase.analytics();
    </script>

    <script src="https://cdn.firebase.com/libs/firebaseui/4.2.0/firebaseui.js"></script>
    <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/4.2.0/firebaseui.css"/>
    <script type="text/javascript">
        // FirebaseUI config.
        var uiConfig = {
            signInSuccessUrl: '/',
            signInOptions: [
                // Comment out any lines corresponding to providers you did not check in
                // the Firebase console.

                {
                    provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
                    signInMethod: firebase.auth.EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD
                },

                firebase.auth.GoogleAuthProvider.PROVIDER_ID,
                //firebase.auth.EmailAuthProvider.PROVIDER_ID,
                //firebase.auth.FacebookAuthProvider.PROVIDER_ID,
                //firebase.auth.TwitterAuthProvider.PROVIDER_ID,
                //firebase.auth.GithubAuthProvider.PROVIDER_ID,
                //firebase.auth.PhoneAuthProvider.PROVIDER_ID

            ],
            // Terms of service url.
            tosUrl: 'https://www.example.com',

            privacyPolicyUrl: function () {
                window.location.assign('https://www.example.com');
            }
        };


        // Initialize the FirebaseUI Widget using Firebase.
        //var ui = new firebaseui.auth.AuthUI(firebase.auth());
        // The start method will wait until the DOM is loaded.
        //ui.start('#firebaseui-auth-container', uiConfig);


        window.addEventListener('load', function () {
            document.getElementById('sign-out').onclick = function () {
                firebase.auth().signOut();
            };

            firebase.auth().onAuthStateChanged(function (user) {
                // alert("statechanged")
                if (user) {
                    //alert("user logged in")

                    // User is signed in, so display the "sign out" button and login info.
                    document.getElementById('sign-out').hidden = false;
                    document.getElementById('login-info').hidden = false;
                    console.log(`Signed in as (${user.email})`);
                    user.getIdToken().then(function (token) {
                        // Add the token to the browser's cookies. The server will then be
                        // able to verify the token against the API.
                        // SECURITY NOTE: As cookies can easily be modified, only put the
                        // token (which is verified server-side) in a cookie; do not add other
                        // user information.
                        document.cookie = "token=" + token;
                    });
                } else {
                    // User is signed out.
                    // Initialize the FirebaseUI Widget using Firebase.
                    var ui = new firebaseui.auth.AuthUI(firebase.auth());
                    // Show the Firebase login button.
                    ui.start('#firebaseui-auth-container', uiConfig);
                    // Update the login state indicators.
                    document.getElementById('sign-out').hidden = true;
                    document.getElementById('login-info').hidden = true;
                    // Clear the token cookie.
                    document.cookie = "token=";
                }
            }, function (error) {
                console.log(error);
                alert('Unable to log in: ' + error)
            });
        });


    </script>
</head>
<body>
<!-- The surrounding HTML is left untouched by FirebaseUI.
     Your app may use that space for branding, controls and other customizations.-->
<h1>Welcome</h1>
<div id="firebaseui-auth-container"></div>
<button id="sign-out" hidden=true>Sign Out</button>
<div id="login-info" hidden=true>
    <h2>Login info:</h2>
    {% if user_data %}
        <dl>
            <dt>Name</dt>
            <dd>{{ user_data['name'] }}</dd>
            <dt>Email</dt>
            <dd>{{ user_data['email'] }}</dd>
        </dl>
    {% elif error_message %}
        <p>Error: {{ error_message }}</p>
    {% endif %}
</div>
</body>
</html>

Then the backend with python it is quite easy to get the user credential object "claims" (which will be a JSON object), which contains the email address which can be matched with a user profile already in my storage:

from google.oauth2 import id_token as gid_token
from flask import Flask, render_template, request, redirect, g

@app.before_request
def before_request_func():
    id_token = request.cookies.get("token")
    system_info = None
    error_message = None
    claims = None
    if id_token:
        try:
            claims = gid_token.verify_firebase_token(
                id_token, firebase_request_adapter)
            g.friendly_id = claims.get('name', claims.get('email', 'Unknown'))
        except ValueError as exc:
            error_message = str(exc)
    g.error_message = error_message  # Make a list of errors
    g.system_info = system_info
    g.claims = claims

Now the claims object is available in any request handler:

@app.route("/my-page.html")
def my_ads():
    if g.claims is None:
        return redirect(f"/login.html", code=302)
    else:
        claims = g.claims
        email = claims.get("email")
    ...

If you still have problems and want to try the above then I could create a sample project open on GitHub or similar.

Upvotes: 2

Related Questions