Может кто-нибудь объяснить шаги по внедрению процесса входа в систему с другими поставщиками OAuth2 Эта ссылка Google Cloud Endpoints с другим провайдером oAuth2 дает мало информации о написании пользовательской аутентификации, но я думаю, что для начинающих, подобных мне, недостаточно, предоставьте подробные инструкции. Особенно заинтересованы в Facebook.
Вход в Facebook в Google Cloud Endpoints
Ответ 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
Примечание:
- Я использую эту библиотеку для проверки подлинности facebook на сервере: https://github.com/pythonforfacebook/facebook-sdk
Ответ 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 (и, вероятно, может быть расширен для поддержки других социальных провайдеров без лишних хлопот). Я знаю, что это напрямую не отвечает на исходный вопрос, но кажется, что это достаточно похоже на то, что я решил поделиться с ним.