Skip to content

Commit

Permalink
Using flask-login extension with Firestore in Datastore mode (GoogleC…
Browse files Browse the repository at this point in the history
…loudPlatform#1416)

* begin tutorial outline

* add new lines and format

* add sample code

* add setting up of local environment

* add tutorial prerequisite

* improve formatting, shorten sentences

* format code

* datastore as drop-in replacement

* add a 'conclusion' section

* fixing frontmatter

* Update index.md

* line edit through introduction

* removing note file

The notes have been incorporated into the main document.

* line edit through selecting service account role

* finished first edit pass

Co-authored-by: Todd Kopriva <43478937+ToddKopriva@users.noreply.github.com>
  • Loading branch information
komlasapaty and ToddKopriva authored Sep 25, 2020
1 parent a17d3bd commit 9e0513d
Showing 1 changed file with 171 additions and 0 deletions.
171 changes: 171 additions & 0 deletions tutorials/using-flask-login-with-cloud-datastore/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: Using Flask-Login with Cloud Firestore in Datastore mode
description: Represent your Cloud Datastore entity with a Python class and use this for Flask-Login user management.
author: komlasapaty
tags: Flask Framework, Python 3
date_published: 2020-09-24
---

In this tutorial, you implement user authentication using the popular Flask extension [Flask-Login](https://flask-login.readthedocs.io) with
[Firestore in Datastore mode](https://cloud.google.com/datastore/docs/datastore-api-tutorial) as the database backend. This tutorial demonstrates the use of
Firestore in Datastore mode (as opposed to any relational database) as the database backend for user authentication with Flask-Login.

The use of Flask-Login should not force you to abandon the power of Firestore in Datastore mode in favor of a relational database.

This tutorial doesn't teach the fundamentals of Flask-Login or Firestore in Datastore mode.

Basic familiarity with the following is assumed:

- Flask
- Flask-Login
- App Engine
- Firestore in Datastore mode

## Requirements

- [Python3.7](https://www.python.org/downloads/)
- [Flask](https://github.com/pallets/flask)
- [Flask-Login](https://flask-login.readthedocs.io)
- [Datastore Entity](https://datastore-entity.readthedocs.io)

## Introduction

Flask-Login requires the user (that is, the application user) to be represented using a Python class with specific properties and methods provided. This
requirement of Flask-Login is straightforward when using a relational database such as MySQL or Postgres. Using an ORM toolkit like SQL-Alchemy, you can easily
create a user model/class to represent a user in a relational database. Methods and properties required by Flask-Login can then be added to the user model.

However, for a NoSQL database like Firestore in Datastore mode, this Flask-Login requirement poses a challenge, because the pattern of using a model/class to
represent a database record does not directly apply.

This tutorial demonstrates how to model your Firestore in Datastore mode entity as a Python class, which lets you use popular Python libraries like
Flask-Login and WTForms.

To model a Datastore entity as a Python class, this tutorial uses the [Datastore Entity library](https://datastore-entity.readthedocs.io). Think of Datastore
Entity as an ORM-like library for Firestore in Datastore mode.

The author of this tutorial is also the author of the Datastore Entity library.

## Set up your local environment

Projects that use the Datastore mode API require an active App Engine application, so you should already have the App Engine and Datastore mode APIs enabled in
your Google Cloud project.

### Create and download service account key

To connect to your Google Cloud service from your local machine, you need the appropriate credentials (service account key).

1. In the Cloud Console, go to the [**Service accounts** page](https://console.cloud.google.com/iam-admin/serviceaccounts).
1. Click **Create service account**.
1. Give the service account an appropriate name, such as `datastore-service-account`, enter a description, and click **Create**.
1. Under **Select a role**, type `Cloud Datastore Owner` in the filter field, select **Cloud Datastore Owner**, click **Continue**, and click **Done**.
1. On the **Service accounts** page, look for the service account that you just created, click the button at the far right end of the row in the
**Actions** column, and select **Create key** in the dropdown menu.
1. Make sure that **JSON** is selected as the **Key type**, and click **Create**.

This automatically downloads the service account JSON key to your local machine. Take note of the filename.

### Configure your service account on your local machine

Point the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the location of the service account key you downloaded:

* Linux or macOS:

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-acount-key.json"

* Windows, with Powershell:

$env:GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-acount-key.json"

* Windows, with Command Prompt:

set GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-acount-key.json"

You are now ready to connect to your Firestore in Datastore mode.

### Install required libraries

Run the following command to install the required libraries:

pip install flask flask-login datastore-entity

Installing `datastore-entity` installs the required official client library for Firestore in Datastore mode.

## Create your Flask user model

Model your `user` entity.

```python
from flask_login import UserMixin
from datastore_entity import DatastoreEntity, EntityValue
import datetime


class User(DatastoreEntity, UserMixin):
username = EntityValue(None)
password = EntityValue(None)
status = EntityValue(1)
date_created = EntityValue(datetime.datetime.utcnow())

# other fields or methods go here...
#def authenticated(self, password):
# ...
```

## Implement the Flask login view

After the entity is modeled, you can fetch your user entity using familiar ORM patterns.

Use the methods provided on your model to fetch the user record.

An example Flask-Login view:

```python
from flask_login import login_user
from models import User

@page.route("/login", methods=['GET','POST'])
def login():
form = LoginForm(next=request.args.get('next'))

if form.validate_on_submit():
identity = request.form.get('username')
password = request.form.get('password')

# fetch user using the 'username' property
# refer to the datastore-entity documentation for more
user = User().get_object('username',identity)

if user and user.authenticated(password):

if login_user(user, remember=True):
user.update_activity()

#handle optionally redirecting to the next URL safely
next_url = form.next.data
if next_url:
return redirect(safe_next_url(next_url))

return redirect(url_for('page/dashboard.html'))
else:
flash('This account is not active','error')

else:
flash('Login or password is incorrect','error')

return render_template("page/login.html", form=form)
```

## Conclusion

You can employ the same approach to model any other Firestore in Datastore mode entity to persist and update data.

This technique can let Firestore in Datastore mode be a drop-in replacement for your relational database with little to no code change.


## What's next

- [Firestore in Datastore mode documentation](https://cloud.google.com/datastore)
- [Flask documentation](https://flask.palletsprojects.com/en/1.1.x/)
- [Flask-Login documentation](https://flask-login.readthedocs.io)
- [Datastore Entity documentation](https://datastore-entity.readthedocs.io)
- [Flask-Login tutorial using Postgres](https://hackersandslackers.com/flask-login-user-authentication)

0 comments on commit 9e0513d

Please sign in to comment.