Reputation: 909
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
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