1+ import functools
2+ import os
3+
4+ import flask
5+
6+ from authlib .client import OAuth2Session
7+ import google .oauth2 .credentials
8+ import googleapiclient .discovery
9+
10+ ACCESS_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
11+ AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'
12+
13+ AUTHORIZATION_SCOPE = 'openid email profile'
14+
15+ AUTH_REDIRECT_URI = os .environ .get ("FN_AUTH_REDIRECT_URI" , default = False )
16+ BASE_URI = os .environ .get ("FN_BASE_URI" , default = False )
17+ CLIENT_ID = os .environ .get ("FN_CLIENT_ID" , default = False )
18+ CLIENT_SECRET = os .environ .get ("FN_CLIENT_SECRET" , default = False )
19+
20+ AUTH_TOKEN_KEY = 'auth_token'
21+ AUTH_STATE_KEY = 'auth_state'
22+ USER_INFO_KEY = 'user_info'
23+
24+ app = flask .Flask (__name__ )
25+ app .secret_key = os .environ .get ("FN_FLASK_SECRET_KEY" , default = False )
26+
27+ @app .route ('/' )
28+ def index ():
29+ if is_logged_in ():
30+ user_info = get_user_info ()
31+ return 'You are currently logged in as ' + user_info ['given_name' ]
32+
33+ return 'You are not currently logged in'
34+
35+ def no_cache (view ):
36+ @functools .wraps (view )
37+ def no_cache_impl (* args , ** kwargs ):
38+ response = flask .make_response (view (* args , ** kwargs ))
39+ response .headers ['Cache-Control' ] = 'no-store, no-cache, must-revalidate, max-age=0'
40+ response .headers ['Pragma' ] = 'no-cache'
41+ response .headers ['Expires' ] = '-1'
42+ return response
43+
44+ return functools .update_wrapper (no_cache_impl , view )
45+
46+ @app .route ('/google/login' )
47+ @no_cache
48+ def login ():
49+ session = OAuth2Session (CLIENT_ID , CLIENT_SECRET , scope = AUTHORIZATION_SCOPE , redirect_uri = AUTH_REDIRECT_URI )
50+ uri , state = session .authorization_url (AUTHORIZATION_URL )
51+ flask .session [AUTH_STATE_KEY ] = state
52+ flask .session .permanent = True
53+ return flask .redirect (uri , code = 302 )
54+
55+
56+ @app .route ('/google/auth' )
57+ @no_cache
58+ def google_auth_redirect ():
59+ state = flask .request .args .get ('state' , default = None , type = None )
60+
61+ session = OAuth2Session (CLIENT_ID , CLIENT_SECRET , scope = AUTHORIZATION_SCOPE , state = state , redirect_uri = AUTH_REDIRECT_URI )
62+ oauth2_tokens = session .fetch_access_token (ACCESS_TOKEN_URI , authorization_response = flask .request .url )
63+ flask .session [AUTH_TOKEN_KEY ] = oauth2_tokens
64+
65+ return flask .redirect (BASE_URI , code = 302 )
66+
67+ @app .route ('/google/logout' )
68+ @no_cache
69+ def logout ():
70+ flask .session .pop (AUTH_TOKEN_KEY , None )
71+ flask .session .pop (AUTH_STATE_KEY , None )
72+ flask .session .pop (USER_INFO_KEY , None )
73+
74+ return flask .redirect (BASE_URI , code = 302 )
75+
76+ def is_logged_in ():
77+ return True if AUTH_TOKEN_KEY in flask .session else False
78+
79+ def build_credentials ():
80+ if not is_logged_in ():
81+ raise Exception ('User must be logged in' )
82+
83+ oauth2_tokens = flask .session [AUTH_TOKEN_KEY ]
84+ return google .oauth2 .credentials .Credentials (
85+ oauth2_tokens ['access_token' ],
86+ refresh_token = oauth2_tokens ['refresh_token' ],
87+ client_id = CLIENT_ID ,
88+ client_secret = CLIENT_SECRET ,
89+ token_uri = ACCESS_TOKEN_URI )
90+
91+ def get_user_info ():
92+ credentials = build_credentials ()
93+ oauth2_client = googleapiclient .discovery .build ('oauth2' , 'v2' , credentials = credentials )
94+ return oauth2_client .userinfo ().get ().execute ()
0 commit comments