Плохой ответ от idP в Auth Code Exchange при соединении аккаунта Google с Rails и DialogFlow - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь создать приложение rails в качестве бэкенда для пользовательских действий Google. Приблизительно пользовательский поток должен был бы вызывать мое пользовательское действие через "talk to my_action ", что потребовало бы от них входа в систему. Оттуда я мог бы продолжить извлечение информации о пользователях из их Google Счета. Проблема, с которой я сейчас сталкиваюсь, связана с процессом привязки аккаунта.

Итак, текущий поток таков:

'talk to my_action ' >> пользователю предлагается просмотреть приложение Google Home для привязки учетных записей >> пользователь нажимает на ссылку, чтобы связать свои учетные записи >> перенаправляется на страницу, где они выбирают учетные записи пользователей / входят в систему с учетной записью Google >> ' Плохой ответ от IdP в Exchange Auth Code Exchange '. Я искал высоко и низко относительно этого сообщения об ошибке, но я мог найти только одно относительно этого конкретного сообщения.

(Я не могу опубликовать скриншот из-за отсутствия репутации, извините! Но по сути экран ошибок - просто *Bad response from IdP in Auth Code Exchange* и ссылка на *Re-run linking flow*, которая все еще не работает.)

В настоящее время я использую гем 'omniauth-google-oauth2' для аутентификации в Google через OAuth2 в OmniAuth. Я не слишком уверен насчет части omniauth, но обмен oauth-кодом должен происходить, как описано в здесь .

Я также использую гем google_assistant, в частности ветвь assistant-api-v2. Этот драгоценный камень не обновлялся некоторое время, и, возможно, не был широко использован / протестирован, так что это может быть потенциальной причиной. Мой сервер размещен на heroku, и я использую DialogFlow в качестве инструмента выполнения. В DialogFlow я уже включил webhooks для своего приложения /myapp/google_assistant и поставил флажок Sign-In Required.

Что касается настроек привязки моих аккаунтов в Действиях на панели инструментов Google, у меня

  1. Установите тип связывания только Oauth, а тип предоставления неявным.
  2. Установите идентификатор клиента как мой идентификатор клиента oauth2
  3. URL авторизации как ' my-app.com / users / auth / google_oauth2 ' (URL-адрес токена не требуется)
  4. Установить область как gmail
  5. Установить информацию о тестировании как «имя пользователя: test@email.com, пароль: пароль». Я еще не знаю, каков правильный формат для этого поля.

Журналы на моем сервере веб-приложений Heroku во время процесса связывания:

2018-07-05T10:44:16.955840 #4]  INFO -- : [a5b63d61-d97d-4b04-a10b-b3fee18c1c7d] Started GET "/users/auth/google_oauth2?response_type=token&client_id=*my_client_id*&redirect_uri=https://oauth-redirect.googleusercontent.com/r/*my_project_id*&scope=https://mail.google.com/&state=AA7-RQxyASRZMH3DU8v1lXOx08dXZdlDa_8qIqoQfcdSdbT2ltPpyO4JVYGo7SZWmCLgu2oq1aPnojP_ygDQQgjH-3fz7EFopdKVl1WqABA_uhSfwxGqN5ywmLXWE-Y74AFMTs4sEVkG_ctUxCz7oMXDumdPELRjHlhO0VRhcJXQFhCZ_2OOrEDKkST-pPLy_cD4T2Pptni9JQj8QUeXYkklg1I-836q4zqt6vOA9mCEEFqS_h0hwaVizBypk8joO85dOibe8w4OYlp4BHDegSQ_97oPeHJu_7TSJg9M2fAyZqww0XDNjwQAFCwFR1Z0fZ3b4RySlG5Uy_yLr_F5wLbMLvLa0mX63mwuf3hOUX4zoCpBoMnBcLi7hFUiaPj1wpTBhmrAZ05Oq6jKRcOqC-FX6yERPl5tQvNdsGdjH63mc-4J3tDL0tUzvvkYm6p0CjuOho4GiQwA1XZGvHZmOcKPLA6CKr26THAXmPVJSQmiIH1CSAuAypguuiNy0yhmMKgTH_WH5M8banpYYob-Yv2jVqu2H5f2RjF0i_XApCcHUj6VoNEg0cFYiMYaiIKJRH20-HpymW3IgOEd_2TjPg5yKEbYN5bN7C-zILVLC_1qyofyze0ag0lxgvsx3kvbAEI1MLpVAF0EACZeHJujzb8YM0vmOB8FuQLOeSanyZ8zrwrWXRzu9hSBQ4eHegsSneb5CugcQkt09uSnQhOTGGwOi0TVk_f6Sw" for *my_ip* at 2018-07-05 10:44:16 +0000

2018-07-05T10:44:16.956747+00:00 app[web.1]: I, [2018-07-05T10:44:16.956679 #4]  INFO -- omniauth: (google_oauth2) Request phase initiated.

2018-07-05T10:44:17.015111+00:00 heroku[router]: at=info method=GET path="/users/auth/google_oauth2?response_type=token&client_id=*my-client-id*&redirect_uri=https://oauth-redirect.googleusercontent.com/r/my-project-id&scope=https://mail.google.com/&state=AA7-RQxyASRZMH3DU8v1lXOx08dXZdlDa_8qIqoQfcdSdbT2ltPpyO4JVYGo7SZWmCLgu2oq1aPnojP_ygDQQgjH-3fz7EFopdKVl1WqABA_uhSfwxGqN5ywmLXWE-Y74AFMTs4sEVkG_ctUxCz7oMXDumdPELRjHlhO0VRhcJXQFhCZ_2OOrEDKkST-pPLy_cD4T2Pptni9JQj8QUeXYkklg1I-836q4zqt6vOA9mCEEFqS_h0hwaVizBypk8joO85dOibe8w4OYlp4BHDegSQ_97oPeHJu_7TSJg9M2fAyZqww0XDNjwQAFCwFR1Z0fZ3b4RySlG5Uy_yLr_F5wLbMLvLa0mX63mwuf3hOUX4zoCpBoMnBcLi7hFUiaPj1wpTBhmrAZ05Oq6jKRcOqC-FX6yERPl5tQvNdsGdjH63mc-4J3tDL0tUzvvkYm6p0CjuOho4GiQwA1XZGvHZmOcKPLA6CKr26THAXmPVJSQmiIH1CSAuAypguuiNy0yhmMKgTH_WH5M8banpYYob-Yv2jVqu2H5f2RjF0i_XApCcHUj6VoNEg0cFYiMYaiIKJRH20-HpymW3IgOEd_2TjPg5yKEbYN5bN7C-zILVLC_1qyofyze0ag0lxgvsx3kvbAEI1MLpVAF0EACZeHJujzb8YM0vmOB8FuQLOeSanyZ8zrwrWXRzu9hSBQ4eHegsSneb5CugcQkt09uSnQhOTGGwOi0TVk_f6Sw" host=*my-web-app* request_id=a5b63d61-d97d-4b04-a10b-b3fee18c1c7d fwd="*my-ip*" dyno=web.1 connect=0ms service=63ms status=302 bytes=5935 protocol=https

Что касается цепочки перенаправления процесса, я не уверен, как полностью сохранить сетевые журналы, но используя расширение Chrome, цепочка перенаправления при входе в учетную запись Google приводит к ошибке неверного ответа:

Status Code URL IP  Page Type   Redirect Type   Redirect URL    
302 https://oauth-redirect.googleusercontent.com/r/*my-project-id*?state=AA7-RQyd1KsD63DBoQF_-NfYijzGvptfXTEj8D3AwQzW_ByUe8K0UnLZVuQjE6y8txJMcabFTC4fQhxHqqpTv28_e3dDuLBpYaGKqo_urwHkswmf1pAV2da7nPoVb-n1DHe1P-xU-jzU5c8vlyWCXJPpMDZyR7K0AQ7qdDCQRtr9oPpDymPyYknGIrQB6rI9VRSFinsGhZTnno2AvtOJCkTZfe3abYSdzjB-Am3PI9p-oAwh0f6mBzUGDYCMIB-traI_INV9fSa8tS9K363pBKUBQ-YgKI5nKI8Uqbz5UduNDwB99eQSUaEZu48vabVTwGjsLUczbkA46-u-AKAV65iwTPE6e-zRI11LRgfw4uObam8S3xvL3ok9pESzwGpMlBmEO0goyg9xJa2ULatLOy2PGRcMbWcuhyp84ttzedmiD2gdidVxHafEwgSpSEqad6YLWvCtV4XGbHxyuFZXF8rjFiDUK__KaJ2G-cbzyiXaVQ-YOh80NM8QQmAIPvys-2BTteP_G-1xjRZFpgJO6-dnZw2jelcF8KkITIGTNMuLejrcCyADjusaNOMrHMkASXohnq5p0lpkjPIcEZryghziPu3soPJ8A9jj8K9Ka-CWNosv9aBAnpN4eKYlOBEVljc6W824XPYgYMW62cBHlTlqV8RMNpo_5h6LRf2UdmY2T85xk3Iuz1_1Lr8jeu-UYZILPP2sc7HrjP7eFp70qgMCUQEk_JwsyQ&code=4/AAC6qctB8IxI0ijzYibSubvzae-yNgNcVOtUrbnhORSMNRxQXi2DeZE9wqn6lLqOLkb0NYeYRU15IH8H6qP9CcY# 172.217.160.1   server_redirect temporary   https://gala-demo.appspot.com/app#redirect_state=AA7-RQxBXe_JzFx7iXtcObVa7AJ4qPiiLY_XHrtgX861Z1TlUiOLFM3ymhzxqHWCONLFXjOJQkhNyCsH35cylVBrKtyLDaE-4J7wJ-P9PS3-JEPVeaoRnm7uo4ncLPW5EMxR--onGLFNZydFbqNKhdhWTox3FkUuv2lNZB2FcY9ZuhmE7LwiMXFYatawmFXpjZ0QdLkKEvqGcrG0gxi6G9e_Rsa1maUBWLvb3GKU3jXfL5J0YQI_Y6WwJj5c1c8gzBnABulzSR9wak3r9J-wTSM1-doKNIWr1OBeeoj40AR-NwIcj_9BgOGupUTQA-jdV0mQL6q69bVDuwrMJ_ftuC2ojAINWuGcVlF8MaT8phT347rFS8jAfZXKMM2N6gwEbO6Pepgtndg74JcKcwN6jhN0_dWE9XnNH78iwZoQP2nIu1_hojLOiN26-Y3l1xjKUu9WpCrdbIn3jdBIjUs_82pwM2uRqdvLAuiJJVktaJ9CNaky29bihLV1KwcyzQU5zMZ0YdOgvDi7vDHF15FyR2mlywXhx2Pzqs_Gi09Q3CUQ_u0JKiM3_Iyo9RxuzDUZIvZRUtGbu4W2rIWHgKuwGEw6C611ZGtUjORNpEjgHc_861OLJJBSqAIqGgE1tvilyV9y6FbqoXtP5bAfYFuWWl5lmcy9r6s3D3coagPdKlHcrxYxkUETRSyuaCcBo75ilztd3YqgyxVIadbgOwHmU0Cx-mtdwwJOfEdousw2dXnAkdVuG1d4fv6dT6XKyd4x7dyh7CPtVH9O2j65NvIqFE90NfQdNupm_kSKfnt3xCnwzTv155BM4B9tiXd6aKp3d2xIkY57nsTqOTBmTZ6_lf9-EWeHxS_0ukbDYiSVQsFwDz8d0GilKeU&state=AA7-RQyd1KsD63DBoQF_-NfYijzGvptfXTEj8D3AwQzW_ByUe8K0UnLZVuQjE6y8txJMcabFTC4fQhxHqqpTv28_e3dDuLBpYaGKqo_urwHkswmf1pAV2da7nPoVb-n1DHe1P-xU-jzU5c8vlyWCXJPpMDZyR7K0AQ7qdDCQRtr9oPpDymPyYknGIrQB6rI9VRSFinsGhZTnno2AvtOJCkTZfe3abYSdzjB-Am3PI9p-oAwh0f6mBzUGDYCMIB-traI_INV9fSa8tS9K363pBKUBQ-YgKI5nKI8Uqbz5UduNDwB99eQSUaEZu48vabVTwGjsLUczbkA46-u-AKAV65iwTPE6e-zRI11LRgfw4uObam8S3xvL3ok9pESzwGpMlBmEO0goyg9xJa2ULatLOy2PGRcMbWcuhyp84ttzedmiD2gdidVxHafEwgSpSEqad6YLWvCtV4XGbHxyuFZXF8rjFiDUK__KaJ2G-cbzyiXaVQ-YOh80NM8QQmAIPvys-2BTteP_G-1xjRZFpgJO6-dnZw2jelcF8KkITIGTNMuLejrcCyADjusaNOMrHMkASXohnq5p0lpkjPIcEZryghziPu3soPJ8A9jj8K9Ka-CWNosv9aBAnpN4eKYlOBEVljc6W824XPYgYMW62cBHlTlqV8RMNpo_5h6LRf2UdmY2T85xk3Iuz1_1Lr8jeu-UYZILPP2sc7HrjP7eFp70qgMCUQEk_JwsyQ&service=*my_project_id*dev

200 https://gala-demo.appspot.com/app#redirect_state=AA7-RQxBXe_JzFx7iXtcObVa7AJ4qPiiLY_XHrtgX861Z1TlUiOLFM3ymhzxqHWCONLFXjOJQkhNyCsH35cylVBrKtyLDaE-4J7wJ-P9PS3-JEPVeaoRnm7uo4ncLPW5EMxR--onGLFNZydFbqNKhdhWTox3FkUuv2lNZB2FcY9ZuhmE7LwiMXFYatawmFXpjZ0QdLkKEvqGcrG0gxi6G9e_Rsa1maUBWLvb3GKU3jXfL5J0YQI_Y6WwJj5c1c8gzBnABulzSR9wak3r9J-wTSM1-doKNIWr1OBeeoj40AR-NwIcj_9BgOGupUTQA-jdV0mQL6q69bVDuwrMJ_ftuC2ojAINWuGcVlF8MaT8phT347rFS8jAfZXKMM2N6gwEbO6Pepgtndg74JcKcwN6jhN0_dWE9XnNH78iwZoQP2nIu1_hojLOiN26-Y3l1xjKUu9WpCrdbIn3jdBIjUs_82pwM2uRqdvLAuiJJVktaJ9CNaky29bihLV1KwcyzQU5zMZ0YdOgvDi7vDHF15FyR2mlywXhx2Pzqs_Gi09Q3CUQ_u0JKiM3_Iyo9RxuzDUZIvZRUtGbu4W2rIWHgKuwGEw6C611ZGtUjORNpEjgHc_861OLJJBSqAIqGgE1tvilyV9y6FbqoXtP5bAfYFuWWl5lmcy9r6s3D3coagPdKlHcrxYxkUETRSyuaCcBo75ilztd3YqgyxVIadbgOwHmU0Cx-mtdwwJOfEdousw2dXnAkdVuG1d4fv6dT6XKyd4x7dyh7CPtVH9O2j65NvIqFE90NfQdNupm_kSKfnt3xCnwzTv155BM4B9tiXd6aKp3d2xIkY57nsTqOTBmTZ6_lf9-EWeHxS_0ukbDYiSVQsFwDz8d0GilKeU&state=AA7-RQyd1KsD63DBoQF_-NfYijzGvptfXTEj8D3AwQzW_ByUe8K0UnLZVuQjE6y8txJMcabFTC4fQhxHqqpTv28_e3dDuLBpYaGKqo_urwHkswmf1pAV2da7nPoVb-n1DHe1P-xU-jzU5c8vlyWCXJPpMDZyR7K0AQ7qdDCQRtr9oPpDymPyYknGIrQB6rI9VRSFinsGhZTnno2AvtOJCkTZfe3abYSdzjB-Am3PI9p-oAwh0f6mBzUGDYCMIB-traI_INV9fSa8tS9K363pBKUBQ-YgKI5nKI8Uqbz5UduNDwB99eQSUaEZu48vabVTwGjsLUczbkA46-u-AKAV65iwTPE6e-zRI11LRgfw4uObam8S3xvL3ok9pESzwGpMlBmEO0goyg9xJa2ULatLOy2PGRcMbWcuhyp84ttzedmiD2gdidVxHafEwgSpSEqad6YLWvCtV4XGbHxyuFZXF8rjFiDUK__KaJ2G-cbzyiXaVQ-YOh80NM8QQmAIPvys-2BTteP_G-1xjRZFpgJO6-dnZw2jelcF8KkITIGTNMuLejrcCyADjusaNOMrHMkASXohnq5p0lpkjPIcEZryghziPu3soPJ8A9jj8K9Ka-CWNosv9aBAnpN4eKYlOBEVljc6W824XPYgYMW62cBHlTlqV8RMNpo_5h6LRf2UdmY2T85xk3Iuz1_1Lr8jeu-UYZILPP2sc7HrjP7eFp70qgMCUQEk_JwsyQ&service=*my_project_id*_dev   74.125.24.153   normal  none    none

Я также заметил, что на последнем шаге аутентификации была ошибка 400:

{
  "error": {
    "code": 400,
    "message": "Bad response from IdP in Auth Code Exchange",
    "status": "FAILED_PRECONDITION"
  }
}

    Request URL: https://oauthintegrations.googleapis.com/v1/token:getForService
    Request Method: POST
    Status Code: 400
    Remote Address: 74.125.24.95:443
    Referrer Policy: no-referrer-when-downgrade
    access-control-allow-origin: https://gala-demo.appspot.com
    access-control-expose-headers: content-encoding,date,server,content-length
    alt-svc: quic=":443"; ma=2592000; v="43,42,41,39,35"
    cache-control: private
    content-encoding: gzip
    content-length: 136
    content-type: application/json; charset=UTF-8
    date: Thu, 05 Jul 2018 13:17:41 GMT
    server: ESF
    status: 400
    vary: Origin, X-Origin, Referer
    x-content-type-options: nosniff
    x-frame-options: SAMEORIGIN
    x-xss-protection: 1; mode=block
    Provisional headers are shown
    Authorization: Bearer *my access_token*
    Content-Type: application/json
    Origin: https://gala-demo.appspot.com
    Referer: https://gala-demo.appspot.com/app
    User-Agent: *my user agent*
    {credential: {,…}, gdiState: "APP_AUTH", serviceId: "ardent-fusion-209108_dev",…}
    credential
    :
    {,…}
    gdiState
    :
    "APP_AUTH"
    scopes
    :
    ["https://mail.google.com/"]
    serviceId
    :
    "*my_project_id*_dev"

Любая помощь будет высоко ценится! Пожалуйста, прокомментируйте, если вам нужна дополнительная информация. Спасибо и хорошего дня! :)

Обновления : С тех пор я понял, что omniauth следует потоку кода авторизации. Таким образом, я изменил свой поток ссылок на код авторизации и добавил /users/auth/google_oauth2 в качестве URL-адреса аутентификации и /users/auth/google_oauth2/callback в качестве URL-адреса токена. Тем не менее, теперь есть ошибка redirect_uri_mismatch, хотя я добавил my_app.com/users/auth/google_oauth2/callback и my_app.com/users/auth/google_oauth2 и oauth-redirect.googleusercontent.com/r/my_proj_id в разрешенные перенаправления uri моего oauth клиента. С тех пор я пытался добавить конечный / к URI перенаправления в клиенте, а также поменять местами https для http и добавить www перед каждым URI, но все эти методы не работают. (Все приведенные выше URL-адреса с https; моя репутация слишком низкая, чтобы публиковать более 8 ссылок, поэтому я должен их опустить)

Новые журналы ошибок таковы:

2018-07-06T10:06:48.542310+00:00 app[web.1]: I, [2018-07-06T10:06:48.542182 #4]  INFO -- : [2270a384-f7a5-4b6a-9dce-a6999dc47b28] Started POST "/users/auth/google_oauth2/callback/" for 66.249.83.158 at 2018-07-06 10:06:48 +0000

2018-07-06T10:06:48.542796+00:00 app[web.1]: I, [2018-07-06T10:06:48.542726 #4]  INFO -- omniauth: (google_oauth2) Callback phase initiated.

2018-07-06T10:06:48.651257+00:00 app[web.1]: E, [2018-07-06T10:06:48.651082 #4] ERROR -- omniauth: (google_oauth2) Authentication failure! invalid_credentials: OAuth2::Error, redirect_uri_mismatch: Bad Request

2018-07-06T10:06:48.651261+00:00 app[web.1]: {
2018-07-06T10:06:48.651264+00:00 app[web.1]:   "error" : "redirect_uri_mismatch",
2018-07-06T10:06:48.651266+00:00 app[web.1]:   "error_description" : "Bad Request"
2018-07-06T10:06:48.651268+00:00 app[web.1]: }

2018-07-06T10:06:48.652619+00:00 app[web.1]: I, [2018-07-06T10:06:48.652524 #4]  INFO -- : [2270a384-f7a5-4b6a-9dce-a6999dc47b28] Processing by Users::OmniauthCallbacksController#failure as JSON

2018-07-06T10:06:48.652781+00:00 app[web.1]: I, [2018-07-06T10:06:48.652696 #4]  INFO -- : [2270a384-f7a5-4b6a-9dce-a6999dc47b28]   Parameters: {"grant_type"=>"authorization_code", "code"=>"*my_auth_code*", "redirect_uri"=>"https://oauth-redirect.googleusercontent.com/r/*my_proj_id*", "client_id"=>"*my_client_id*", "client_secret"=>"*my_client_secret*"}

1 Ответ

0 голосов
/ 05 июля 2018

Давайте посмотрим, что идет не так, и вместо того, чтобы пытаться это исправить, мы рассмотрим совершенно другой подход к проблеме, который, как мы надеемся, будет работать лучше.

Что идет не так (и некоторый фон)

Для традиционной аутентификации с помощью Действия в Google вам необходимо настроить сервер OAuth. Когда пользователь попадает в точку в своем действии, где вам требуется аутентифицированный пользователь, он направляет его на ваш веб-сервер OAuth для входа в систему, ожидает, что ваш сервер отправит обратно маркер доступа или код авторизации, возможно, проделает дополнительную работу, чтобы получить токен авторизации, а затем отправлять вам токен доступа или аутентификации каждый раз, когда вызывается выполнение веб-крюка. Затем вы используете этот токен, чтобы выяснить, кто пользователь, и действовать соответствующим образом.

Похоже, происходит то, что Ассистент отправляет пользователя к вашей точке входа в систему с информацией, которая гласит: «Когда вы закончите, перенаправьте сюда токен доступа». Эта часть кажется в порядке. Вы проходите процесс входа, который включает в себя Google Sign In. В какой-то момент вы перенаправляете обратно на тот же URL, который хочет помощник.

Проблема в том, что вместо отправки токена доступа он отправляет однократный код авторизации. Помощник получает это и, поскольку он не настроен для обработки, выдает ошибку.

Не ясно, почему он отправляет код вместо токена доступа. Может случиться так, что OmniAuth предназначен для использования метода «код авторизации», и вы настроили помощник для использования «неявного» метода. Или же это может быть из-за того, что вход и помощник используют один и тот же URL-адрес как часть процесса, и это сбивает с толку. ИЛИ может случиться так, что OmniAuth на самом деле не призван играть роль сервера OAuth.

Если вы действительно хотите пойти по этому пути, изучите конфигурацию OmniAuth и рассмотрите возможность изменения ее конфигурации или конфигурации действия.

Обновление : Звучит так, будто использовался неверный поток аутентификации, и я рад, что вы исправили это. Авторизованный redirect_uri, который вы должны установить для OmniAuth, должен быть точно , что Google отправляет как часть своего запроса: https://oauth-redirect.googleusercontent.com/r/my_project_id

Однако ... вам это может не понадобиться.

Использование входа в Google для привязки аккаунта

Поскольку вы регистрируете пользователя в своем веб-приложении с помощью Google Sign-In, вы можете избежать всей проблемы с сервером OAuth и воспользоваться доступным ярлыком. Если ваше веб-приложение и ваше Действие являются частью одного и того же Облачного проекта Google, то Google Sign In for Assistant отправит вам идентификационный токен для пользователя, как только они подтвердят свою подлинность в проекте. Они могут аутентифицировать себя либо с помощью голоса в действии, либо войдя в веб-приложение с помощью входа в Google.

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

Большая «проблема» в том, что вам нужно запрашивать дополнительные области только через ваше веб-приложение - нет способа сделать это как часть действия. Однако это не является серьезным препятствием - это просто означает, что если пользователь достигает вашего Действия без идентификатора, попросите его сначала войти в ваше веб-приложение.

См. Дальнейшее обсуждение и диаграммы по этому вопросу переполнения стека

...