-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathapp.py
165 lines (137 loc) · 4.66 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import os
import base64
import json
import functools
import time
import logging
from logging import StreamHandler
from flask import (
Flask,
session,
redirect,
url_for,
request,
render_template,
current_app
)
from flask.ext.sqlalchemy import SQLAlchemy
import requests
import clef
SQLALCHEMY_DATABASE_URI = os.environ.get(
'HEROKU_POSTGRESQL_WHITE_URL',
'sqlite:////tmp/test.db'
)
DEBUG = not os.environ.get('PRODUCTION')
REDIRECT_URL = os.environ.get('REDIRECT_URL', 'http://localhost:5000/login')
CLEF_APP_ID = '4f318ac177a9391c2e0d221203725ffd'
CLEF_APP_SECRET = '2125d80f4583c52c46f8084bcc030c9b'
SECRET_KEY = 'development key'
app = Flask(__name__)
app.config.from_object(__name__)
# set up logging
app.logger.setLevel(logging.DEBUG)
app.logger.addHandler(StreamHandler())
db = SQLAlchemy(app)
clef.initialize(app_id=CLEF_APP_ID, app_secret=CLEF_APP_SECRET)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String())
first_name = db.Column(db.String())
clef_id = db.Column(db.String())
logged_out_at = db.Column(db.BigInteger)
class LogoutHookException(Exception):
pass
def logged_in(view):
"""
Decorator that checks whether a user is currently logged in.
If a user is logged in it provides the user object.
Uses Clef-based database logout.
"""
@functools.wraps(view)
def decorated_view(*args, **kwargs):
user_id = session.get('user', -1)
logged_in_at = session.get('logged_in_at', None)
user = User.query.get(user_id)
# does check for database logout of user
if user and user.logged_out_at > logged_in_at:
session.clear()
user = None
return view(user=user, *args, **kwargs)
return decorated_view
def is_valid_state(state):
state_is_valid = ('state' in session
and len(session['state']) > 0
and session['state'] == state)
session.pop('state', None)
return state_is_valid
def generate_state():
state = base64.urlsafe_b64encode(os.urandom(32))
session['state'] = state
return state
@app.route('/')
@logged_in
def hello(user=None):
return render_template(
'index.html',
user=user,
state=generate_state(),
redirect_url=current_app.config['REDIRECT_URL']
)
@app.route('/login')
def login():
# If the state parameter doesn't match what we passed into the Clef button,
# then this request could have been generated by a 3rd party, so we should
# abort it.
#
# For more protection about the state parameter and CSRF, check out
# http://docs.getclef.com/v1.0/docs/verifying-state-parameter
state = request.args.get('state')
if not is_valid_state(state):
return "Oops, the state parameter didn't match what was passed in to the Clef button."
code = request.args.get('code')
# call to get user information handles the OAuth handshake
try:
user_information = clef.get_login_information(code=code)
except clef.APIError as e:
app.logger.error(e)
# request was successful so store the user in the session
else:
clef_id = user_information.get('id')
email = user_information.get('email')
first_name = user_information.get('first_name')
user = User.query.filter_by(clef_id=clef_id).first()
if not user:
user = User(email=email, first_name=first_name, clef_id=clef_id)
db.session.add(user)
db.session.commit()
session['user'] = user.id
session['logged_in_at'] = time.time()
return redirect(url_for('hello'))
@app.route('/logout', methods=['POST'])
@logged_in
def logout(user=None):
# Clef is notifying you that a user has logged out from their phone
logout_token = request.form.get('logout_token')
# use the clef_id to look up the user in your database
# http://docs.getclef.com/v1.0/docs/database-logout
try:
clef_id = clef.get_logout_information(logout_token=logout_token)
except clef.APIError as e:
app.logger.error(e)
return 'Logout error'
else:
user = User.query.filter_by(clef_id=clef_id).first()
if not user:
app.logger.error('Invalid user')
else:
user.logged_out_at = time.time()
db.session.add(user)
db.session.commit()
return 'Ok'
if __name__ == "__main__":
# NOTE: For the sample app, we drop all information every time the app reloads.
#
# Obviously, this doesn't really work in production. Remove the following
# lines to have a sane DB configuration.
db.create_all()
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))