autenticação no google, pro appengine

Google AppEngineAcabo de resolver um problema que vem me irritando muito nos últimos meses: sincronizar uma quande quantidade de dados com o appengine.

Depois de passar muita raiva com o bulkloader, que inevitavelmente acaba gerando esses Datastore Timeout, decidi fazer um outro esquema que funcionou muito melhor. Um dia coloco aqui, mas não agora.

O lance é que pra enviar os dados, habilitei o https e também autenticação. Assim, os blocos de dados podem ser sincronizados com segurança, apenas por um administrador da aplicação.

O problema foi fazer meu script se autenticar no Google Accounts. Apesar da API de autenticação do appengine, preferi não usá-la pois não há necessidade no meu caso, e optei por usar o formulário genérico do Google.

Encontrei este documento com alguns exemplos e dúvidas, mas não foi suficiente. Finalmente, entendi como o negócio funciona:

  • Faz um POST no serviço do Google Accounts enviando usuário, senha, nome de identificação e URL do app;
  • O Google Accounts irá fornecer um token, que então deve ser passado pro sistema de autenticação do próprio app (igual ao dev_appserver.py)
  • Nesse último request, o Google fornece um Cookie chamado ACSID, que deverá ser usado em todos as próximas requisições.

E pra resolver esse problema, escrevi uma classe chamada GoogleAuth, que recebe os dados necessários, se autentica no Google Accounts, e passa a fornecer o ACSID como string. Em caso de falha, tipo usuário ou senha errada, a classe gera um ValueError.

#!/usr/bin/env python
# coding: utf-8

import sys, urllib, urllib2

class GoogleAuth:
    cookie = ''

    # save the ACSID cookie before redirecting
    class RedirectHandler(urllib2.HTTPRedirectHandler):
        def __init__(self, klass): self.klass = klass
        def http_error_302(self, req, fp, code, msg, headers):
            try: self.klass.cookie = headers.get('Set-Cookie').split(';')[0]
            except: pass
            return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)

    # authenticate
    def __init__(self, appurl, appname, username, password):
        googauth = 'https://www.google.com/accounts/ClientLogin'

        # prepare the request data
        request_data = dict(
            Email=username,
            Passwd=password,
            source=appname,
            service='ah',
            accountType='HOSTED_OR_GOOGLE',
            )

        # get the token from google auth
        fd = urllib2.urlopen(googauth, urllib.urlencode(request_data))
        auth_dict = dict(x.split('=') for x in fd.read().split('\n') if x)
        fd.close()

        token = auth_dict.get('Auth')
        if auth_dict.get('Error') or not token:
            raise ValueError('authentication failed.')

        # get the ACSID cookie for further athenticated requests
        opener = urllib2.build_opener(self.RedirectHandler(self))
        fd = opener.open(appurl+'_ah/login?'+urllib.urlencode({'continue':appurl, 'auth':token}))
        fd.close()

    # return the cookie
    def __str__(self):
        return self.cookie

if __name__ == '__main__':
    appurl = 'http://myapp.appspot.com/'
    appname  = 'My App'
    username = 'foo'
    password = 'bar'

    # authenticate
    try: acsid = GoogleAuth(appurl, appname, username, password)
    except Exception, e:
        print str(e)
        sys.exit(1)

    # now access resources that require authentication...
    print 'authentication cookie:', acsid
    request = urllib2.Request(appurl, headers={'Cookie':acsid})
    chunk = urllib2.urlopen(request).read()
Anúncios

One Comment on “autenticação no google, pro appengine”

  1. gm disse:

    boa cabelo, gostei da limpeza :D


Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s