Подтвердить что ты не робот

Вход в Facebook в Google Cloud Endpoints

Может кто-нибудь объяснить шаги по внедрению процесса входа в систему с другими поставщиками OAuth2 Эта ссылка Google Cloud Endpoints с другим провайдером oAuth2 дает мало информации о написании пользовательской аутентификации, но я думаю, что для начинающих, подобных мне, недостаточно, предоставьте подробные инструкции. Особенно заинтересованы в Facebook.

4b9b3361

Ответ 1

Вам необходимо реализовать API-интерфейсы на стороне клиента Facebook в соответствии с их документацией и средой развертывания вашего клиентского приложения (Browser vs iOS vs Android). Это включает в себя регистрацию вашего приложения с ними. Ваше зарегистрированное приложение направит пользователя пройти поток аутентификации, а в конце его клиентское приложение получит доступ к токену краткосрочного доступа. Facebook имеет несколько типов токенов доступа, но тот, который кажется вам интересным, называется токеном доступа пользователя, поскольку он идентифицирует авторизованного пользователя.

Передайте токен доступа в API облачных конечных точек через поле или заголовок. Внутри вашего кода API получают токен доступа и реализуют API Facebook, который проверяет правильность токена доступа. Первый ответ на этот вопрос SO делает его довольно простым, но вы, вероятно, захотите снова ссылаться на свою документацию. Если эта проверка пройдет, вы запустите свой код API, иначе исключите исключение.

Обычно вы также захотите внедрить механизм кэширования, чтобы предотвратить вызов API проверки на стороне сервера Facebook для каждого запроса Cloud Endpoints.

Наконец, я упомянул, что ваше клиентское приложение имеет краткосрочный токен. Если у вас есть клиентское приложение, основанное на браузере, вы, вероятно, захотите обновить его до долгого токена. У Facebook есть поток для этого, который включает в себя ваш код API, запрашивающий долгоживущий токен с короткоживущим. Затем вам нужно будет перенести этот долгожданный токен обратно в клиентское приложение, чтобы использовать его для будущих вызовов API Cloud Endpoints.

Если ваше клиентское приложение основано на iOS или Android, то ваши токены управляются кодом Facebook, и вы просто запрашиваете токены доступа из соответствующих API, когда они вам понадобятся.

Ответ 2

Итак, я действительно пытался реализовать этот пользовательский поток аутентификации. Кажется, он работает нормально, хотя, возможно, дальнейшее внимание будет уделяться безопасности.

Сначала пользователь переходит к моему приложению и проверяет подлинность с помощью facebook, приложение получило свой user_id и access_token. Затем приложение вызывает auth API на сервер с этой информацией.

class AuthAPI(remote.Service):
    @classmethod
    def validate_facebook_user(cls, user_id, user_token):
        try:
            graph = facebook.GraphAPI(user_token)
            profile = graph.get_object("me", fields='email, first_name, last_name, username')
        except facebook.GraphAPIError, e:
            return (None, None, str(e))

        if (profile is not None):
            # Check if match user_id
            if (profile.get('id', '') == user_id):
                # Check if user exists in our own datastore
                (user, token) = User.get_by_facebook_id(user_id, 'auth', user_token)
                # Create new user if not 
                if user is None:
                    #print 'Create new user'
                    username = profile.get('username', '')
                    password = security.generate_random_string(length=20)
                    unique_properties = ['email_address']
                    if (username != ''):
                        (is_created, user) = User.create_user(
                            username,
                            unique_properties,
                            email_address = profile.get('email', ''),
                            name = profile.get('first_name', ''),
                            last_name = profile.get('last_name', ''),
                            password_raw = password,
                            facebook_id = user_id,
                            facebook_token = user_token,
                            verified=False,
                        )
                        if is_created==False:
                            return (None, None, 'Cannot create user')
                        token_str = User.create_auth_token(user.get_id())
                        #print (user, token_str)
                # Return if user exists 
                if token is not None:
                    return (user, token.token, 'Successfully logged in')
                else:
                    return (None, None, 'Invalid token')
        return (None, None, 'Invalid facebook id and token')
    # Return a user_id and token if authenticated successfully
    LOGIN_REQ = endpoints.ResourceContainer(MessageCommon, 
                                           type=messages.StringField(2, required=True),
                                           user_id=messages.StringField(3, required=False),
                                           token=messages.StringField(4, required=False))    

    @endpoints.method(LOGIN_REQ, MessageCommon,
                  path='login', http_method='POST', name='login')
    def login(self, request):
        type = request.type
        result = MessageCommon()
        # TODO: Change to enum type if we have multiple auth ways
        if (type == "facebook"):
            # Facebook user validation
            user_id = request.user_id
            access_token = request.token
            (user_obj, auth_token, msg) = self.validate_facebook_user(user_id, access_token)
            # If we can get user data
            if (user_obj is not None and auth_token is not None):
                print (user_obj, auth_token)
                result.success = True
                result.message = msg
                result.data = json.dumps({
                    'user_id': user_obj.get_id(),
                    'user_token': auth_token
                })
            # If we cannot
            else:
                result.success = False
                result.message = msg
        return result

В дополнение к этому, вы можете реализовать обычный поток аутентификации пользователей, следующий здесь: http://blog.abahgat.com/2013/01/07/user-authentication-with-webapp2-on-google-app-engine/.

Это потому, что user_id и user_token, которые я получаю, были предоставлены webapp2_extras.appengine.auth.

Реализация User.get_by_facebook_id:

class User(webapp2_extras.appengine.auth.models.User):
    @classmethod
    def get_by_facebook_id(cls, fb_id, subj='auth', fb_token=""):
        u = cls.query(cls.facebook_id==fb_id).get()
        if u is not None:
            user_id = u.key.id()
            # TODO: something better here, now just append the facebook_token to a prefix
            token_str = "fbtk" + str(fb_token) 
            # get this token if it exists 
            token_key = cls.token_model.get(user_id, subj, token_str)
            print token_key, fb_token
            if token_key is None:
                # return a token that created from access_token string
                if (fb_token == ""):
                    return (None, None)
                else:
                    token = cls.token_model.create(user_id, subj, token_str)
            else: 
                token = token_key
            return (u, token)
        return (None, None)

Сервер подтверждает, что пользователь снова аутентифицирован с помощью facebook. Если он проходит, пользователь считается зарегистрированным. В этом случае сервер передает обратно user_token (сгенерированный на основе facebook_token) и user_id из нашего хранилища данных.

Любые другие вызовы API должны использовать этот user_id и user_token

def get_request_class(messageCls):
    return endpoints.ResourceContainer(messageCls, 
                               user_id=messages.IntegerField(2, required=False),
                               user_token=messages.StringField(3, required=False)) 
def authenticated_required(endpoint_method):
    """
    Decorator that check if API calls are authenticated
    """
    def check_login(self, request, *args, **kwargs):
        try:
            user_id = request.user_id
            user_token = request.user_token
            if (user_id is not None and user_token is not None):
                # Validate user 
                (user, timestamp) = User.get_by_auth_token(user_id, user_token)
                if user is not None:
                    return endpoint_method(self, request, user, *args, **kwargs )
            raise endpoints.UnauthorizedException('Invalid user_id or access_token')
        except:
            raise endpoints.UnauthorizedException('Invalid access token')


@endpoints.api(name='blah', version='v1', allowed_client_ids = env.CLIENT_IDS, auth=AUTH_CONFIG)
class BlahApi(remote.Service):

    # Add user_id/user_token to the request 
    Blah_Req = get_request_class(message_types.VoidMessage)
    @endpoints.method(Blah_Req, BlahMessage, path='list', name='list')
    @authenticated_required
    def blah_list(self, request, user):
        newMessage = BlahMessage(Blah.query().get())
        return newMessage

Примечание:

Ответ 3

Я применил этот вариант использования, добавив обработчик webapp2 для обмена токеном доступа Facebook для одного, созданного моим собственным приложением, используя SimpleAuth mixin для проверки:

class AuthHandler(webapp2.RequestHandler, SimpleAuthHandler):
    """Authenticates a user to the application via a third-party provider.

    The return value of this request is an OAuth token response.

    Only a subset of the PROVIDERS specified in SimpleAuthHandler are currently supported.
    Tested providers: Facebook
    """
    def _on_signin(self, data, auth_info, provider):
        # Create the auth ID format used by the User model
        auth_id = '%s:%s' % (provider, data['id'])
        user_model = auth.get_auth().store.user_model
        user = user_model.get_by_auth_id(auth_id)

        if not user:
            ok, user = user_model.create_user(auth_id)
            if not ok:
                logging.error('Unable to create user for auth_id %s' % auth_id)
                self.abort(500, 'Unable to create user')

        return user

    def post(self):
        # Consider adding a check for a valid endpoints client ID here as well.

        access_token = self.request.get('x_access_token')
        provider = self.request.get('x_provider')

        if provider not in self.PROVIDERS or access_token is None:
            self.abort(401, 'Unknown provider or access token')

        auth_info = {'access_token': access_token}
        fetch_user_info = getattr(self, '_get_%s_user_info' % provider)
        user_info = fetch_user_info(auth_info)

        if 'id' in user_info:
            user = self._on_signin(user_info, auth_info, provider)
            token = user.create_bearer_token(user.get_id())

            self.response.content_type = 'application/json'
            self.response.body = json.dumps({
                'access_token': token.token,
                'token_type': 'Bearer',
                'expires_in': token.bearer_token_timedelta.total_seconds(),
                'refresh_token': token.refresh_token
            })
        else:
            self.abort(401, 'Access token is invalid')

Обменный токен доступа может передаваться по каждому запросу конечных точек в заголовке авторизации или как часть сообщения RPC, если вы предпочитаете. Вот пример его чтения из заголовка:

def get_current_user():
    token = os.getenv('HTTP_AUTHORIZATION')
    if token:
        try:
            token = token.split(' ')[1]
        except IndexError:
            pass

    user, _ = User.get_by_bearer_token(token)
    return user

Я отправил полный пример в Github: https://github.com/loudnate/appengine-endpoints-auth-example

Ответ 4

Таким образом, ни одно тело не пролило свет на сторону клиентской части Android. Поскольку в этом случае вам не нужен логин Google, значит, код для получения дескриптора api будет выглядеть так:

private Api getEndpointsApiHandle() {
    Api.Builder api = new Api.Builder(HTTP_TRANSPORT, JSON_FACTORY, null);
    api.setRootUrl(yourRootUrl);
    return api.build();
}

Если вы заметили; Вам потребуется передать null в качестве учетных данных. Этот код работает как шарм

Ответ 5

Я тоже написал собственное решение этой проблемы. Вы можете проверить код здесь: https://github.com/rggibson/Authtopus

Authtopus - это библиотека python для пользовательской аутентификации с облачными конечными точками Google. Он поддерживает основные учетные записи пользователей и паролей + логины, а также логины через Facebook и Google (и, вероятно, может быть расширен для поддержки других социальных провайдеров без лишних хлопот). Я знаю, что это напрямую не отвечает на исходный вопрос, но кажется, что это достаточно похоже на то, что я решил поделиться с ним.