Add Google SSO

This pull request adds Google SSO support. There are two main changes:

1. Add the Google auth view resource
2. Make passwords optional when creating a new user. This allows an admin
to create a user without a password so that they can only login via Google.
This commit is contained in:
Robert Picard 2015-12-21 18:34:07 -05:00
parent 70c92fea15
commit 350d013043
4 changed files with 52 additions and 6 deletions

View File

@ -230,5 +230,46 @@ class Ping(Resource):
return dict(token=create_token(user)) return dict(token=create_token(user))
class Google(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(Google, self).__init__()
def post(self):
access_token_url = 'https://accounts.google.com/o/oauth2/token'
people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'
self.reqparse.add_argument('clientId', type=str, required=True, location='json')
self.reqparse.add_argument('redirectUri', type=str, required=True, location='json')
self.reqparse.add_argument('code', type=str, required=True, location='json')
args = self.reqparse.parse_args()
# Step 1. Exchange authorization code for access token
payload = {
'client_id': args['clientId'],
'grant_type': 'authorization_code',
'redirect_uri': args['redirectUri'],
'code': args['code'],
'client_secret': current_app.config.get('GOOGLE_SECRET')
}
r = requests.post(access_token_url, data=payload)
token = r.json()
# Step 2. Retrieve information about the current user
headers = {'Authorization': 'Bearer {0}'.format(token['access_token'])}
r = requests.get(people_api_url, headers=headers)
profile = r.json()
user = user_service.get_by_email(profile['email'])
if user:
return dict(token=create_token(user))
api.add_resource(Login, '/auth/login', endpoint='login') api.add_resource(Login, '/auth/login', endpoint='login')
api.add_resource(Ping, '/auth/ping', endpoint='ping') api.add_resource(Ping, '/auth/ping', endpoint='ping')
api.add_resource(Google, '/auth/google', endpoint='google')

View File

@ -30,8 +30,7 @@
Password Password
</label> </label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" required/> <input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" />
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -52,7 +52,10 @@ class User(db.Model):
:param password: :param password:
:return: :return:
""" """
return bcrypt.check_password_hash(self.password, password) if self.password:
return bcrypt.check_password_hash(self.password, password)
else:
return False
def hash_password(self): def hash_password(self):
""" """
@ -60,8 +63,11 @@ class User(db.Model):
:return: :return:
""" """
self.password = bcrypt.generate_password_hash(self.password) if self.password:
return self.password self.password = bcrypt.generate_password_hash(self.password)
return self.password
else:
return None
@property @property
def is_admin(self): def is_admin(self):

View File

@ -157,7 +157,7 @@ class UsersList(AuthenticatedResource):
""" """
self.reqparse.add_argument('username', type=str, location='json', required=True) self.reqparse.add_argument('username', type=str, location='json', required=True)
self.reqparse.add_argument('email', type=str, location='json', required=True) self.reqparse.add_argument('email', type=str, location='json', required=True)
self.reqparse.add_argument('password', type=str, location='json', required=True) self.reqparse.add_argument('password', type=str, location='json', default=None)
self.reqparse.add_argument('active', type=bool, default=True, location='json') self.reqparse.add_argument('active', type=bool, default=True, location='json')
self.reqparse.add_argument('roles', type=roles, default=[], location='json') self.reqparse.add_argument('roles', type=roles, default=[], location='json')