1+ #!/usr/bin/env python3
12# Copyright 2021 Google LLC
23#
3- # Licensed under the Apache License, Version 2.0 (the "License");
4- # you may not use this file except in compliance with the License.
5- # You may obtain a copy of the License at
4+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+ # use this file except in compliance with the License. You may obtain a copy of
6+ # the License at
67#
78# http://www.apache.org/licenses/LICENSE-2.0
89#
910# Unless required by applicable law or agreed to in writing, software
10- # distributed under the License is distributed on an "AS IS" BASIS,
11- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12- # See the License for the specific language governing permissions and
13- # limitations under the License.
11+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+ # License for the specific language governing permissions and limitations under
14+ # the License.
15+ """The Flask server application. Defines routes and ancillary methods.
16+
17+ Ensure that you select an approprate run strategy at the bottom of this file.
18+ """
1419
1520import os
1621import flask
2126import google_auth_oauthlib .flow
2227import googleapiclient .discovery
2328
29+ # Instantiate the Flask application.
2430app = flask .Flask (__name__ )
2531
26- # Note: A secret key is included in the sample so that it works.
27- # If you use this code in your application, replace this with a truly secret
28- # key. See https://flask.palletsprojects.com/quickstart/#sessions.
32+ # Note: A secret key is included in the sample so that it works. If you use this
33+ # code in your application, replace this with a truly secret key. See
34+ # https://flask.palletsprojects.com/quickstart/#sessions.
2935app .secret_key = "REPLACE ME - this value is here as a placeholder."
3036
3137# Configure the flask cookie settings per the iframe security recommendations:
4248
4349# This OAuth 2.0 access scope allows for full read/write access to the
4450# authenticated user's account and requires requests to use an SSL connection.
45- # These scopes should match the scopes in your GCP project's
46- # OAuth Consent Screen: https://console.cloud.google.com/apis/credentials/consent
51+ # These scopes should match the scopes in your GCP project's OAuth Consent
52+ # Screen: https://console.cloud.google.com/apis/credentials/consent
4753SCOPES = [
4854 "https://www.googleapis.com/auth/userinfo.profile" ,
4955 "https://www.googleapis.com/auth/userinfo.email"
5460def index ():
5561 """
5662 Render the index page from the "index.html" template. This is meant to act
57- as a facsimile of a company's home page.
58- The Add-on Discovery URL should be set to the /classroom-addon route below.
63+ as a facsimile of a company's home page. The Add-on Discovery URL should be
64+ set to the /classroom-addon route below.
5965 """
6066
61- return flask .render_template ("index.html" ,
62- message = "You've reached the index page." )
67+ return flask .render_template (
68+ "index.html" , message = "You've reached the index page." )
6369
6470
6571@app .route ("/classroom-addon" )
6672def classroom_addon ():
6773 """
6874 Checks if a user is signed in. If so, renders the addon discovery page from
6975 the "addon-discovery.html" template. This is meant to be the landing page
70- when opening the web app in the Classroom add-on iframe.
71- Otherwise, renders the "authorization.html" template.
76+ when opening the web app in the Classroom add-on iframe. Otherwise, renders
77+ the "authorization.html" template.
7278 """
7379
7480 if "credentials" not in flask .session :
@@ -82,10 +88,11 @@ def classroom_addon():
8288@app .route ("/test/<request_type>" )
8389def test_api_request (request_type = "username" ):
8490 """
85- Tests an API request, rendering the result in the "show-api-query-result.html" template.
91+ Tests an API request, rendering the result in the
92+ "show-api-query-result.html" template.
8693
87- Args:
88- request_type: The type of API request to test. Currently only "username" is supported.
94+ Args: request_type: The type of API request to test. Currently only
95+ "username" is supported.
8996 """
9097
9198 if "credentials" not in flask .session :
@@ -110,13 +117,14 @@ def test_api_request(request_type="username"):
110117
111118 # Save credentials back to session in case access token was refreshed.
112119 # ACTION ITEM: In a production app, you likely want to save these
113- # credentials in a persistent database instead.
120+ # credentials in a persistent database instead.
114121 flask .session ["credentials" ] = credentials_to_dict (credentials )
115122
116123 # Render the results of the API call.
117- return flask .render_template ("show-api-query-result.html" ,
118- data = json .dumps (fetched_data , indent = 2 ),
119- data_title = request_type )
124+ return flask .render_template (
125+ "show-api-query-result.html" ,
126+ data = json .dumps (fetched_data , indent = 2 ),
127+ data_title = request_type )
120128
121129
122130@app .route ("/authorize" )
@@ -125,14 +133,15 @@ def authorize():
125133 Initializes the OAuth flow and redirects to Google's authorization page.
126134 """
127135
128- # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
136+ # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow
137+ # steps.
129138 flow = google_auth_oauthlib .flow .Flow .from_client_secrets_file (
130139 CLIENT_SECRETS_FILE , scopes = SCOPES )
131140
132- # The URI created here must exactly match one of the authorized redirect URIs
133- # for the OAuth 2.0 client, which you configured in the API Console. If this
134- # value doesn't match an authorized URI, you will get a "redirect_uri_mismatch"
135- # error.
141+ # The URI created here must exactly match one of the authorized redirect
142+ # URIs for the OAuth 2.0 client, which you configured in the API Console. If
143+ # this value doesn't match an authorized URI, you will get a
144+ # "redirect_uri_mismatch" error.
136145 flow .redirect_uri = flask .url_for ("callback" , _external = True )
137146
138147 authorization_url , state = flow .authorization_url (
@@ -153,7 +162,8 @@ def authorize():
153162def callback ():
154163 """
155164 Runs upon return from the OAuth 2.0 authorization server. Fetches and stores
156- the user's credentials, including the access token, refresh token, and allowed scopes.
165+ the user's credentials, including the access token, refresh token, and
166+ allowed scopes.
157167 """
158168
159169 # Specify the state when creating the flow in the callback so that it can be
@@ -168,9 +178,8 @@ def callback():
168178 authorization_response = flask .request .url
169179 flow .fetch_token (authorization_response = authorization_response )
170180
171- # Store credentials in the session.
172- # ACTION ITEM: In a production app, you likely want to save these
173- # credentials in a persistent database instead.
181+ # Store credentials in the session. ACTION ITEM: In a production app, you
182+ # likely want to save these credentials in a persistent database instead.
174183 credentials = flow .credentials
175184 flask .session ["credentials" ] = credentials_to_dict (credentials )
176185
@@ -191,9 +200,10 @@ def revoke():
191200 """
192201
193202 if "credentials" not in flask .session :
194- return flask .render_template ("addon-discovery.html" ,
195- message = "You need to authorize before " +
196- "attempting to revoke credentials." )
203+ return flask .render_template (
204+ "addon-discovery.html" ,
205+ message = "You need to authorize before " +
206+ "attempting to revoke credentials." )
197207
198208 credentials = google .oauth2 .credentials .Credentials (
199209 ** flask .session ["credentials" ])
@@ -216,8 +226,8 @@ def revoke():
216226@app .route ("/start-auth-flow" )
217227def start_auth_flow ():
218228 """
219- Starts the OAuth 2.0 authorization flow. It's important that the
220- template be rendered to properly manage popups.
229+ Starts the OAuth 2.0 authorization flow. It's important that the template be
230+ rendered to properly manage popups.
221231 """
222232
223233 return flask .render_template ("authorization.html" )
@@ -270,23 +280,23 @@ def credentials_to_dict(credentials):
270280 ### OPTION 1: Unsecured localhost
271281 # When running locally on unsecured HTTP, use this line to disable
272282 # OAuthlib's HTTPs verification.
283+
273284 # Important: When running in production *do not* leave this option enabled.
274285 os .environ ["OAUTHLIB_INSECURE_TRANSPORT" ] = "1"
275286
276287 # Run the application on a local server, defaults to http://localhost:5000.
277288 # Note: the OAuth flow requires a TLD, *not* an IP address; "localhost" is
278- # acceptable, but http://127.0.0.1 is not.
289+ # acceptable, but http://127.0.0.1 is not.
279290 app .run (debug = True )
280291
281292 ### OPTION 2: Secure localhost
282293 # Run the application over HTTPs with a locally stored certificate and key.
283- # Defaults to https://localhost:5000.
294+ # Defaults to https://localhost:5000.\
284295 # app.run(host="localhost",
285- # ssl_context=("localhost.pem", "localhost-key.pem"),
286- # debug=True)
296+ # ssl_context=("localhost.pem", "localhost-key.pem"), debug=True)
287297
288298 ### OPTION 3: Production- or cloud-ready server
289- # Start a Gunicorn server, which is appropriate for use in
290- # production or a cloud deployment.
299+ # Start a Gunicorn server, which is appropriate for use in production or a
300+ # cloud deployment.
291301 # server_port = os.environ.get("PORT", "8080")
292302 # app.run(debug=True, port=server_port, host="0.0.0.0")
0 commit comments