Facebook登录Google Cloud Endpoints

use*_*986 22 google-app-engine oauth-2.0 google-cloud-endpoints

有人可以解释与其他OAuth2提供商实施登录过程的步骤此链接Google Cloud Endpoints与另一个oAuth2提供商提供的关于编写自定义身份验证的信息很少,但我想对于像我这样的初学者来说还不够,请详细说明.特别是对Facebook感兴趣.

Pau*_*ulR 20

您需要根据他们的文档以及您将客户端应用程序部署到的环境(浏览器vs iOS与Android)实施Facebook的客户端API.这包括向他们注册您的应用程序.您注册的应用程序将指导用户完成身份验证流程,最后您的客户端应用程序将可以访问短期访问令牌.Facebook有多种类型的访问令牌,但是你感兴趣的那种称为用户访问令牌,因为它识别了授权用户.

通过字段或标头将访问令牌传递到您的Cloud Endpoints API.在您的API代码内部接收访问令牌并实施Facebook的API,以检查访问令牌的有效性.关于这个问题的第一个答案使它看起来很容易,但你可能想再次引用他们的文档.如果该检查通过,那么您将运行您的API代码,否则抛出异常.

您通常还希望实现一种缓存机制,以防止为每个Cloud Endpoints请求调用Facebook服务器端验证API.

最后,我提到您的客户端应用程序有一个短期令牌.如果您有一个基于浏览器的客户端应用程序,那么您可能希望将其升级为长期令牌.Facebook也有这样的流程,其中涉及您的API代码请求具有短期存在的长期令牌.然后,您需要将该长期令牌转移回客户端应用程序,以用于将来的Cloud Endpoints API调用.

如果您的客户端应用程序是基于iOS或Android的,则您的令牌由Facebook代码管理,您只需在需要时从相应的API请求访问令牌.


Nat*_* Do 5

所以我实际上试图实现自定义身份验证流程.虽然可能会在安全方面进一步考虑,但似乎工作正常.

首先,用户转到我的应用程序并通过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
Run Code Online (Sandbox Code Playgroud)

除此之外,您可能还需要按照以下说明实现普通用户身份验证流程: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)
Run Code Online (Sandbox Code Playgroud)

服务器验证用户是否再次使用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
Run Code Online (Sandbox Code Playgroud)

注意: