
Before you begin
- Labs create a Google Cloud project and resources for a fixed time
- Labs have a time limit and no pause feature. If you end the lab, you'll have to restart from the beginning.
- On the top left of your screen, click Start lab to begin
Set up the Python application and necessary resources
/ 15
Create a Book
/ 20
Create OAuth authorization credentials for your application
/ 25
Add login, callback, and logout endpoints
/ 20
Test the Application
/ 20
Cloud Client Libraries are the recommended method for calling Google Cloud APIs from your applications. Cloud Client Libraries use the natural conventions and style of the programming language that you're using for your application. Cloud Client Libraries handle low-level communication with the server, including authentication and retry logic.
Firestore is a fast, fully managed, serverless, NoSQL document database built for automatic scaling, high performance, and ease of application development.
Google APIs use the OAuth 2.0 protocol for authentication and authorization.
Secret Manager lets you store API keys, passwords, certificates, and other sensitive data as binary blobs or text strings.
The Cloud Translation API enables your websites and applications to dynamically translate text programmatically. Cloud Translation can translate text for more than 100 languages, and it can detect the language of source text.
In this lab, you update a Python application that manages a list of books. You add the ability to log in to the application by using OAuth, and require the user to log in when adding, editing, or deleting books.
You also use the Cloud Translation API to translate the book descriptions into a different language. You add a user profile that stores the preferred language for the user.
In this lab, you learn to:
This Qwiklabs hands-on lab lets you do the lab activities yourself in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials that you use to sign in and access Google Cloud for the duration of the lab.
To complete this lab, you need:
Click the Start Lab button. If you need to pay for the lab, a pop-up opens for you to select your payment method. On the left is a panel populated with the temporary credentials that you must use for this lab.
Copy the username, and then click Open Google Console. The lab spins up resources, and then opens another tab that shows the Choose an account page.
On the Choose an account page, click Use Another Account. The Sign in page opens.
Paste the username that you copied from the Connection Details panel. Then copy and paste the password.
After a few moments, the Cloud console opens in this tab.
Google Cloud Shell is a virtual machine that is loaded with development tools. It offers a persistent 5GB home directory and runs on the Google Cloud.
Google Cloud Shell provides command-line access to your Google Cloud resources.
In Cloud console, on the top right toolbar, click the Open Cloud Shell button.
Click Continue.
It takes a few moments to provision and connect to the environment. When you are connected, you are already authenticated, and the project is set to your PROJECT_ID. For example:
gcloud is the command-line tool for Google Cloud. It comes pre-installed on Cloud Shell and supports tab-completion.
Output:
Example output:
Output:
Example output:
In this task, you download the Python application and create the resources used by the current version of the app.
To create the Firestore database, in Cloud Shell, run the following command:
The Firestore database is used to store book and user profile data.
If you're asked to authorize Cloud Shell, click Authorize.
To create the Cloud Storage bucket, run the following command:
The Cloud Storage bucket is used to store book cover images. The bucket has uniform bucket level access and does not use public access prevention.
To make all objects in the bucket publicly readable, run the following command:
To verify the objective, click Check my progress.
To copy the Python code from a Cloud Storage bucket into the home directory, run the following command:
To check the contents of the bookshelf directory, run the following command:
You should see a list that contains three Python files, a requirements file, and four template files:
To list the dependencies in the requirements file, run the following command:
The requirements file specifies the following dependencies:
To install the dependencies in the requirements file, run the following command:
pip is the package installer for Python. This pip3
command installs the packages specified in the requirements.txt file for use with Python version 3.
To start the application, run the following command:
If you have successfully created the files, the application should now be hosted on port 8080.
To run the application in the web browser, click Web Preview, and then select Preview on port 8080.
A new tab is opened in the browser, and the application is running. This page displays a list of all existing books. There are no books yet.
Right-click this Wizard of Oz book cover image, and save it to your computer as oz.png
:
In the application tab, click +Add book.
Enter the following information into the form:
Field | Value |
---|---|
Title | Wonderful Wizard of Oz |
Author | Frank L. Baum |
Date Published | 1900 |
Description | A young girl and her dog are carried away to a magical land, where they meet a bunch of unusual people and learn not to stand under houses. |
For Cover Image, click Choose File.
Select the file that you downloaded (oz.png), and click Open.
Click Save.
You're returned to the view page, and your book details are shown.
At the top of the page, click Books.
You're returned to the list page, and Wonderful Wizard of Oz is shown in the list, along with its book cover. The book details are stored in the Firestore database, and the cover image is stored in Cloud Storage.
In Cloud Shell, to quit the application, enter CTRL-C
.
To verify the objective, click Check my progress.
In this task, you create authorization credentials that identify your application to Google's OAuth 2.0 server.
When you use OAuth 2.0 for authorization, your app requests one or more scopes of access from a Google Account. Google displays a consent screen to the user to capture the user's consent to share data with the application.
In the Google Cloud console, select the Navigation menu (), and then select APIs & Services > OAuth consent screen.
This page lets you select the type of users that will use your application. Internal users are users within your organization. External users are any users with a Google Account.
Click Get Started.
For App name, enter Bookshelf
.
For User support email, select the student email.
Click Next.
For Audience, select External, and then click Next.
Users with a test account will be able to log in to the app.
On the left panel of the lab instructions, copy the Username.
For Email addresses, paste the copied username, and then click Next.
Enable the checkbox to agree to the user data policy, and then click Continue.
Click Create.
In the navigation menu, click Branding.
Click + Add Domain.
In the Authorized domains section, for Authorized domain 1, enter cloudshell.dev
.
When the application is running in Cloud Shell, cloudshell.dev
is the domain name.
Click Save.
In the navigation menu, click Data Access.
Next, you need to select scopes that will be requested of users for your application. Scopes express the types of private user data in the user's Google Account that the application would like to access.
There are three types of scopes:
Click Add or Remove Scopes.
A list of scopes is presented.
At the beginning of the list, select the box next to openid.
For Filter, enter userinfo.profile
, press Enter, and then select the box next to the .../auth/userinfo.profile
scope.
For Filter, clear the userinfo.profile filter, enter contacts
, press Enter, and then select the box for the .../auth/contacts
scope.
Click Update.
You should see two non-sensitive scopes (openid and userinfo.profile), and one sensitive scope (contacts).
Click Save.
In the navigation menu, click Audience.
Test users are required when users are external and publishing status is set to Testing.
Click + Add Users.
On the left panel of the lab instructions, copy the Username again.
In the Add users pane, paste the copied username into the box, and then click Save.
In the navigation menu, click Clients, and then click + Create Client.
For Application type, select Web application.
For Name, enter Bookshelf
.
For Authorized redirect URIs, click + Add URI.
The URI specified here will be used when Google redirects the browser back to the application after capturing user consent.
To get the redirect URI, in Cloud Shell, run the following command:
Copy the URI that was created by the echo command, and then, for URIs 1, paste in the URI.
Click Create.
For the Bookshelf OAuth 2.0 Client ID, in the Actions column, click Download OAuth client ().
Click Download JSON, and then save the client secret JSON to your local machine.
The client secret file will be used to verify your app with Google.
Click Close.
In Cloud Shell, click More () in the top-right toolbar, and then click Upload.
Click Choose Files, and select the client secret JSON file, and then click Open.
Click Upload.
The client secret JSON file is now available in the home directory. The contents of this file will be used during the OAuth process.
In Cloud Shell, run the following command:
The JSON contents include the client_secret, which should be treated like a password. For example, you never want to store this JSON file into a code repository.
Secret Manager is a secure and recommended place to store the client secret JSON file.
To enable the Secret Manager API, run the following command:
To rename the client secret file, run the following command:
To create the secret, run the following command:
There is now a secret named bookshelf-client-secrets
that can be accessed from your application.
Another secret value that is needed for your application is the Flask secret key, which is used to sign information in cookies.
To create a secret for the Flask secret key, run the following command:
This command creates a random 20 character alphanumeric password, and then stores it in a secret named flask-secret-key
.
To verify the objective, click Check my progress.
In this task, you modify the application to use Secret Manager.
Open the file called requirements.txt
with nano using the following command:
In the requirements file, use the down arrow to move to the first empty line, and then add the following line:
The requirements.txt file should now look like this:
To save the file and exit, click CTRL-X, click Y
, and then click Enter.
To install the updated versions of the dependencies, run the following command:
The secrets.py
file contains code to retrieve secrets from Secret Manager.
To create the secrets.py
file, run the following command:
The get_secret()
function accepts a secret ID and an optional version ID. The requested secret is returned from the function.
The main code file should call Secret Manager to retrieve secrets.
In a file editor, open the file ~/bookshelf/main.py
.
After the import for storage
, add the following line:
This line imports the secrets.py file you just created.
In the app.config.update function call, change the SECRET_KEY from:
to:
The Flask secret key is no longer stored in the application code.
Save the file.
In this task, you add functions that manage the OAuth login flow.
When a user logs in to the web application, the app will start the OAuth authorization sequence. OAuth allows the user to authenticate and consent to access being requested by the application. The OAuth authorization sequence looks like this:
The authorization sequence begins when the application redirects the browser to a Google URL. Google handles the user authentication, session selection, and user consent. The result is an authorization code, which the application can exchange for an access token and a refresh token.
The application stores the refresh token for future use, and uses the access token to access Google APIs. The Bookshelf application calls a Google API to retrieve information about the user. When the access token expires, the application uses the refresh token to obtain a new access token.
The Python OAuth client for Google requires three more Python packages.
In the ~/bookshelf/requirements.txt
file, add the following lines:
The requirements.txt file should now look like this:
Save the file.
To install the updated versions of the dependencies, run the following command:
The oauth.py
file contains code to retrieve an OAuth token from Google.
To create the oauth.py
file, run the following command:
The authorize()
function starts the authorization sequence. It configures the flow by using the passed in client_config parameter, which will be built from the OAuth configuration JSON string stored as a secret. The callback_uri specifies where Google will call with the authorization code. This URI must match a URI configured as an authorized redirect URI for the Bookshelf application. The flow.authorization_url()
call builds the full URL for the redirection to Google. A state is created and passed in, and it will be stored in the session to match this call with the eventual callback. The authorization URL and state are returned to the caller.
The handle_callback()
function is used when the callback is received from Google. The state specified in the callback URL must match the stored state that was sent in the authorization URL. The flow.fetch_token()
call can then be used to fetch the credentials, including the refresh and access tokens. The returned credentials are then used to call Google and receive user info for the logged-in user. The credentials and user info are then returned to the caller.
In this task, you use the OAuth functions you just created to implement login, logout, and callback endpoints.
To create an error template, run the following command:
When logging in, the user might encounter an error. This page will be used to display the error.
In a file editor, open the file ~/bookshelf/templates/base.html
.
Login and logout links will be added to the application.
In the navbar section, on the line after the closing tag (/ul) of the unordered list (ul) with class="nav navbar-nav"
, add the following section:
The file will now look like this:
The base template now uses the session to check if the user is logged in. If the user is logged in, the user's email address and a logout link are shown. If the user is not logged in, a login link is shown.
Save the file.
In a file editor, open the file ~/bookshelf/main.py
.
Add session
to the flask imports.
The flask imports should now look like this:
A session will provide access to information that is associated with the logged-in user. The session data will be stored in cookies.
After the cloud_logging import
line, add the following lines:
In main.py
, the json
library is used to convert the client secret string to a mapping (object), and the os
library is used to use environment variables. The urlparse()
function will be used to replace the scheme and hostname in a URL.
After the import for secrets
, add the following line:
This imports the OAuth functions you created.
In the app.config.update()
function call, add the following lines after the ALLOWED_EXTENSIONS
line:
The following configuration items are added:
CLIENT_SECRETS
contains a mapping (object) for the OAuth configuration that was stored in Secret Manager.SCOPES
contains a list of scopes to be requested.EXTERNAL_HOST_URL
is used to determine the callback URL. When you use Web Preview with Cloud Shell, the application set to run as localhost (127.0.0.1) port 80 is exposed to the internet on https://8080-...-cloudshell.dev
. This URL will be used to convert the localhost URL for the callback endpoint to the publicly accessible URL. The value will be passed in as an environment variable.After the log_request()
function (which starts with def log_request():
), add the following functions:
The logout_session()
function clears known session entries.
The external_url()
function replaces the scheme and hostname of a URL with a different hostname for external access. If the replacement hostname is not specified, the function will ensure that the returned URL is using https.
The /error
endpoint is used to display an error.
The /login
endpoint checks the session to see if a user is logged in. A user is logged in if credentials are stored in the session. If the user is not logged in, oauth.authorize()
is called to get the authorization URL and state for the redirection to Google. The state is saved in the session, and then the browser is redirected to the authorization URL.
The /oauth2callback
endpoint is called by Google during the authorization process. If there was an error, then the process was unsuccessful, and the user is redirected to the error page. If not an error, handle_callback()
is called to retrieve the token and user information. At the end of the process, the user is redirected to the previous page where the login was automatically started, or to the root page (book list) if there is no return location.
The /logout
endpoint logs out the user by removing credentials and user data from the session, and returns to the root page.
A user can browse the books on the bookshelf without being logged in. However, it makes sense to force a user to log in before they can modify the books in any way.
When a user tries to add, edit, or delete a book, but they are not logged in, they should be forced to login.
In the add()
function, directly after the call to log_request()
, add the following lines:
In add()
, if the user is not logged in, they are returned to the add page after login.
In the edit()
function, directly after the call to log_request()
, add the following lines:
In edit()
, if the user is not logged in, they are returned to the edit page for this book after login.
In the delete()
function, directly after the call to log_request()
, add the following lines:
In delete()
, if the user is not logged in, they are returned to the view page for this book after login.
Save the file.
To start the HTTP server, in Cloud Shell, run the following command:
There is an environment variable being passed in to the application:
EXTERNAL_HOST_URL
specifies the scheme and hostname that should be used in the callback URL. If this environment variable is not specified, the redirect_uri passed to Google in the authorization URL will use the hostname that the application sees in incoming URLs: 127.0.0.1:8080
, which is localhost. Web Preview forwards requests from the cloudshell.dev URL to localhost (http://127.0.0.1:8080).To open the application in the web browser, click Web Preview, and then select Preview on port 8080.
A new tab is opened in the browser, and the application is running. You should see the Wizard of Oz book. The user is not logged in.
Click + Add book.
You must be logged in to add a book, so you're asked to choose an account for signing in with Google:
Click the student email, and then click Continue.
Google will now obtain consent for any restricted or non-sensitive scopes that are being requested. In this case, downloading all of your contacts is a sensitive scope.
Click Allow.
You're returned to the application on the Add book page. You're logged in, with your email in the upper right corner. If you look at the logs in Cloud Shell, you should see the callback call from Google:
The user consented to access, so the parameter named code
has the authorization code that was subsequently exchanged for the credentials. The scopes allowed are also returned.
Click Books, and then click + Add Book.
You're already logged in, so you can go directly to the Add book page.
Click Logout, and then click Login.
Click the student email, then click Continue, and then click Cancel.
You're returned to the error page. Look at the logs in Cloud Shell. You should see the callback made by Google, which should look something like this:
In this case, the consent was not provided, so the authorization code was not returned and the application cannot get credentials for the user.
In Cloud Shell, to quit the application, enter CTRL-C
.
To verify the objective, click Check my progress.
In this task, you create functions that use the Cloud Translation API to detect text language and translate text.
Calls to the Cloud Translation API use the credentials of the application.
The application requires another Python package.
In the ~/bookshelf/requirements.txt
file, add the following line:
The requirements.txt file should now look like this:
Save the file.
To install the updated dependencies, run the following command:
The translate.py
file contains code to perform translation.
To create the translate.py
file, run the following command:
The get_languages()
function retrieves the list of languages supported by the Cloud Translation API. Each language in the list contains an ID (language_code) and the display text (display_name).
The detect_language()
function detects the language for a text string.
The translate_language()
function translates text into a specified language.
In this task, you create a user profile for logged-in users. A preferred language can be selected for the user.
The profile will be stored in a Firestore collection named profiles
. A default profile with a preferred language of English will be used until the user updates the profile.
The profiledb.py
file contains code to read and update user profiles. The email address of the user will be used as the profile key. In this implementation, the only item in the profile will be the preferred language.
To create the profiledb.py
file, run the following command:
The read()
function retrieves a profile for a specified user. If a profile is not found, a copy of the default profile is returned.
The read_entry()
function returns a single value from a user's profile. If the key is not found in the user's profile, the passed in default value is returned instead.
The update()
function creates or overwrites the user's profile with the specified data.
To create a new template for the user profile in profile.html
, run the following command:
This template creates a single form with a select control and a submit button. The select control is loaded with all of the languages passed in using the languages
list variable. The value of each entry is the language_code
, and the display_name
is shown in the select control. The initially displayed language is the preferredLanguage
specified in the profile.
In a file editor, open the file ~/bookshelf/main.py
.
After the import for oauth
, add the following lines:
This imports the translate.py and profiledb.py files you just created.
To add the /profile
endpoint, add the following function after the /books/<book_id>/delete
endpoint:
The profile can only be accessed for logged-in users, so the user is redirected to login if not already logged in.
The email of the logged-in user is taken from the user information in the session, and then the current profile is read.
The profile is rendered by using the profile.html template.
After you click the submit button, the profile is updated in the database, the preferred language for the user is stored in the session, and the browser is redirected to the root page.
Save the file.
In a file editor, open the file ~/bookshelf/templates/base.html
.
In this file, change the following line from:
to:
This changes the displayed email address to a clickable link that redirects to the /profile
endpoint.
Save the file.
In a file editor, open the file ~/bookshelf/templates/view.html
.
In this file, change the following line from:
to:
If translation_language is not specified, the description is unchanged. However, if there is a translation language, then the language of the original description is displayed, and the next line shows the translated version with the text. The view endpoint must pass in this extra information.
Save the file.
In a file editor, open the file ~/bookshelf/main.py
.
After the log_request()
function (which starts with def log_request():
), add the following code:
The detect_language() function returns a detected language code, but no display name. This code creates a mapping from language code to display name. This will be used for displaying the detected language on the view template.
Replace the entire view endpoint code with the following:
The code now translates the book's description into the user's preferred language and passes the translation and languages to the template.
Save the file.
To start the HTTP server, run the following command:
To open the application in the web browser, click Web Preview, and then select Preview on port 8080.
A new tab is opened in the browser, and the application is running. You should see the Wizard of Oz book.
If the user is not logged in, click Login, and then log the user in by providing consent.
The email address should now be a link.
Click the email address.
The profile is displayed. The language select control should show English
.
Change the Preferred Language to Swahili, and then click Save.
Click the Wonderful Wizard of Oz book.
The view page now contains both the original English description and the Swahili translation.
To verify the objective, click Check my progress.
You successfully modified an application to use OAuth to log in users. You then added a user profile with a preferred language, and you used the Cloud Translation API to provide translations for book descriptions.
When you have completed your lab, click End Lab. Google Cloud Skills Boost removes the resources you’ve used and cleans the account for you.
You will be given an opportunity to rate the lab experience. Select the applicable number of stars, type a comment, and then click Submit.
The number of stars indicates the following:
You can close the dialog box if you don't want to provide feedback.
For feedback, suggestions, or corrections, please use the Support tab.
Copyright 2024 Google LLC All rights reserved. Google and the Google logo are trademarks of Google LLC. All other company and product names may be trademarks of the respective companies with which they are associated.
This content is not currently available
We will notify you via email when it becomes available
Great!
We will contact you via email if it becomes available
One lab at a time
Confirm to end all existing labs and start this one