Esempio n. 1
0
    def register_user(self):

        schema = RELS['v1.AuthView:register'][request.method]

        try:
            data = request_register_options.parse_args()
            validate(data, schema, format_checker=FormatChecker())

            invite_token = data['token']
            if invite_token:
                expired, invalid, invitor = get_token_status(invite_token, 'invite', 'USE_INVITE')

                if invalid or not invitor:
                    return dict(status=409, message="Invite is invalid"), 409

                if expired:
                    return dict(status=409, message="Invite has expired"), 409

                inviteTokenObj = Invite.find(token=invite_token).first()

                if not inviteTokenObj:
                    return dict(status=409, message="Invite not found"), 409

                if inviteTokenObj.invitee_id:
                    return dict(status=409, message="Invite already used"), 409

            password = encrypt_password(data['password'])
            user = register_user(email=data['email'], password=password, first_name=data['firstName'],
                   last_name=data['lastName'], roles=[Role.first(name='user')])

            if invite_token:
                inviteTokenObj.invitee_id = user.id
                inviteTokenObj.save()

            token = generate_confirmation_token(user)
            confirmation_link = urljoin(current_app.config['CLIENT_DOMAIN'], '/#/confirm?token='+token)

            #TODO this mail send should be performed asynchronously using celery, see issue #88850472
            send_message(
                subject='Please Confirm Your FogMine Account',
                sender="*****@*****.**",
                recipients = [user.email],
                html_body=render_template('email/activate.html', user=user, confirmation_link=confirmation_link),
                text_body=render_template('email/activate.txt', user=user, confirmation_link=confirmation_link)
            )

            user_data = generate_response_dict(user=user)

            #SEE 90454516, Login the user
            login_user(user)
            user.save() #saving the user as a precaution, want the log data

            return dict(status=201, message='A confirmation email has been sent to '+user.email, user=user_data), 201

        except ValidationError as e:
            return dict(status=400, message=e.message), 400
        except IntegrityError:
            return {'status': 409, 'message': 'An account with that email already exists.'}, 409
        except werkzeug.exceptions.ClientDisconnected:
            return dict(status=400, message='one or more required arguments missing from this request'), 400
Esempio n. 2
0
def login_handler(response, provider, query):
    """Shared method to handle the signin process"""

    connection = _datastore.find_connection(**query)

    if connection:
        after_this_request(_commit)
        token_pair = get_token_pair_from_oauth_response(provider, response)
        if (token_pair['access_token'] != connection.access_token or
                token_pair['secret'] != connection.secret):
            connection.access_token = token_pair['access_token']
            connection.secret = token_pair['secret']
            _datastore.put(connection)
        user = connection.user
        login_user(user)
        key = _social.post_oauth_login_session_key
        redirect_url = session.pop(key, get_post_login_redirect())

        login_completed.send(current_app._get_current_object(),
                             provider=provider, user=user)

        return redirect(redirect_url)

    login_failed.send(current_app._get_current_object(),
                      provider=provider,
                      oauth_response=response)

    next_url = get_url(_security.login_manager.login_view)
    msg = '%s account not associated with an existing user' % provider.name
    do_flash(msg, 'error')
    return redirect(next_url)
Esempio n. 3
0
    def before_request():
        """Login the default user if running in desktop mode"""

        # Check the auth key is valid, if it's set, and we're not in server
        # mode, and it's not a help file request.
        if not config.SERVER_MODE and app.PGADMIN_KEY != '':
            if (
                ('key' not in request.args or
                 request.args['key'] != app.PGADMIN_KEY) and
                request.cookies.get('PGADMIN_KEY') != app.PGADMIN_KEY and
                request.endpoint != 'help.static'
            ):
                abort(401)

        if not config.SERVER_MODE and not current_user.is_authenticated:
            user = user_datastore.get_user(config.DESKTOP_USER)
            # Throw an error if we failed to find the desktop user, to give
            # the sysadmin a hint. We'll continue to try to login anyway as
            # that'll through a nice 500 error for us.
            if user is None:
                app.logger.error(
                    'The desktop user %s was not found in the configuration '
                    'database.'
                    % config.DESKTOP_USER
                )
                abort(401)
            login_user(user)
Esempio n. 4
0
def _login_with_ldap(email, password, config):
    _logger.debug('Attempting login via LDAP for {}...', email)

    _login_failed = functools.partial(
        error_abort,
        'Username or password are incorrect for {}'.format(email), code=requests.codes.unauthorized)

    if not config['ldap_login_enabled'] or not email or not password:
        _logger.debug('Rejecting login because LDAP is disabled or no username/password')
        _login_failed()

    try:

        ldap_obj = ldap.initialize(config['ldap_uri'])
        ldap_obj.bind_s(email, password)
        ldap_infos = ldap_obj.search_s(config['ldap_base_dn'], ldap.SCOPE_SUBTREE, 'userPrincipalName={}'.format(email))
        if not ldap_infos:
            _logger.error('Could not authenticate via LDAP - no records found')
            _login_failed()

        ldap_info = ldap_infos[0][1]
        user_info = {
            'email': ldap_info['mail'][0].decode('utf-8'),
            'given_name': ldap_info['givenName'][0].decode('utf-8'),
            'family_name': ldap_info['sn'][0].decode('utf-8'),
        }
        user = get_or_create_user(user_info)
        login_user(user)
        return _make_success_login_response(user, user_info)
    except ldap.INVALID_CREDENTIALS:
        _logger.error('LDAP Invalid credentials', exc_info=True)
        _login_failed()
Esempio n. 5
0
    def update_password(self):
        """Used in conjunction with reset password to set password to a known value"""
        schema = RELS['v1.AuthView:update'][request.method]
        args = reset_password_options.parse_args()

        try:
            validate(args, schema, format_checker=FormatChecker())
        except ValidationError as e:
            return dict(status=400, message=e.message), 400

        token = args.get('token')
        expired, invalid, user = reset_password_token_status(token)

        if invalid or not user:
            return dict(status=409, message="Invalid reset token"), 409

        if expired:
            return dict(status=409, message="Reset token has expired"), 409

        update_password(user, args.get('password'))
        user.reset_secret()
        send_password_reset_notice(user)

        #SEE 90720022, Login the user
        login_user(user)
        user.save() #saving the user as a precaution, want the log data

        return {'status': 200, 'message': 'Password updated', 'user': generate_response_dict(user=user)}
Esempio n. 6
0
File: auth.py Progetto: keho98/argos
def _process_user(provider, provider_id, token, secret, userdata):
    """
    Processes a user to either login or
    add a new authentication to an existing user.
    """
    # If there is already an authenticated user,
    # add this provider to that user.
    if current_user.is_authenticated:
        try:
            current_user.add_provider(provider, provider_id, token, secret, update=True)

        # Conflict - this added authentication already exists but is
        # associated with another user.
        except AuthExistsForUserException as e:
            return jsonify(status=409, message=e.message)

    # Otherwise...
    else:
        # Try to find an existing auth and user.
        auth = Auth.for_provider(provider, provider_id)

        # If one is found, update the auth.
        if auth:
            user = auth.user
            auth.update_token(token, secret)

        # Otherwise create a new user and auth.
        else:
            user = User(**userdata)
            auth = Auth(provider, provider_id, token, secret)
            auth.user = user
            db.session.add(user)
            db.session.add(auth)
        db.session.commit()
        login_user(user)
Esempio n. 7
0
    def reset_password(token):
        """View function that handles a reset password request."""

        expired, invalid, user = reset_password_token_status(token)

        if invalid:
            do_flash(*get_message('INVALID_RESET_PASSWORD_TOKEN'))
        if expired:
            do_flash(*get_message('PASSWORD_RESET_EXPIRED', email=user.email,
                                  within=_security.reset_password_within))
        if invalid or expired:
            return redirect(url_for('browser.forgot_password'))
        has_error = False
        form = _security.reset_password_form()

        if form.validate_on_submit():
            try:
                update_password(user, form.password.data)
            except SOCKETErrorException as e:
                # Handle socket errors which are not covered by SMTPExceptions.
                logging.exception(str(e), exc_info=True)
                flash(gettext(u'SMTP Socket error: {}\n'
                              u'Your password has not been changed.'
                              ).format(e),
                      'danger')
                has_error = True
            except (SMTPConnectError, SMTPResponseException,
                    SMTPServerDisconnected, SMTPDataError, SMTPHeloError,
                    SMTPException, SMTPAuthenticationError, SMTPSenderRefused,
                    SMTPRecipientsRefused) as e:

                # Handle smtp specific exceptions.
                logging.exception(str(e), exc_info=True)
                flash(gettext(u'SMTP error: {}\n'
                              u'Your password has not been changed.'
                              ).format(e),
                      'danger')
                has_error = True
            except Exception as e:
                # Handle other exceptions.
                logging.exception(str(e), exc_info=True)
                flash(gettext(u'Error: {}\n'
                              u'Your password has not been changed.'
                              ).format(e),
                      'danger')
                has_error = True

            if not has_error:
                after_this_request(_commit)
                do_flash(*get_message('PASSWORD_RESET'))
                login_user(user)
                return redirect(get_url(_security.post_reset_view) or
                                get_url(_security.post_login_view))

        return _security.render_template(
            config_value('RESET_PASSWORD_TEMPLATE'),
            reset_password_form=form,
            reset_password_token=token,
            **_ctx('reset_password'))
Esempio n. 8
0
def test_login():
    """
    A route for logging in users, for testing.
    """
    id = request.form['id']
    user = User.query.get(id)
    login_user(user)
    return jsonify(success=True)
Esempio n. 9
0
def test_login():
    """
    A route for logging in users, for testing,
    since right now users are exclusively authenticated
    via third-party providers.
    """
    id = request.form['id']
    user = User.query.get(id)
    login_user(user)
    return jsonify(success=True)
Esempio n. 10
0
def _login_with_google_oauth2(auth_code):
    user_info = get_oauth2_identity(auth_code)
    if not user_info:
        error_abort('Could not complete OAuth2 exchange', code=requests.codes.unauthorized)

    _check_alowed_email_domain(user_info)

    user = get_or_create_user(user_info)
    login_user(user)

    return _make_success_login_response(user, user_info)
Esempio n. 11
0
def login():
    login_user_form = LoginForm(request.form)
    # Handle logging in
    if request.method == 'POST':
        if login_user_form.validate_on_submit():
            login_user(login_user_form.user)
            flash("You are logged in.", 'success')
            redirect_url = request.args.get("next") or url_for("user.members")
            return redirect(redirect_url)
        else:
            flash_errors(login_user_form)
    return render_template("public/login.html", login_user_form=login_user_form)
Esempio n. 12
0
def login():
    data = request.json
    user = user_datastore.get_user(data['email'])
    if not user:
        return "Invalid username or password", 401
    if not verify_and_update_password(data['password'], user):
        return "Invalid username or password", 401

    login_user(user)
    return jsonify({
        'auth_token': user.get_auth_token(),
        'email': user.email
    })
Esempio n. 13
0
    def before_request():
        """Login the default user if running in desktop mode"""
        if config.SERVER_MODE is False:
            user = user_datastore.get_user(config.DESKTOP_USER)

            # Throw an error if we failed to find the desktop user, to give
            # the sysadmin a hint. We'll continue to try to login anyway as
            # that'll through a nice 500 error for us.
            if user is None:
                app.logger.error('The desktop user %s was not found in the configuration database.' % config.DESKTOP_USER)
                abort(401)

            login_user(user)
Esempio n. 14
0
def testing_login():
    if not current_app.config.get('TESTING'):
        abort(requests.codes.not_found)

    user_id = request.args.get('user_id')
    if user_id is not None:
        user = User.query.get_or_404(int(user_id))
    else:
        testing_email = 'testing@localhost'
        user = get_or_create_user({'email': testing_email})
    assert user
    login_user(user)
    user_info = {}
    return _make_success_login_response(user, user_info)
Esempio n. 15
0
    def post(self):
        if request.json:
            data = request.json

            user = self.model.query.filter(self.model.email == data['email']).first()
            if user and verify_and_update_password(data['password'], user) and login_user(user):
                user_data = self.schema().dump(user).data
                return jsonify({'success': 200, 'data': user_data})
            else:
                return make_response(jsonify({'error': 403, 'data': 'invalid data'}), 403)
        else:
            data = request.form
            user = self.model.query.filter(self.model.email == data['email']).first()
            if user and verify_and_update_password(data['password'], user) and login_user(user):
                return redirect('/test/v1/admin/', 302)
Esempio n. 16
0
def _login_session(test_app, client):
    """Logs in a client with the with the client's user.

    Expects client to have an attribute 'user' with the user object that will
    be logged in.

    Uses session-based authentication and intended to be used by the
    session_client fixture (cookie-based session management). For token-based
    AJAX authentication, use the authenticated_client fixture.
    """
    login_user(client.user)
    # Session will be lost for the actual test, manually add session cookie
    session_serializer = test_app.session_interface.get_signing_serializer(test_app)
    session_cookie = session_serializer.dumps(dict(session))
    client.set_cookie('localhost', 'session', session_cookie)
Esempio n. 17
0
def login():
    form = LoginForm()

    if form.validate_on_submit():
        login_user(form.user)
        session['auth_username'] = form.user.username
        current_app.logger.info('{} Logged in'.format(form.user.username))
        return redirect(url_for('Admin.index'))
    else:
        if form.is_submitted():
            username = request.form.get('username')
            current_app.logger.info('Failed login attempt by {}'.format(username))
            return redirect(url_for('.login'))

    return render_template('security/login_user.html', form=form)
Esempio n. 18
0
    def post(self):
        if "onelogin" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Onelogin is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404
        auth = OneLogin_Saml2_Auth(self.req, current_app.config.get("ONELOGIN_SETTINGS"))

        self.reqparse.add_argument('return_to', required=False, default=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('acs', required=False)
        self.reqparse.add_argument('sls', required=False)

        args = self.reqparse.parse_args()

        return_to = args['return_to']

        if args['acs'] != None:
            # valids the SAML response and checks if successfully authenticated
            if self._consumer(auth):
                email = auth.get_attribute(current_app.config.get("ONELOGIN_EMAIL_FIELD"))[0]
                user = User.query.filter(User.email == email).first()

                # if we get an sso user create them an account
                if not user:
                    user = User(
                        email=email,
                        active=True,
                        role=current_app.config.get('ONELOGIN_DEFAULT_ROLE')
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                    )
                    db.session.add(user)
                    db.session.commit()
                    db.session.refresh(user)

                # Tell Flask-Principal the identity changed
                identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
                login_user(user)
                db.session.commit()
                db.session.refresh(user)

                self_url = OneLogin_Saml2_Utils.get_self_url(self.req)
                if 'RelayState' in request.form and self_url != request.form['RelayState']:
                    return redirect(auth.redirect_to(request.form['RelayState']), code=302)
                else:
                    return redirect(current_app.config.get('BASE_URL'), code=302)
            else:
                return dict(message='OneLogin authentication failed.'), 403
        elif args['sls'] != None:
            return dict(message='OneLogin SLS not implemented yet.'), 405
        else:
            return redirect(auth.login(return_to=return_to))
Esempio n. 19
0
def login():
    body = request.get_json()
    email = body['email']
    password = body['password']
    user = User.query.filter_by(email=email).one_or_none()
    if user and verify_password(password, user.password):
        login_user(user)
        return jsonify({
            'data': {
                'email': user.email,
                'roles': [role.name for role in user.roles]
            }
        })
    return jsonify({
        'message': 'Email or password is incorrect'
    }), 422
Esempio n. 20
0
def reauth():
    token = (request.json or {}).get('auth_token')
    if token is None:
        abort(requests.codes.unauthorized)
    try:
        token_data = _get_token_serializer().loads(
            token, max_age=_MAX_TOKEN_AGE)
    except BadSignature:
        abort(requests.codes.unauthorized)
    user = User.query.get_or_404(token_data['user_id'])

    login_user(user)

    return jsonify({
        'auth_token': token,
        'user_info': token_data['user_info'],
    })
Esempio n. 21
0
def oidc_callback():
    user_info = oidc.authenticate('dex', request)

    user = user_datastore.get_user(user_info['email'])

    if not user:
        user = create_user(user_info)

    login_user(user)

    next_url = url_for('base.index')

    if 'next_url' in session:
        next_url = session['next_url']
        del session['next_url']

    return redirect(next_url)
Esempio n. 22
0
def login():

    auth_code = (request.json or {}).get('authorizationCode')
    if auth_code is None:
        abort(requests.codes.unauthorized)

    user_info = get_oauth2_identity(auth_code)
    if not user_info:
        abort(requests.codes.unauthorized)

    _check_alowed_email_domain(user_info)

    user = _get_or_create_user(user_info)
    _fix_first_user_role(user)

    login_user(user)

    return _make_success_login_response(user, user_info)
Esempio n. 23
0
def authorized():
    resp = youckan.authorized_response()
    if resp is None or isinstance(resp, OAuthException):
        # TODO: better error handling
        abort(403)

    session['youckan.token'] = (resp['access_token'], '')
    response = youckan.get('me')
    data = response.data

    user = datastore.find_user(email=data['email'])
    if not user:
        user = datastore.create_user(
            slug=data['slug'],
            first_name=data['first_name'],
            last_name=data['last_name'],
            email=data['email'],
            active=data['is_active'],
            avatar_url=data['profile'].get('avatar') or None,
            website=data['profile'].get('website') or None,
            about=data['profile'].get('about') or None
        )
    else:
        user.first_name = data['first_name']
        user.last_name = data['last_name']
        user.active = data['is_active']
        user.avatar_url = data['profile'].get('avatar') or None
        user.website = data['profile'].get('website') or None
        user.about = data['profile'].get('about') or None

    admin_role = datastore.find_or_create_role('admin')
    if data['is_superuser'] and not user.has_role(admin_role):
        datastore.add_role_to_user(user, admin_role)

    user.save()
    login_user(user)

    redirect_to = url_for('site.home')
    if 'state' in request.args:
        state = request.args.get('state')
        decoded_state = json.loads(b64decode(state))
        redirect_to = decoded_state.get('next_url', redirect_to)
    return redirect(redirect_to)
Esempio n. 24
0
def _login_with_credentials(credentials):
    config = get_runtime_config_private_dict()
    username = credentials.get('username')
    password = credentials.get('password')

    email = _fix_email(username, config)
    user = User.query.filter_by(email=email).first()

    if current_app.config['TESTING']:
        if user is None:
            user = get_or_create_user({'email': email})
        if not login_user(user):
            _logger.error('Could not login user {}', email)
            error_abort('Login failed', code=requests.codes.unauthorized)
        return _make_success_login_response(user)

    if user is not None and user.password:
        if verify_password(password, user.password):
            login_user(user)
            return _make_success_login_response(user)
    _logger.debug('Could not login user locally (no user or password mismatch)')
    return _login_with_ldap(email, password, config)
Esempio n. 25
0
    def post(self):
        if "aad" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "AzureAD is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get('AAD_CLIENT_ID'),
            redirectUri=current_app.config.get('AAD_REDIRECT_URI'),
            return_to=current_app.config.get('WEB_PATH')
        )
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('id_token', type=str, required=True)
        self.reqparse.add_argument('state', type=str, required=False, default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]
        id_token = args['id_token']

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        # fetch token public key
        jwks_url = current_app.config.get('AAD_JWKS_URL')

        # Validate id_token and extract username (email)
        username = self.validate_id_token(id_token, client_id, jwks_url)

        user = setup_user(username, '', current_app.config.get('AAD_DEFAULT_ROLE', 'View'))

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
        login_user(user)
        db.session.commit()
        db.session.refresh(user)

        return redirect(return_to, code=302)
Esempio n. 26
0
def login_handler(response, provider, query):
    """Shared method to handle the signin process"""
    connection = _datastore.find_connection(**query)
    if connection:
        after_this_request(_commit)
        user = connection.user
        login_user(user)
        key = _social.post_oauth_login_session_key
        redirect_url = session.pop(key, get_post_login_redirect())

        login_completed.send(current_app._get_current_object(),
                             provider=provider, user=user)

        return redirect(redirect_url)
    login_failed.send(current_app._get_current_object(),
                      provider=provider,
                      oauth_response=response)
    session['failed_login_connection'] = \
        get_connection_values_from_oauth_response(provider, response)
    next = make_external('/#!login/provider/'+provider.id)
    #next = get_url(_security.login_manager.login_view)
    #msg = '%s account not associated with an existing user' % provider.name
    #do_flash(msg, 'error')
    return redirect(next)
Esempio n. 27
0
def authenticated_client(request, client, setupdb):  # pylint: disable=redefined-outer-name
    """Rewrite client requests to include an authentication token."""
    user_id = None
    if 'use_alt_user' in request.keywords:
        print(f'authenticated_client: Using alternate user ID {setupdb.test_alt_user_id}')
        user_id = setupdb.test_alt_user_id
    else:
        print(f'authenticated_client: Using default user ID {setupdb.test_user_id}')
        user_id = setupdb.test_user_id
    client.user = models.User.query.get(user_id)

    login_user(client.user)
    token = client.user.get_auth_token()

    def open_proxy(*args, **kwargs):
        """Proxy client to automatically insert authentication header"""
        if 'headers' not in kwargs:
            kwargs['headers'] = {}
        kwargs['headers']['Authentication-Token'] = token
        return client.open_(*args, **kwargs)
    client.open_ = client.open
    client.open = open_proxy

    return client
Esempio n. 28
0
def reauth():
    token = (request.json or {}).get('auth_token')
    if token is None:
        error_abort('Missing reauth token')
    try:
        token_data = _get_token_serializer().loads(
            token, max_age=_MAX_TOKEN_AGE)
    except BadSignature:
        error_abort('Reauth token invalid', code=requests.codes.unauthorized)
    user = User.query.get_or_404(token_data['user_id'])

    if not login_user(user):
        error_abort('Login failed', code=requests.codes.unauthorized)

    return jsonify({
        'auth_token': token,
        'user_info': token_data['user_info'],
    })
    def post(self):
        if "google" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Google is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get("GOOGLE_CLIENT_ID"),
            redirectUri=api.url_for(Google),
            return_to=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state',
                                   type=str,
                                   required=False,
                                   default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        access_token_url = 'https://accounts.google.com/o/oauth2/token'
        people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'

        args = self.reqparse.parse_args()

        # Step 1. Exchange authorization code for access token
        payload = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'redirect_uri': redirect_uri,
            'code': args['code'],
            'client_secret': current_app.config.get('GOOGLE_SECRET')
        }

        r = requests.post(access_token_url, data=payload)
        token = r.json()

        # Step 1bis. Validate (some information of) the id token (if necessary)
        google_hosted_domain = current_app.config.get("GOOGLE_HOSTED_DOMAIN")
        if google_hosted_domain is not None:
            current_app.logger.debug(
                'We need to verify that the token was issued for this hosted domain: %s '
                % (google_hosted_domain))

            # Get the JSON Web Token
            id_token = r.json()['id_token']
            current_app.logger.debug('The id_token is: %s' % (id_token))

            # Extract the payload
            (header_data, payload_data) = fetch_token_header_payload(id_token)
            current_app.logger.debug('id_token.header_data: %s' %
                                     (header_data))
            current_app.logger.debug('id_token.payload_data: %s' %
                                     (payload_data))

            token_hd = payload_data.get('hd')
            if token_hd != google_hosted_domain:
                current_app.logger.debug('Verification failed: %s != %s' %
                                         (token_hd, google_hosted_domain))
                return dict(message='Token is invalid %s' % token), 403
            current_app.logger.debug('Verification passed')

        # 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.query.filter(User.email == profile['email']).first()

        # if we get an sso user create them an account
        if not user:
            user = User(email=profile['email'],
                        active=True,
                        role='View'
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                        )
            db.session.add(user)
            db.session.commit()
            db.session.refresh(user)

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(),
                              identity=Identity(user.id))
        login_user(user)

        return redirect(return_to, code=302)
Esempio n. 30
0
 def login_user(self, user):
     """Perform any login actions."""
     return login_user(user)
Esempio n. 31
0
    def reset_password(token):
        """View function that handles a reset password request."""

        expired, invalid, user = reset_password_token_status(token)

        if invalid:
            do_flash(*get_message('INVALID_RESET_PASSWORD_TOKEN'))
        if expired:
            do_flash(*get_message('PASSWORD_RESET_EXPIRED',
                                  email=user.email,
                                  within=_security.reset_password_within))
        if invalid or expired:
            return redirect(url_for('browser.forgot_password'))
        has_error = False
        form = _security.reset_password_form()

        if form.validate_on_submit():
            try:
                update_password(user, form.password.data)
            except SOCKETErrorException as e:
                # Handle socket errors which are not covered by SMTPExceptions.
                logging.exception(str(e), exc_info=True)
                flash(
                    gettext(u'SMTP Socket error: {}\n'
                            u'Your password has not been changed.').format(e),
                    'danger')
                has_error = True
            except (SMTPConnectError, SMTPResponseException,
                    SMTPServerDisconnected, SMTPDataError, SMTPHeloError,
                    SMTPException, SMTPAuthenticationError, SMTPSenderRefused,
                    SMTPRecipientsRefused) as e:

                # Handle smtp specific exceptions.
                logging.exception(str(e), exc_info=True)
                flash(
                    gettext(u'SMTP error: {}\n'
                            u'Your password has not been changed.').format(e),
                    'danger')
                has_error = True
            except Exception as e:
                # Handle other exceptions.
                logging.exception(str(e), exc_info=True)
                flash(
                    gettext(u'Error: {}\n'
                            u'Your password has not been changed.').format(e),
                    'danger')
                has_error = True

            if not has_error:
                after_this_request(_commit)
                do_flash(*get_message('PASSWORD_RESET'))
                login_user(user)
                return redirect(
                    get_url(_security.post_reset_view)
                    or get_url(_security.post_login_view))

        return _security.render_template(
            config_value('RESET_PASSWORD_TEMPLATE'),
            reset_password_form=form,
            reset_password_token=token,
            **_ctx('reset_password'))
Esempio n. 32
0
    def post(self):
        if "ping" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Ping is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get('PING_CLIENT_ID'),
            redirectUri=current_app.config.get('PING_REDIRECT_URI'),
            return_to=current_app.config.get('WEB_PATH')
        )
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state', type=str, required=False, default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        # take the information we have received from the provider to create a new request
        params = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'scope': 'openid email profile address',
            'redirect_uri': redirect_uri,
            'code': args['code']
        }

        # you can either discover these dynamically or simply configure them
        access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL')
        user_api_url = current_app.config.get('PING_USER_API_URL')

        # the secret and cliendId will be given to you when you signup for the provider
        basic = base64.b64encode(bytes('{0}:{1}'.format(client_id, current_app.config.get("PING_SECRET"))))
        headers = {'Authorization': 'Basic {0}'.format(basic.decode('utf-8'))}

        # exchange authorization code for access token.
        r = requests.post(access_token_url, headers=headers, params=params)
        id_token = r.json()['id_token']
        access_token = r.json()['access_token']

        # fetch token public key
        header_data = fetch_token_header_payload(id_token)[0]
        jwks_url = current_app.config.get('PING_JWKS_URL')

        # retrieve the key material as specified by the token header
        r = requests.get(jwks_url)
        for key in r.json()['keys']:
            if key['kid'] == header_data['kid']:
                secret = get_rsa_public_key(key['n'], key['e'])
                algo = header_data['alg']
                break
        else:
            return dict(message='Key not found'), 403

        # validate your token based on the key it was signed with
        try:
            jwt.decode(id_token, secret.decode('utf-8'), algorithms=[algo], audience=client_id)
        except jwt.DecodeError:
            return dict(message='Token is invalid'), 403
        except jwt.ExpiredSignatureError:
            return dict(message='Token has expired'), 403
        except jwt.InvalidTokenError:
            return dict(message='Token is invalid'), 403

        user_params = dict(access_token=access_token, schema='profile')

        # retrieve information about the current user.
        r = requests.get(user_api_url, params=user_params)
        profile = r.json()

        user = setup_user(
            profile.get('email'),
            profile.get('groups', profile.get('googleGroups', [])),
            current_app.config.get('PING_DEFAULT_ROLE', 'View'))

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
        login_user(user)
        db.session.commit()
        db.session.refresh(user)

        return redirect(return_to, code=302)
Esempio n. 33
0
def oAuthAuthorize(provider):
    oAuthClient = oauth.create_client(provider)
    oAuthProviderQuery = settings.oAuthProvider.query.filter_by(
        name=provider).first()
    if oAuthProviderQuery is not None:

        try:
            token = oAuthClient.authorize_access_token()
        except:
            return redirect('/login')

        userData = oAuthClient.get(oAuthProviderQuery.profile_endpoint)
        userDataDict = userData.json()

        userQuery = Sec.User.query.filter_by(
            oAuthID=userDataDict[oAuthProviderQuery.id_value],
            oAuthProvider=provider,
            authType=1).first()

        # Default expiration time to 365 days into the future
        if 'expires_at' not in token:
            if 'expires_in' in token:
                token['expires_at'] = datetime.timedelta(seconds=int(
                    token['exipires_in'])) + datetime.datetime.now()
            else:
                token['expires_at'] = time() + (365 * 24 * 3600)

        # If oAuth ID, Provider, and Auth Type Match - Initiate Login
        if userQuery is not None:
            existingTokenQuery = Sec.OAuth2Token.query.filter_by(
                user=userQuery.id).all()
            for existingToken in existingTokenQuery:
                db.session.delete(existingToken)
            db.session.commit()
            newToken = None
            if 'refresh_token' in token:
                newToken = Sec.OAuth2Token(provider, token['token_type'],
                                           token['access_token'],
                                           token['refresh_token'],
                                           token['expires_at'], userQuery.id)
            else:
                newToken = Sec.OAuth2Token(provider, token['token_type'],
                                           token['access_token'], None,
                                           token['expires_at'], userQuery.id)
            db.session.add(newToken)
            db.session.commit()

            if userQuery.active is False:
                flash(
                    "User has been Disabled.  Please contact your administrator",
                    "error")
                return redirect('/login')
            else:
                login_user(userQuery)

                if oAuthProviderQuery.preset_auth_type == "Discord":
                    discord_processLogin(userDataDict, userQuery)
                elif oAuthProviderQuery.preset_auth_type == "Reddit":
                    reddit_processLogin(userDataDict, userQuery)
                elif oAuthProviderQuery.preset_auth_type == "Facebook":
                    facebook_processLogin(oAuthProviderQuery.api_base_url,
                                          userDataDict, userQuery)

                if userQuery.email is None or userQuery.email == 'None':
                    flash("Please Add an Email Address to your User Profile",
                          "error")
                    return redirect(url_for('settings.user_page'))
                else:
                    return redirect(url_for('root.main_page'))

        # If No Match, Determine if a User Needs to be created
        else:
            existingEmailQuery = None
            hasEmail = False

            if oAuthProviderQuery.email_value in userDataDict:
                existingEmailQuery = Sec.User.query.filter_by(
                    email=userDataDict[
                        oAuthProviderQuery.email_value]).first()
                hasEmail = True
            else:
                flash("Please Add an Email Address to your User Profile",
                      "error")

            # No Username Match - Create New User
            if existingEmailQuery is None:
                convertedUsername = userDataDict[
                    oAuthProviderQuery.username_value].replace(" ", "_")
                userDataDict[
                    oAuthProviderQuery.username_value] = convertedUsername
                existingUsernameQuery = Sec.User.query.filter_by(
                    username=convertedUsername).first()
                requestedUsername = convertedUsername
                if existingUsernameQuery is not None:
                    requestedUsername = requestedUsername + str(
                        random.randint(1, 9999))
                if hasEmail is True:
                    user_datastore.create_user(
                        email=userDataDict[oAuthProviderQuery.email_value],
                        username=requestedUsername,
                        active=True,
                        confirmed_at=datetime.datetime.now(),
                        authType=1,
                        oAuthID=userDataDict[oAuthProviderQuery.id_value],
                        oAuthProvider=provider)
                else:
                    user_datastore.create_user(
                        email=None,
                        username=requestedUsername,
                        active=True,
                        confirmed_at=datetime.datetime.now(),
                        authType=1,
                        oAuthID=userDataDict[oAuthProviderQuery.id_value],
                        oAuthProvider=provider)
                db.session.commit()
                user = Sec.User.query.filter_by(
                    username=requestedUsername).first()
                defaultRoleQuery = Sec.Role.query.filter_by(default=True)
                for role in defaultRoleQuery:
                    user_datastore.add_role_to_user(user, role.name)
                user.uuid = str(uuid.uuid4())
                user.xmppToken = str(os.urandom(32).hex())

                if oAuthProviderQuery.preset_auth_type == "Discord":
                    discord_processLogin(userDataDict, user)
                elif oAuthProviderQuery.preset_auth_type == "Reddit":
                    reddit_processLogin(userDataDict, user)
                elif oAuthProviderQuery.preset_auth_type == "Facebook":
                    facebook_processLogin(oAuthProviderQuery.api_base_url,
                                          userDataDict, user)

                newToken = None
                if 'refresh_token' in token:
                    newToken = Sec.OAuth2Token(provider, token['token_type'],
                                               token['access_token'],
                                               token['refresh_token'],
                                               token['expires_at'], user.id)
                else:
                    newToken = Sec.OAuth2Token(provider, token['token_type'],
                                               token['access_token'], None,
                                               token['expires_at'], user.id)
                db.session.add(newToken)
                db.session.commit()
                login_user(user)

                runWebhook("ZZZ", 20, user=user.username)
                newLog(
                    1, "A New User has Registered - Username:"******"An existing OAuth User exists under this email address with another provider",
                        "error")
                    return redirect('/')
Esempio n. 34
0
    def post(self):
        if "google" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Google is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get("GOOGLE_CLIENT_ID"),
            redirectUri=api.url_for(Google),
            return_to=current_app.config.get('WEB_PATH')
        )
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state', type=str, required=False, default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        access_token_url = 'https://accounts.google.com/o/oauth2/token'
        people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'

        args = self.reqparse.parse_args()

        # Step 1. Exchange authorization code for access token
        payload = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'redirect_uri': redirect_uri,
            'code': args['code'],
            'client_secret': current_app.config.get('GOOGLE_SECRET')
        }

        r = requests.post(access_token_url, data=payload)
        token = r.json()

        # Step 1bis. Validate (some information of) the id token (if necessary)
        google_hosted_domain = current_app.config.get("GOOGLE_HOSTED_DOMAIN")
        if google_hosted_domain is not None:
            current_app.logger.debug('We need to verify that the token was issued for this hosted domain: %s ' % (google_hosted_domain))

	    # Get the JSON Web Token
            id_token = r.json()['id_token']
            current_app.logger.debug('The id_token is: %s' % (id_token))

            # Extract the payload
            (header_data, payload_data) = fetch_token_header_payload(id_token)
            current_app.logger.debug('id_token.header_data: %s' % (header_data))
            current_app.logger.debug('id_token.payload_data: %s' % (payload_data))

            token_hd = payload_data.get('hd')
            if token_hd != google_hosted_domain:
                current_app.logger.debug('Verification failed: %s != %s' % (token_hd, google_hosted_domain))
                return dict(message='Token is invalid %s' % token), 403
            current_app.logger.debug('Verification passed')

        # 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 = setup_user(profile.get('email'), profile.get('groups', []), current_app.config.get('GOOGLE_DEFAULT_ROLE', 'View'))

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
        login_user(user)
        db.session.commit()
        db.session.refresh(user)

        return redirect(return_to, code=302)
Esempio n. 35
0
def authorize(*args, **kwargs):
    """
    Authorization endpoint for OAuth2.
    Successful interaction with this endpoint yields an access token
    for the requesting client.

    Please note that I am not very confident of this current approach's
    security; it involves a shaky workaround to implement the resource owner
    password credentials (ROPC) OAuth2 flow for the official mobile app.

    Mobile applications are considered "public" clients because their client
    credentials (client id and client secret) cannot reliably be kept secure,
    since if they are bundled with the application they are potentially accessible
    to anyone who has the app on their phone.

    But the ROPC flow works like this:

        * User opens the mobile app and is greated with a native login view.
        * User enters their credentials and hits "Login".
        * POST to `/oauth/authorize?client_id=<CLIENT_ID>&response_type=code&grant_type=resource_owner_credentials&scope=userinfo&redirect_uri=<your redirect uri>`, with data: `{"email": <user email>, "password": <user password>}`
        * Server finds client with the specified client id, checks that the client is allowed the ROPC grant type, and if so, authenticates the user with the provided credentials.
        * If successful, the server responds with the header `Location: <your redirect uri>/?code=<your authorization code>`
        * In the mobile app, you can extract the authorization code and exchange it for the access token by sending a GET request to `/oauth/token?code=<your authorization code>&grant_type=authorization_code&client_id=<your client_id>&redirect_uri=<your redirect uri>`
        * If successful, the server responds with something like: `{"refresh_token": "Z9QAolFevdLXjO7OR1ImJ1pkqc248j", "scope": "userinfo", "access_token": "wvjTny7CXEVEQSfyxC1MSP11NEPnlj", "token_type": "Bearer"}`

    Clearly this is not the ideal flow and not really according to OAuth2 specifications. Anyone can discover and use the client id to imitate the official client since there is no (and cannot be any) verification of client authenticity with a client secret. Unless the provider server is secured with SSL/HTTPS, passwords are sent out in the open. It is only a temporary solution.

    A potential long-term solution is to use OAuth2's "client credentials" flow and consider each *installation* of the mobile application as a *separate* client. Thus each installation has its own unique client id and client secret, relevant only for that particular user. This still may not be very good (haven't thought it all the way through).

    For some more info see:

        * https://stackoverflow.com/questions/14574846/client-authentication-on-public-client
        * https://stackoverflow.com/questions/6190381/how-to-keep-the-client-credentials-confidential-while-using-oauth2s-resource-o
    """

    # NB: request.values refers to values from both
    # the response body and the url parameters.
    client_id = request.values.get('client_id')
    client = Client.query.get(client_id)
    grant_type = request.values.get('grant_type')

    # Check to see if the requested scope(s) are permitted
    # for this client.
    # Since this is a workaround (see below), this looks a bit weird.
    # But if the expected kwargs isn't processed (flask_oauthlib only processes them
    # if it is a GET request), collect the scope information another way.
    #        GET                    POST
    scopes = kwargs.get('scopes') or request.values.get('scope').split()
    client.validate_scopes(scopes)

    # Check to see if the requested grant type is permitted
    # for this client.
    client.validate_grant_type(grant_type)

    if request.method == 'GET':
        kwargs['client'] = client
        if grant_type == 'authorization_code':
            # The user must authenticate herself,
            # if not already authenticated.
            if not current_user.is_authenticated():
                return redirect(
                    url_for('security.login', next=url_for('authorize')))

            kwargs['user'] = current_user

            return render_template('authorize.html', **kwargs)

        response = jsonify({
            'message':
            'Invalid grant type for this request. Perhaps you mean a grant_type of `authorization_code`?'
        })
        response.status_code = 400
        return response

    elif request.method == 'POST':

        # Authenticate on behalf of the user.
        # ONLY TRUSTED CLIENTS should be allowed this grant type.
        # i.e. only official clients, all others should be using
        # grant type of `authorization_code`.
        # This is enforced since clients are by default restrited to only
        # the `authorization_code` grant type, unless explicitly set otherwise.
        # Note: this is a workaround since flask_oauthlib does not support the "password"
        # grant type at the moment (which is equivalent to "resource_owner_credentials").
        if grant_type == 'resource_owner_credentials':
            form = LoginForm(request.form, csrf_enabled=False)
            if form.validate_on_submit():
                login_user(form.user)
                return True
            else:
                print(form.errors)
                return False

        # Otherwise, assume this request is coming from
        # the authorization_code's authorize form.
        else:
            confirm = request.form.get('confirm', 'no')
            return confirm == 'yes'
Esempio n. 36
0
def test_access_matrix(script_info_cli_list):
    """Test of combinations of cli commands."""
    script_info = script_info_cli_list
    runner = CliRunner()

    user_roles = {
        '*****@*****.**': ['admin'],
        '*****@*****.**': ['admin'],
        '*****@*****.**': ['opener'],
        '*****@*****.**': ['editor'],
        '*****@*****.**': ['opener'],
    }

    action_roles = {
        'open': ['admin', 'opener'],
        'edit': ['admin', 'editor'],
    }

    for role in {role for roles in user_roles.values() for role in roles}:
        # Role creation
        result = runner.invoke(roles_create, [role], obj=script_info)
        assert result.exit_code == 0

    for email, roles in user_roles.items():
        result = runner.invoke(users_create,
                               [email, '--password', '123456', '-a'],
                               obj=script_info)
        assert result.exit_code == 0

        for role in roles:
            # Role creation
            result = runner.invoke(roles_add, [email, role], obj=script_info)
            assert result.exit_code == 0

    def role_args(roles):
        """Generate role arguments."""
        for role in roles:
            yield 'role'
            yield role

    for action, roles in action_roles.items():

        result = runner.invoke(access,
                               ['allow', action] + list(role_args(roles)),
                               obj=script_info)
        assert result.exit_code == 0

    result = runner.invoke(
        access, ['deny', 'edit', 'user', '*****@*****.**'],
        obj=script_info)
    assert result.exit_code == 0

    result = runner.invoke(
        access,
        ['allow', '-a', '1', 'edit', 'user', '*****@*****.**'],
        obj=script_info)
    assert result.exit_code == 0

    permission_open = DynamicPermission(ActionNeed('open'))
    permission_edit = DynamicPermission(ActionNeed('edit'))

    permission_edit_1 = DynamicPermission(ParameterizedActionNeed('edit', 1))
    permission_edit_2 = DynamicPermission(ParameterizedActionNeed('edit', 2))

    user_permissions = {
        '*****@*****.**': {
            True: [
                permission_open,
                permission_edit,
                permission_edit_1,
                permission_edit_2,
            ],
        },
        '*****@*****.**': {
            True: [permission_open],
            False: [
                permission_edit,
                permission_edit_1,
                permission_edit_2,
            ],
        },
        '*****@*****.**': {
            True: [permission_open],
            False: [
                permission_edit,
                permission_edit_1,
                permission_edit_2,
            ],
        },
        '*****@*****.**': {
            True: [
                permission_edit,
                permission_edit_1,
                permission_edit_2,
            ],
            False: [permission_open],
        },
        '*****@*****.**': {
            True: [
                permission_open,
                permission_edit_1,
            ],
            False: [
                permission_edit,
                permission_edit_2,
            ],
        },
    }

    for email, permissions in user_permissions.items():
        with script_info.create_app(None).test_request_context():
            user = _security.datastore.find_user(email=email)
            login_user(user)
            identity = g.identity

            for can, actions in permissions.items():
                for action in actions:
                    assert action.allows(identity) == can, identity

    result = runner.invoke(access, ['remove', 'edit'], obj=script_info)
    assert result.exit_code == 2

    result = runner.invoke(access,
                           ['show', '-e', '*****@*****.**'],
                           obj=script_info)
    assert result.exit_code == 0
    assert 'user:[email protected]:edit::deny' in result.output

    result = runner.invoke(access, ['show', '-r', 'editor'], obj=script_info)
    assert 'role:editor:edit::allow\n' == result.output
    assert result.exit_code == 0

    #
    # Remove all permissions.
    #
    for action, roles in action_roles.items():

        result = runner.invoke(access,
                               ['remove', action] + list(role_args(roles)),
                               obj=script_info)
        assert result.exit_code == 0

    result = runner.invoke(
        access, ['remove', 'edit', 'user', '*****@*****.**'],
        obj=script_info)
    assert result.exit_code == 0

    result = runner.invoke(
        access,
        ['remove', '-a', '1', 'edit', 'user', '*****@*****.**'],
        obj=script_info)
    assert result.exit_code == 0

    # All authorizations should be removed.
    result = runner.invoke(access,
                           ['show', '-r', '*****@*****.**'],
                           obj=script_info)
    assert result.exit_code == 0
    assert result.output == ''
Esempio n. 37
0
 def login_user(self, user):
     """Perform any login actions."""
     if not current_security.confirmable or \
             current_security.login_without_confirmation:
         after_this_request(_commit)
         login_user(user)
Esempio n. 38
0
 def login_user_silent(self, user):
     login_user(user)
     user.login_count -= 1
     self.put(user)
    def post(self):
        if "ping" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Ping is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get('PING_CLIENT_ID'),
            redirectUri=current_app.config.get('PING_REDIRECT_URI'),
            return_to=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state',
                                   type=str,
                                   required=False,
                                   default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        # take the information we have received from the provider to create a new request
        params = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'scope': 'openid email profile address',
            'redirect_uri': redirect_uri,
            'code': args['code']
        }

        # you can either discover these dynamically or simply configure them
        access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL')
        user_api_url = current_app.config.get('PING_USER_API_URL')

        # the secret and cliendId will be given to you when you signup for the provider
        basic = base64.b64encode(
            bytes('{0}:{1}'.format(client_id,
                                   current_app.config.get("PING_SECRET"))))
        headers = {'Authorization': 'Basic {0}'.format(basic.decode('utf-8'))}

        # exchange authorization code for access token.
        r = requests.post(access_token_url, headers=headers, params=params)
        id_token = r.json()['id_token']
        access_token = r.json()['access_token']

        # fetch token public key
        header_data = fetch_token_header_payload(id_token)[0]
        jwks_url = current_app.config.get('PING_JWKS_URL')

        # retrieve the key material as specified by the token header
        r = requests.get(jwks_url)
        for key in r.json()['keys']:
            if key['kid'] == header_data['kid']:
                secret = get_rsa_public_key(key['n'], key['e'])
                algo = header_data['alg']
                break
        else:
            return dict(message='Key not found'), 403

        # validate your token based on the key it was signed with
        try:
            current_app.logger.debug(id_token)
            current_app.logger.debug(secret)
            current_app.logger.debug(algo)
            jwt.decode(id_token,
                       secret.decode('utf-8'),
                       algorithms=[algo],
                       audience=client_id)
        except jwt.DecodeError:
            return dict(message='Token is invalid'), 403
        except jwt.ExpiredSignatureError:
            return dict(message='Token has expired'), 403
        except jwt.InvalidTokenError:
            return dict(message='Token is invalid'), 403

        user_params = dict(access_token=access_token, schema='profile')

        # retrieve information about the current user.
        r = requests.get(user_api_url, params=user_params)
        profile = r.json()

        user = User.query.filter(User.email == profile['email']).first()

        # if we get an sso user create them an account
        if not user:
            user = User(email=profile['email'],
                        active=True,
                        role='View'
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                        )
            db.session.add(user)
            db.session.commit()
            db.session.refresh(user)

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(),
                              identity=Identity(user.id))
        login_user(user)

        return redirect(return_to, code=302)