Beispiel #1
0
def verify_web_api(req):
    try:
        client_api_key = _extract_api_key(req)
        if not client_api_key:
            raise ValueError('Missing authorization credentials')

        app_id = request.view_args['app_id']
        (api_key, ips, ops) = (db.session.query(
            Apps.api_key, Apps.allowed_ips,
            Apps.options).filter_by(_id=app_id, _deleted=0).one_or_none())
        if client_api_key != api_key:
            raise ValueError('API key does not match')
        if ips:
            allowed_ips = ips.split('|')
            remote_ip = get_remote_ip(req)
            if remote_ip not in allowed_ips and remote_ip != '127.0.0.1':
                logger.info('IP is not allowed',
                            ip=remote_ip,
                            whitelist=allowed_ips)
                raise PermissionError()

        app = Apps()
        app._id = app_id
        app.is_authenticated = True
        app.options = ops
        return app
    except PermissionError:
        abort(403, 'Your IP is not allowed to access this API')
    except Exception as e:
        logger.debug('API authorization failed. ' + repr(e))
        return None
Beispiel #2
0
    def _make_web_authorize_success_response(self, token: Tokens):
        parent_resp = super()._make_web_authorize_success_response(token)

        amz_pay_enabled = self.channel.option_enabled('amazon_pay')
        if not amz_pay_enabled or self.log.intent != AuthLogs.INTENT_PAY_WITH_AMAZON:
            return parent_resp

        resp = make_response(parent_resp)
        cookie_object = {
            "access_token": token.access_token,
            "max_age": 3300,
            "expiration_date": unix_time_millis(token.expires_at),
            "client_id": self.channel.client_id,
            "scope": self._perms_for_pay()
        }
        domain = self.session.lpwa_domain or self.extract_domain_for_cookie(
            url=self.log.callback_uri)
        resp.set_cookie(key='amazon_Login_state_cache',
                        value=up.quote(json.dumps(cookie_object), safe=''),
                        domain=domain,
                        expires=None,
                        max_age=None)
        resp.set_cookie(key='amazon_Login_accessToken',
                        value=token.access_token,
                        domain=domain,
                        expires=None,
                        max_age=3300)
        logger.debug('Set cookie for amazon pay', domain=domain or 'localhost')
        return resp
Beispiel #3
0
    def _parse_oauth_state(self,
                           state: str) -> Tuple[AuthLogs, OAuthSessionParams]:
        try:
            log_id, args = jwt_token_helper.decode(token=state)
            log: Optional[AuthLogs] = AuthLogs.query.filter_by(
                _id=log_id).one_or_none()

            if not log or log.nonce != args.get('_nonce'):
                logger.debug('Invalid OAuth state or nonce does not match')
                raise BadRequestError('Invalid OAuth state')

            if log.status != AuthLogs.STATUS_UNKNOWN:
                logger.debug(
                    'Validate OAuth state failed. Illegal auth log status.',
                    status=log.status,
                    expected=AuthLogs.STATUS_UNKNOWN)
                raise BadRequestError('Invalid OAuth state')

            if log.provider != self.provider:
                logger.warn(
                    'Provider in OAuth state does not match with current provider',
                    provider=log.provider,
                    expected=self.provider)
                raise PermissionDeniedError(
                    'OAuth state invalid, provider does not match')

            return log, OAuthSessionParams(data=args)
        except TokenParseError as e:
            logger.warning('Parse OAuth state failed',
                           error=e.message,
                           token=state)
            raise BadRequestError('Invalid OAuth state')
Beispiel #4
0
    def handle_authorize_error(self, params: OAuthCallbackParams):
        self.log, self.session = self._parse_oauth_state(params.state)
        self.log.status = AuthLogs.STATUS_FAILED
        logger.debug('Parse OAuth state result',
                     sub=self.log._id,
                     **self.session.to_dict())

        error, desc = self._get_error(params.to_dict(), action='authorize')
        logger.debug('Authorize with provider failed',
                     provider=self.provider.upper(),
                     error=error,
                     message=desc)

        error_data = {
            'error': self.ERROR_AUTHORIZE_FAILED,
            'msg': '{}, {}'.format(error, desc),
            'nonce': self.session.nonce,
            'provider': self.provider
        }
        if self.log.callback_if_failed:
            redirect_url = add_params_to_uri(uri=self.log.callback_if_failed,
                                             **error_data)
            return redirect(redirect_url)
        else:
            return jsonify(error_data)
Beispiel #5
0
def get_authorized_profile():
    auth_token = request.form.get('auth_token')
    try:
        body = oauth_serv.get_authorized_profile(auth_token=auth_token, params=request.form)
        logger.debug('Profile authenticated', style='hybrid', **body)
        return jsonify(body)
    except TokenParseError as e:
        logger.warning('Parse auth token failed', error=e.message, auth_token=auth_token)
        abort(400, 'Invalid auth token')
Beispiel #6
0
 def decode(self, token: str) -> Tuple[str, Dict[str, Any]]:
     try:
         payload = jwt.decode(token, key=self.key, issuer=self.issuer,
                              algorithms=['HS256'])
         if int(payload['exp']) < int(time.time()):
             raise TokenParseError('Token expired')
         return payload['sub'], payload['data']
     except TokenParseError:
         raise
     except Exception as e:
         logger.debug('Decore JWT token error', error=str(e))
         raise TokenParseError('Token malformed')
Beispiel #7
0
    def _allow_get_scope_id(self):
        ss = SystemSettings.all_as_dict()
        return_scoped_id = ss.get('return_scoped_id', 'never')
        logger.debug('System variables', return_scoped_id=return_scoped_id)

        if return_scoped_id == 'always':
            return True
        elif return_scoped_id == 'never':
            return False
        level = (db.session.query(Admins.level).join(
            Apps, and_(Admins._id == Apps.owner_id,
                       Apps._id == self.app_id)).scalar())
        return Admins.check_has_plus_level(provider=self.provider, level=level)
Beispiel #8
0
def parse_auth_token(auth_token: str) -> Tuple[AuthLogs, Dict[str, Any]]:
    log_id, args = easy_token_helper.decode(token=auth_token)
    log: Optional[AuthLogs] = AuthLogs.query.filter_by(_id=log_id).one_or_none()

    if not log or log.nonce != args.get('_nonce'):
        logger.debug('Invalid auth token or nonce does not match')
        raise BadRequestError('Invalid auth token')

    if log.status not in [AuthLogs.STATUS_AUTHORIZED, AuthLogs.STATUS_WAIT_REGISTER]:
        logger.debug('Validate auth token failed. Illegal auth log status.',
                     status=log.status,
                     expected=[AuthLogs.STATUS_AUTHORIZED, AuthLogs.STATUS_WAIT_REGISTER])
        raise BadRequestError('Invalid auth token')

    return log, args
Beispiel #9
0
def parse_associate_token(associate_token: str):
    social_id, args = easy_token_helper.decode(token=associate_token)
    log: Optional[AssociateLogs] = AssociateLogs.query \
        .filter_by(dst_social_id=social_id) \
        .order_by(AssociateLogs._id.desc()).first()

    if not log or log.nonce != args.get('_nonce'):
        logger.debug('Invalid associate token or nonce does not match')
        raise BadRequestError('Invalid associate token')

    if log.status != AssociateLogs.STATUS_NEW:
        logger.debug('Illegal associate log status', status=log.status, expected=AssociateLogs.STATUS_NEW)
        raise BadRequestError('Invalid associate token')

    return log
Beispiel #10
0
    def decode(self, token: str) -> Tuple[str, Dict[str, Any]]:
        try:
            data = base64decode(token[4:], urlsafe=True)
            payload = self.deserialize(packed=data)
            sign = payload['sign']
            del payload['sign']
            raw = urlparse.urlencode(payload)
            expected_sign = calculate_hmac(self.key, raw, digestmod=hashlib.sha256)

            if sign != expected_sign:
                msg = 'Invalid signature, expected sign: {}, actual sign: {}'
                raise TokenParseError(msg.format(expected_sign, sign, token))
            if payload['exp'] < int(time.time()):
                raise TokenParseError('Token expired')
            return payload['sub'], payload['data']
        except TokenParseError:
            raise
        except Exception as e:
            logger.debug('Decode easy token error', error=str(e))
            raise TokenParseError('Token malformed')
Beispiel #11
0
    def handle_authorize_success(self, params: OAuthCallbackParams):
        self.log, self.session = self._parse_oauth_state(state=params.state)
        logger.debug('Parse OAuth state result',
                     sub=self.log._id,
                     **self.session.to_dict())

        try:
            self.channel = Channels.query.filter_by(
                app_id=self.log.app_id, provider=self.provider).one_or_none()
            profile, token = self._handle_authentication(params=params)
        except Exception:
            self.log.status = AuthLogs.STATUS_FAILED
            db.session.commit()
            raise

        intent = self.log.intent
        if intent == AuthLogs.INTENT_ASSOCIATE:
            if self.session.provider != self.provider:
                self._raise_error(error='permission_denied',
                                  msg='Target provider does not match')
            elif profile.user_id:
                self._raise_error(error='conflict',
                                  msg='Profile has linked with another user')
            profile.merge_with(alias=self.session.dst_social_id)
        elif intent == AuthLogs.INTENT_LOGIN and not self.log.is_login:
            self._raise_error(
                error='invalid_request',
                msg='Social profile does not exist, should register instead')
        elif intent == AuthLogs.INTENT_REGISTER and self.log.is_login:
            self._raise_error(
                error='invalid_request',
                msg='Social profile already existed, should login instead')

        if self._is_mobile():
            return self._make_mobile_authorize_success_response(token)
        else:
            return self._make_web_authorize_success_response(token)
Beispiel #12
0
    def authorize(self, params: OAuthAuthorizeParams):
        # Verify request params and extract session data
        self.session = self._verify_and_parse_session(params=params)

        oauth_app = Apps.query.filter_by(_id=params.app_id,
                                         _deleted=0).one_or_none()
        self.channel = Channels.query.filter_by(
            app_id=params.app_id, provider=self.provider).one_or_none()
        if not oauth_app or not self.channel:
            raise NotFoundError(msg='Application or channel not found')

        if not self._is_mobile():
            allowed_uris = [
                up.unquote_plus(uri) for uri in oauth_app.get_callback_uris()
            ]
            logger.debug('Verify callback URI',
                         style='hybrid',
                         allowed_uris=allowed_uris,
                         succ_callback=params.success_callback,
                         fail_callback=params.failed_callback)

            illegal_callback_msg = (
                'Invalid callback_uri value. '
                'Check if it is registered in EasyLogin developer site')

            if not verify_callback_uri(allowed_uris, params.success_callback):
                raise PermissionDeniedError(msg=illegal_callback_msg)

            if params.failed_callback and not verify_callback_uri(
                    allowed_uris, params.failed_callback):
                raise PermissionDeniedError(msg=illegal_callback_msg)

        self.log = AuthLogs(provider=self.provider,
                            app_id=params.app_id,
                            nonce=gen_random_token(nbytes=32),
                            intent=params.intent,
                            platform=self.session.platform,
                            callback_uri=params.success_callback,
                            callback_if_failed=params.failed_callback)
        db.session.add(self.log)
        db.session.flush()
        db.session.add(
            JournalLogs(ua=request.headers.get('User-Agent'),
                        ip=get_remote_ip(request),
                        path=request.full_path,
                        ref_id=self.log._id))

        oauth_state = generate_oauth_state(self.log, **self.session.to_dict())
        if self._is_mobile():
            return jsonify({
                'channel': {
                    'client_id': self.channel.client_id,
                    'options': self.channel.get_options(),
                    'scopes': self.channel.get_permissions()
                },
                'state': oauth_state
            })
        else:
            url = self._build_authorize_uri(state=oauth_state)
            logger.debug('Authorize URL', url)
            return redirect(url)