1. Boo

    08.03.2011

    0 ↑
    0 ↓
    Здравствуйте.
    При авторизации через Яндекс, если запрашивать дополнительные параметры через AX, то Яндексовский OpenID-сервер делает POST на complete-URL.

    В результате, вот этот кусок кода:
    openid_response = consumer.complete(request.POST or request.GET, request.build_absolute_uri())
    возвращает:
    <openid.consumer.consumer.FailureResponse id='http://boobsd.ya.ru/' message="return_to does not match return URL. Expected 'http://talchuk.com/openid/complete/?janrain_nonce=2011-03-08T18%3A29%3A21Z4RZCXD', got u'http://talchuk.com/openid/complete/?janrain_nonce=2011-03-08T18%3A29%3A21Z4RZCXD'">
    Не знаю, что это может означать, ведь урлы совпадают.

    Если сказать Яндексу при авторизации, чтобы он не передавал дополнительные данные, то авторизация проходит успешно (через GET).

    Кто-нибудь знает, что может означать эта ошибка?

    P.S.: python-openid версии 2.2.1, Django - trunk
  2. Ivan Sagalaev

    09.03.2011

    0 ↑
    0 ↓

    Попробуй python-openid обновить. Такое ощущение, что они там где-то запутались между юникодом и байтами. Ещё вариант: насильно перевести содержимое POST в байтовые строки.

  3. Boo

    09.03.2011

    0 ↑
    0 ↓
    Попробуй python-openid обновить.
    Попробовал 2.2.4 и 2.2.5 - не помогло.
    Насилие над POST-данными тоже ничего не дало.
  4. nwalker.mp

    18.03.2011

    0 ↑
    0 ↓

    та же проблема. в консоль при этом выкидывает следующее:

       Verifying return_to arguments: return_to parameter janrain_nonce absent from query {u'openid.response_nonce': '2011-03-18T23:44:56ZpzygcZ', u'openid.ax.count.old_email': '1', u'openid.ax.type.email': 'http://axschema.org/contact/email', u'openid.mode': 'id_res', u'openid.signed': 'assoc_handle,ax.count.email,ax.count.first_name,ax.count.fullname,ax.count.last_name,ax.count.nickname,ax.count.old_email,ax.count.old_fullname,ax.count.old_nickname,ax.mode,ax.type.email,ax.type.first_name,ax.type.fullname,ax.type.last_name,ax.type.nickname,ax.type.old_email,ax.type.old_fullname,ax.type.old_nickname,ax.value.email.1,ax.value.fullname.1,ax.value.nickname.1,ax.value.old_email.1,ax.value.old_fullname.1,ax.value.old_nickname.1,claimed_id,identity,mode,ns,ns.ax,op_endpoint,response_nonce,return_to,signed', u'openid.ax.count.email': '1', u'openid.op_endpoint': 'http://openid.yandex.ru/server/', u'openid.ax.count.old_nickname': '1', u'openid.ax.count.nickname': '1', u'openid.ax.value.old_fullname.1': 'Alex Bubnoff', u'openid.ax.count.first_name': '0', u'openid.ax.type.last_name': 'http://axschema.org/namePerson/last', u'openid.ax.value.nickname.1': 'nwalker', u'openid.identity': 'http://openid.yandex.ru/nwalker/', u'openid.ax.value.fullname.1': 'Alex Bubnoff', u'openid.return_to': 'http://showsurf.local:8000/complete/openid/?janrain_nonce=2011-03-18T23%3A44%3A55ZL1ofCr', u'openid.ax.count.old_fullname': '1', u'openid.ax.mode': 'fetch_response', u'openid.ax.value.old_nickname.1': 'nwalker', u'openid.claimed_id': 'http://nwalker.ya.ru/', u'openid.ns.ax': u'http://openid.net/srv/ax/1.0', u'openid.ax.count.fullname': '1', u'openid.ax.value.old_email.1': 'alex.bubnoff@gmail.com', u'openid.ax.type.old_fullname': 'http://schema.openid.net/namePerson', u'openid.ax.value.email.1': 'alex.bubnoff@gmail.com', u'openid.sig': 'Ja8gNW/G2XSH6Ckf0SxkQFtFXb8=', u'openid.ax.type.fullname': 'http://axschema.org/namePerson', 'openid.ns': u'http://specs.openid.net/auth/2.0', u'openid.ax.count.last_name': '0', u'openid.ax.type.first_name': 'http://axschema.org/namePerson/first', u'openid.ax.type.old_email': 'http://schema.openid.net/contact/email', u'openid.ax.type.nickname': 'http://axschema.org/namePerson/friendly', u'openid.ax.type.old_nickname': 'http://schema.openid.net/namePerson/friendly', u'openid.assoc_handle': '{HMAC-SHA1}{4d83ee25}{b7zcMw==}'}
    

    в общем, его тут и вправду нет. зато он есть в request.GET. стало быть, у меня вопрос - а где он должен быть? сильно ли будет неправильным просто склеить GET и POST и по ним уже работать?

  5. Boo

    19.03.2011

    0 ↑
    0 ↓
    в общем, его тут и вправду нет. зато он есть в request.GET
    Он может быть как в request.GET, так и в request.POST. Спецификация это допускает.
    Зачем склеивать, если можно сделать, что-то вроде этого:
    openid_response = consumer.complete(request.POST or request.GET, request.build_absolute_uri())
  6. nwalker.mp

    19.03.2011

    1 ↑
    0 ↓
           request.POST or request.GET
    

    Это же просто выбор. Если janrain_nonce в GET, а все остальное в POST, то consumer получит только POST и вернет ту же самую ошибку.

  7. Boo

    20.03.2011

    0 ↑
    0 ↓
    Если janrain_nonce в GET, а все остальное в POST, то consumer получит только POST и вернет ту же самую ошибку.
    Прошу прощения, сглупил.
    Попытался воспроизвести вашу ошибку - ничего не получилось. janrain_nonce в POST нет, но и ошибки - тоже.
  8. nwalker.mp

    20.03.2011

    0 ↑
    0 ↓

    Я использую django-social-auth(помимо OpenID нужны еще Facebook и Google), в нем идет примерно следущее:

         # 1st stage
         consumer = openid.consumer.Consumer(...)
         request = consumer.begin(openid_url)
         if request.shouldSendRedirect(): # request.endpoint.compabilityMode() -> 
                                          # endpoint.preferredNamespace() != OPENID_2_0_MESSAGE_NS
             return HttpResponseRedirect(request.redirectURL(trust_root, return_to))
         else:
             form_tag = {'id': 'openid_message'}
             auth_html = request.htmlMarkup(trust_root, return_to,
                                            form_tag_attrs=form_tag)
             return HttpResponse(auth_html,
                                 content_type='text/html;charset=UTF-8')
    
         # 2nd stage
         query = request.POST if request.method == 'POST' else request.GET
         response = consumer.complete(dict(query.items()),
                                      request.build_absolute_uri())
         if response.status == FAILURE:
            raise ValueError('OpenID authentication failed: %s' % \
                             response.message)
    

    Велосипед, который у меня был до django-social-auth просто игнорировал preferredNamespace и работал всегда через GET. И работал. Вопрос мой начинает сводиться к тому, стоит ли вообще работать через POST, который, к тому же, не дружит с CSRF? А если стоит - могут ли параметры при работе через POST быть и в POST, и в GET, как отдает ya.ru?

  9. Boo

    20.03.2011

    0 ↑
    0 ↓
    Зачем нужно
    dict(query.items())
    ?
  10. nwalker.mp

    20.03.2011

    0 ↑
    0 ↓

    Предположу, что перестраховка на случай модификации внутри consumer.complete().

  11. Boo

    20.03.2011

    0 ↑
    0 ↓
    Все равно не получается воспроизвести вашу ошибку.
    Какая у вас версия python-openid?
    Может быть дело в showsurf.local:8000 ?
  12. nwalker.mp

    20.03.2011

    0 ↑
    0 ↓

    Какая у вас версия python-openid?

    Форк с улучшенной поддержкой юникода.

      -e git+https://github.com/vzima/python-openid.git@9c3c55f375718c25a25547bf833e40953d4ca574#egg=python_openid-2.2.5-py2.6-dev
    

    Может быть дело в showsurf.local:8000 ?

    Не. Велосипедная версия работала на том же адресе. Я внезапно вспомнил еще отличие работавшего велосипеда - он работал без storage.

  13. krvss

    29.03.2011

    0 ↑
    0 ↓
    Тоже использую Django Social Auth и иногда (ах, как я ненавижу это слово, когда речь идет о багах) получаю тот же результат. То же самое было сегодня, но опция "передать дополнительные данные" была отключена.

    Не работает на боевом (не на localhost то бишь) сервере. В логах видно интересное:

    1. janrain_nonce потом приходит отдельно через POST: janrain_nonce=2011-03-29T11%3A47%3A33Z5EFo8O
    2. Но вот что я вижу в get в параметре 'openid.return_to': 'http://conceptor.ru/social_login_complete/yandex?janrain_nonce=2011-03-29T11%3A47%3A33Z5EFo8O'

    Пока непонятно, при каких именно условиях сие происходит.
  14. krvss

    31.03.2011

    0 ↑
    0 ↓
    Сегодня вычислил, когда это происходит. Чтобы воспроизвести ошибку, нужно выбрать опцию "отправлять дополнительные данные", а в сами дополнительные данные (например, в имя) ввести несколько символов. После этого, очевидно, место для jairain_nonce в GET не хватает, и он попадает в POST. Сделал issue на github: https://github.com/openid/python-openid/issues/14
  15. Ivan Sagalaev

    31.03.2011

    0 ↑
    0 ↓

    Секундочку, я правильно понимаю, что данные в ответе сервера разъезжаются по POST-телу и query-параметрам? Если так, то это похоже на баг с нашей стороны (openid.yandex.ru), мы посмотрим.

  16. Ivan Sagalaev

    31.03.2011

    0 ↑
    0 ↓

    С другой стороны, запрос на сервер приходит с return_to, в котором как раз написан janrain_nonce, поэтому сервер на него всё и POST'ит. Больше того, по идее, это не должно ломать консумера, потому что тот же nonce приезжает и POST'ом, и только на него консумер и должен смотреть. Пока что не понимаю, буду думать дальше…

  17. Ivan Sagalaev

    31.03.2011

    0 ↑
    0 ↓

    Кстати:

    Вопрос мой начинает сводиться к тому, стоит ли вообще работать через POST, который, к тому же, не дружит с CSRF?

    POST приходится использовать, когда данных слишком много, и их могут из query-параметров порезать HTTP-сервера. Это даже не наша самодеятельность, так себя python-openid ведёт. С CSRF в этом случае всё просто: заверните вьюху, принимающую OpenID-возврат в @csrf_exempt.

  18. krvss

    01.04.2011

    0 ↑
    0 ↓
    Иван, если нужно, то могу закопировать дамп обмена данными с сервером - так сказать для наглядности. Я его уже отправил авторам python-openid, им тоже интересно что именно происходит.
  19. Ivan Sagalaev

    01.04.2011

    0 ↑
    0 ↓

    Да, давайте. Я, в общем, тоже могу наверное сымтировать такой процесс, но у меня руки скоро не дойдут.

  20. krvss

    02.04.2011

    0 ↑
    0 ↓
    Вот:

    Generated checkid_setup request to http://openid.yandex.ru/server/ with assocication {HMAC-SHA1}{4d823362}{YMPDGg==}

    [pid: 11073|app: 0|req: 12/35] 93.92.220.138 () {48 vars in 947 bytes} [Tue Mar 29 15:47:32 2011] GET /social_begin_or_assoc/yandex?openid_ya_user=username&next=/social_login_close => generated 2201 bytes in 820 msecs (HTTP/1.1 200) 3 headers in 159 bytes (0 async switches on async core 0) Verifying return_to arguments: return_to parameter janrain_nonce absent from query {u'openid.response_nonce': '2011-03-29T11:47:39Zu9jE7z', u'openid.ax.count.old_email': '0', u'openid.ax.type.email': 'http://axschema.org/contact/email', u'openid.mode': 'id_res', u'openid.signed': 'assoc_handle,ax.count.email,ax.count.first_name,ax.count.fullname,ax.count.last_name,ax.count.nickname,ax.count.old_email,ax.count.old_fullname,ax.count.old_nickname,ax.mode,ax.type.email,ax.type.first_name,ax.type.fullname,ax.type.last_name,ax.type.nickname,ax.type.old_email,ax.type.old_fullname,ax.type.old_nickname,ax.value.fullname.1,ax.value.nickname.1,ax.value.old_fullname.1,ax.value.old_nickname.1,claimed_id,identity,mode,ns,ns.ax,op_endpoint,response_nonce,return_to,signed', u'openid.ax.count.email': '0', u'openid.op_endpoint': 'http://openid.yandex.ru/server/', u'openid.ax.count.old_nickname': '1', u'openid.ax.count.nickname': '1', u'openid.ax.value.old_fullname.1': 'full name', u'openid.ax.count.first_name': '0', u'openid.ax.value.fullname.1': 'full name', u'openid.ax.value.nickname.1': 'username', u'openid.identity': 'http://openid.yandex.ru/username/', u'openid.ax.type.last_name': 'http://axschema.org/namePerson/last', u'openid.return_to': 'http://mysite.ws/social_login_complete/yandex?janrain_nonce=2011-03-29T11%3A47%3A33Z5EFo8O', u'openid.ax.count.old_fullname': '1', u'openid.ax.mode': 'fetch_response', u'openid.ax.value.old_nickname.1': 'username', u'openid.claimed_id': 'http://openid.yandex.ru/username/', u'openid.ns.ax': u'http://openid.net/srv/ax/1.0', u'openid.ax.type.old_fullname': 'http://schema.openid.net/namePerson', u'openid.ax.count.fullname': '1', u'openid.sig': '6GHupLLR5bz2kvWWdYEHLe3pJ+c=', u'openid.ax.type.fullname': 'http://axschema.org/namePerson', 'openid.ns': u'http://specs.openid.net/auth/2.0', u'openid.ax.count.last_name': '0', u'openid.ax.type.first_name': 'http://axschema.org/namePerson/first', u'openid.ax.type.old_email': 'http://schema.openid.net/contact/email', u'openid.ax.type.nickname': 'http://axschema.org/namePerson/friendly', u'openid.ax.type.old_nickname': 'http://schema.openid.net/namePerson/friendly', u'openid.assoc_handle': '{HMAC-SHA1}{4d823362}{YMPDGg==}'}

    [pid: 11073|app: 0|req: 13/38] 93.92.220.138 () {52 vars in 2391 bytes} [Tue Mar 29 15:47:40 2011] POST /social_login_complete/yandex?janrain_nonce=2011-03-29T11%3A47%3A33Z5EFo8O => generated 1281 bytes in 248 msecs (HTTP/1.1 200) 3 headers in 160 bytes (0 async switches on async core 0)
  21. uznick

    02.04.2011

    0 ↑
    0 ↓
    Подтверждаю проблему, я гугля по ней сюда и пришел: OpenID authentication failed: return_to does not match return URL. Expected 'http://127.0.0.1:8080/complete/yandex/?janrain_nonce=2011-04-02T22%3A09%3A27ZWzD96L', got u'http://127.0.0.1:8080/complete/yandex/?janrain_nonce=2011-04-02T22%3A09%3A27ZWzD96L'
  22. krvss

    03.04.2011

    0 ↑
    0 ↓
    По справедливости надо сказать, что, например, myopenid.com провоцирует точно такую же проблему, если указано что-то в контактной информации. Правда цепочка такая, что myopenid сделан на том же движке python-openid (это проект janrain), я так понимаю что и Яндекс на нем работает. Получается такой круг забавный, что клиент и библиотека python-openid не вполне стыкуются друг с другом.
  23. Ivan Sagalaev

    04.04.2011

    0 ↑
    0 ↓

    Да, мы тоже на python-openid. Хотя у меня постоянно теплится желание написать с нуля нормальную библиотеку :-). Останавливает только полное отсутствие смысла.

  24. krvss

    28.04.2011

    0 ↑
    0 ↓
    Общими усилиями проблема решена в коммите fd1d841703d095a769f4 django-social-auth. Я сделал merge, так что обновляйтесь и получайте удовольствие :)
  25. Виктор Борнов

    28.04.2011

    0 ↑
    0 ↓
    Общими усилиями проблема решена в коммите fd1d841703d095a769f4 django-social-auth.
    О, очень вовремя!
    Спасибо!

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.