farmOS / field-kit

A modular, offline-first companion app to farmOS.
https://farmOS.org
GNU General Public License v3.0
59 stars 38 forks source link

Refresh token failing #452

Closed jgaehring closed 2 years ago

jgaehring commented 3 years ago

When the OAuth access_token expires and Field Kit (via farmOS.js) tries to refresh the token, it fails (full error response further below).

I think this is probably a server issue, but I wanted to put some notes here for future reference and as a reminder to follow-up when we're closer to a beta for FK 2.x. @paul121 and I did a little troubleshooting today but couldn't zero in on the issue, in part because he couldn't reproduce the same error on his machine to debug. I've got a lot of unstaged changes still locally so we probably need to wait til those are committed before pursuing again.

Next Steps

JSON Dump

Copy and pasting some JSON from requests/responses to and from /oauth/token:

{
  "first": {
    "token_type": "Bearer",
    "expires_in": 3600,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjI0YWY0MzI5NmNiMzRhODhiMmRiNThmNDg4NjQwY2E3N2YyN2E4ODcyMDEwYzZkMDMwNDg5MDNkNTE4ZmE5Njc0NmRhODEzMWNiNGU1NTgxIn0.eyJhdWQiOiI4MjhkMGU4Yi05MWQ1LTQ1ZjItOGQzZi02MWE0ZDQxMDQ3YTIiLCJqdGkiOiIyNGFmNDMyOTZjYjM0YTg4YjJkYjU4ZjQ4ODY0MGNhNzdmMjdhODg3MjAxMGM2ZDAzMDQ4OTAzZDUxOGZhOTY3NDZkYTgxMzFjYjRlNTU4MSIsImlhdCI6MTYyNTA2NjUxOCwibmJmIjoxNjI1MDY2NTE4LCJleHAiOjE2MjUwNzAxMTgsInN1YiI6IjEiLCJzY29wZXMiOlsiYXV0aGVudGljYXRlZCIsImZhcm1fbWFuYWdlciJdfQ.WxFuJPxojL0pjLoNR5_1jcr_p09mS719sxSiN706kMaBKT3917SGNJFPG8BZRI7YYX6WclKd9AtdyTOwfpISbaRsExwbHcoufrdBrkZHtSc4_o_5ydZ2SBOmopPo0yWwXaSOOvhXN455omSvDikIVJqfQ2nbIv8N6bTut1Z0leriaG7g3ABfxoWBavbsBvG8Ezgl7orva0mKPi15NImuYv_yLjPKV2SZ7pJQpYZf9VKwj8OI9eblo-9cNCJMcyYEhK2qTjIVq6kA9nIKnw-DXTkvdztjzg1LXSWH3ulSrFVdSOMyNb0UORvSbvGnVNqdzpF9bAg7UQojT_T63-mQBRnpZOoallZV_lK8wBU051L2PXFIBsXWsdUTNO-LITJH1xnkpdXWrWobDT88rWh7xvjp6EqzXjt9XE4sS3bjbQKzoBjVgmhW_huO4mw_ST1L0F8DKt5LGMV-CUO4onPZO5Ptz7FHvDZl_ktbHTd3JQ3OVLszCpXinrt681xVPQl1yvhkcZxUeS2xforEShKjQ8JvzckJI5nlZjRC0_E_ZR2453MJPlOTstRJd-UXQadqft0L6EfZVNzw5rJtZ4Zoq3cPFjnYhDxoTYDIlG_HGD1jidR2gH2y08OVxlL-2Io3ptcKREL6NftAics7UjmfzIG6QAHyhF4StfvajbSidnc",
    "refresh_token": "def50200da03878b400f3bcc0360fcecabbad16575065a8251220c0849c7420c82335bcac99afb0892f9ba2e481eaba97e0b2ada95a6dece838799384b6e70cb9a7528010d31e5fb9131eb05ec13f4d49f17ada1ff60b81e189fbda0101cb2dc1ab0d119b1aff3cc629d56aad01eae007425d40a086af393e63d1ec1d67f5dd4626aea28fabf278df3b154843d68c2ed640f245690bba439663cea0949fa9661042eb9df638ca097cffb94ca09453f44306cf3ccbfe11fbbb6a8bd1d71b7b641793c247e9ceadf8106f6b671cd85cb2710a6456a0d5ad68c36a3b03fc8d2c0eed03c44020c236779e24e68857dd9f8309fe1492c0151fdc3b492a8d5d544df8c0f2cc3520ab33476eef70842d2884c7f5c6ba14f1b46f382b8d2d12cbb14d4ebdba156afa6dc6b5912ff85eaee4028f714e01329846ac8d3aabba7873945bc057ef237dcaddf30bf6c88f69341763a08d25620ec041378f5763c29705fe13701366225ce904e0792f0dd5a79da5172bbe251c84968d2d72756af6e77e7059538a525a14a905ad57dd80f53bee1f6e23693823aac56cf40b9676824878ee2d26897e75a21"
  },
  "second": {
    "token_type": "Bearer",
    "expires_in": 3600,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjFhZGIwODI1MzcyODE5MWUwNzQyM2EzZjhiM2I0OWIxNDBlYTJlOWI1MjQyYmM5Y2I3MmIzZmRkNTZlMmY1MzE1OTAyZjkzNDIyYmNiZjg0In0.eyJhdWQiOiI4MjhkMGU4Yi05MWQ1LTQ1ZjItOGQzZi02MWE0ZDQxMDQ3YTIiLCJqdGkiOiIxYWRiMDgyNTM3MjgxOTFlMDc0MjNhM2Y4YjNiNDliMTQwZWEyZTliNTI0MmJjOWNiNzJiM2ZkZDU2ZTJmNTMxNTkwMmY5MzQyMmJjYmY4NCIsImlhdCI6MTYyNTA2ODQ3MywibmJmIjoxNjI1MDY4NDczLCJleHAiOjE2MjUwNzIwNzMsInN1YiI6IjEiLCJzY29wZXMiOlsiYXV0aGVudGljYXRlZCIsImZhcm1fbWFuYWdlciJdfQ.G-C9PI-3uO_h04y5Ynq5brL63ZemTCerjJe81gfsvHDsIv3fbU85YaE8TiBZukDGsvp3cvPYmG-0YlMPCXLhtAoFxUf59w7Hm6Gc3ICd65Tpx-VCvwk7VS3FbhYgT7_rj2-hTbnkuLZ6ln-fRntmB73yUvfpVVFbtuL3Qso2YEiCCBgU0WJQAEFV0N2v__qNS2pgVM4LdFO9i1MJr_ZkUqIfoN70TLQtSflFH4Gt_1xnoxyP0PqYEkccZOsYY86_AUMcbeN6e9EA6WFlRTbmP7vqx7DMzhGgJ4EN6GvvuNrqAKEWstK9ZVYb4UQEGj_6EK4GB4Z0vQUKBOtbXPfqtTi4zIOEsxms3wXX_Vig8wuEagQHnCx2Qr9p53he48UfeuKZBhrwgQSab_ckkGtvrvJiLt0ynGKZXoTuRqkAN8egeAKsyq9TwtO1Z8rpH2cdDtexJ936IW2uYral6oYxgi1UqxS5HQ3utZwwchG6Rxhrh8o7gfdB6ySwGyHOsDxEKaTHFmfg6IbqCD5aTMdZV3e3MP8e7ANmjBiB8xvk7HTnLGW2gvhWbDh8_l0cSA54BecpnLmIWxcoXJ1mgA0NJrQ6q3Yg77gg35DOXot9rtmjD6Xi4k3myBkcnWj5oXN6ZbNhjMzYfW8shjuKF9ZSq9yrI0JJ3CziHY1LPfc2DaY",
    "refresh_token": "def5020069e17097aa92a32d21cb43d73de005f54b4256df983bbe7f02fd6ce078c34c3f6510e158e1454d89e2d27e75a2f5591be28e03dcb046a7d4a4cb35e159164099a04401b6e04a4e1802e26918f42daae6cd89473b0a48de6bf98546bdab8c185c5246a6361c5dbed5c99f0e892e578b8b31cbbfb27890486f4a693e85e8294caf2cecd7477eb015f59651974ec5df700ec2b8c09745642cb041b5e00c8d3fd3a55f6e6a2f612e2132018b7f336dcd5befcd2fa56b35d3345f37826c03754bd643bceaee99a724f3af5a91cee55bf76017d5e853bd80140cc6a09314e5599123d1c6e421c3cb605d9b32390c7f3a7f6e707e0747b9865817082fa79972435f528582041e3218d678243879cc732e82450ba7f0a304589afb2ed2d38dd1912a800ec922f57eb57523995db45633a7eed315a26a3a8b89b507c57103fa1824e22476806ec220a9267b07e0ec841a082a5cabcd59d1d108bce364af7d034d0313a36d77e25a794d45a0ebe9356286db0765be01f1cae6d884504110e6c1162ce50871268d1251afebfe576b906bd273b20fbe24dc5a503650356627d0804aad72061e"
  },
  "login response": {
    "token_type": "Bearer",
    "expires_in": 15,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjNjYTE3YTI5NDY5N2RhZTc5MWVhZTJiNGIyMDk2NGQ3MWRiNDNhZTdkMzZjNGRiOTVkMGRjMDUzZjNiYTU5NTM4OTg1MTY5NTg0YzQ2YWVjIn0.eyJhdWQiOiI4MjhkMGU4Yi05MWQ1LTQ1ZjItOGQzZi02MWE0ZDQxMDQ3YTIiLCJqdGkiOiIzY2ExN2EyOTQ2OTdkYWU3OTFlYWUyYjRiMjA5NjRkNzFkYjQzYWU3ZDM2YzRkYjk1ZDBkYzA1M2YzYmE1OTUzODk4NTE2OTU4NGM0NmFlYyIsImlhdCI6MTYyNTA2OTI2NCwibmJmIjoxNjI1MDY5MjY0LCJleHAiOjE2MjUwNjkyNzksInN1YiI6IjEiLCJzY29wZXMiOlsiYXV0aGVudGljYXRlZCIsImZhcm1fbWFuYWdlciJdfQ.mqyW6yQSogmqhQ0CJ6jaNfuXRkrO-ywyVfUwwO8qTNnlZp10wvGF_gy92k9HvjKZdna5MGuBagBEnuil8mXPer98VeyBlnzk4WZm1EIBkmG7_P8dSe-kJi0AQB7yrd97-0DXaRpUw4ky54mlfJEEH0KXXldyWnSWTC6ykKP8XonkcRpDKLfLmnxuPkf28xoPkvRidqHuIAJ_JqUyhkmHuPuJOGf5pz5TL8PjUf_ZJZHuZIwMc0AGQptqKBXzQ8dXn-JVGvNZp2G3FCHk1Qp88-g-_UJBrI8UWPtcHEzkNLDgnPBBQy2wQCl3uBWOSOUOnzzl3YEnidM2NL8vDwTREDQUnDRJEyh6FdcWeMtNoqPAsSuBgX9Wo3vjJN9mS67vUeDhRw37L5bR0wMYy8jQzYfG4ltJcbPgjfpxb-qRuRa8DZZMOCwKfH0AfRoZoQnrT0NWAh4O3EMBLX-i7i_bReQXl1dLIy2BdwrCEgR94U4ZyE8AeZ-zfUzn-tFf4phNwNdCF1rof4ZctTo-HacFWeGlYAS30utnQ6DocWYFvcVnm8G1TnSupa10ejWjXdRbj9FKlc5goLl_ZmM5Ch17KU0r2Nkz1g1nW79qXrjdCy5hY1y-oOlUpyQ0Bs3YKy476UlSvn7cFoMYLfCqRJVfQ3byJUZRo5wXpAl28WvL81s",
    "refresh_token": "def502000614e34079146625e34a268542454a7059ea5536896cc95378546a222b2d476a33e26a714eb8fe01159726dd0432110e2735509682db39e068e53f22ab451a1f3b968d95ee295b242f3fa50154af7880134548cb9069eebf6e3d8b86ce336628c03fce3380605d4f5ef0284c63d33773887a073e6380c0da4b1f18f4c8d94f6406b192ab0c9b96f44c7be71fd46d02a350596d3bc3a0081ce2f53c6897dd992560497360939d14bade8f377d9848d683323693752437421f02a1a25691c6a0a49fda4d0b3502a1582329e5c663febae23fb26f2c8bc449b58e0c2f1fc8c5dd2a40d2bfc1deb22f9298055bedae389e3e59cf16f9b2bbb66608f63dbd73302d3811ab75b5b63843451ee05bd53ebac36cdcc9c8346a4733c36d50deed284bea4835309af8e24a135b83e8734404c996f03311662291e501ff448fbd275cff2ac5b9494686ecb5a0adc130d2a0e0f6ffa31a875227dfa92fdc17c3629a79e1b4ed2e9ef1d321c6090a7442a4a62a17e9cac14aebae6c6f690b98eec69b021b92125bac086d50bc1be2ab657c3c3f24dc0d0d6609567696d1c02c940ed7055af49b"
  },
  "refresh payload": {
    "grant_type": "refresh_token",
    "client_id": "farm_client",
    "refresh_token": "def502000614e34079146625e34a268542454a7059ea5536896cc95378546a222b2d476a33e26a714eb8fe01159726dd0432110e2735509682db39e068e53f22ab451a1f3b968d95ee295b242f3fa50154af7880134548cb9069eebf6e3d8b86ce336628c03fce3380605d4f5ef0284c63d33773887a073e6380c0da4b1f18f4c8d94f6406b192ab0c9b96f44c7be71fd46d02a350596d3bc3a0081ce2f53c6897dd992560497360939d14bade8f377d9848d683323693752437421f02a1a25691c6a0a49fda4d0b3502a1582329e5c663febae23fb26f2c8bc449b58e0c2f1fc8c5dd2a40d2bfc1deb22f9298055bedae389e3e59cf16f9b2bbb66608f63dbd73302d3811ab75b5b63843451ee05bd53ebac36cdcc9c8346a4733c36d50deed284bea4835309af8e24a135b83e8734404c996f03311662291e501ff448fbd275cff2ac5b9494686ecb5a0adc130d2a0e0f6ffa31a875227dfa92fdc17c3629a79e1b4ed2e9ef1d321c6090a7442a4a62a17e9cac14aebae6c6f690b98eec69b021b92125bac086d50bc1be2ab657c3c3f24dc0d0d6609567696d1c02c940ed7055af49b"
  },
  "refresh error response": {
    "error": "invalid_grant",
    "error_description": "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.",
    "hint": "Check the configuration to see if the grant is enabled.",
    "message": "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
  }
}
mstenta commented 2 years ago

Maybe you already tried this, but I wonder if it's related to this change?

https://github.com/farmOS/farmOS/commit/a86166d

We explicitly set the redirect URI for the farm_client OAuth client to https://farmOS.app (not sure if the capital OS matters?)

I see in the error message it says (emphasis mine):

The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.

mstenta commented 2 years ago

Ah actually, that commit was July 22nd, but you opened this originally on June 30th, so maybe they are unrelated. Still... might want to check the redirect URI in Field Kit to make sure that's set up properly, otherwise it might lead to another issue anyway.

jgaehring commented 2 years ago

Hmm, I saw that message too and did wonder about the redirection URI. Although I don't think we send that URI from FK, with the password authorization flow we use.

I'll have to look into this further next week. Right now I'm wrapping up #454, which is a huge chunk towards finishing the alpha release! It will also allow me to commit my first (mostly) working version of FK in a while, so we can actually try to reproduce this on other machines, which seems key. :crossed_fingers:

jgaehring commented 2 years ago

Cross-linking this issue with farmOS/farmOS.js#35, since that issue is also concerned with OAuth, and will also require some of @paul121's counsel.

jgaehring commented 2 years ago

It will be interesting to see if this issue persists in the dev deployments (#468).

Also, going over old issues, but would it make sense to consider #435 along with the other OAuth stuff here?

paul121 commented 2 years ago

So one potential issue here could be if we're trying to perform the refresh_token grant as the Drupal admin user (user id = 1). I've opened an issue for that: https://www.drupal.org/project/farm/issues/3241690 But after chatting a bit with @jgaehring it seems like this was happening for "normal" users as well.

So I pulled the latest farmOS-client 2.0.0-alpha.1 into my local and took a peek at all this in my debugger. What's interesting is that the server is not seeing any of the body payload for the refresh_token grant, so it defaults to the implicit grant type (which we have disabled). This logic here: https://git.drupalcode.org/project/simple_oauth/-/blob/5.x/src/Controller/Oauth2Token.php#L45.

The password grant goes through fine though. It seems like the difference is the Content-Type header.

The initial password grant is sent as a Content-Type: 'application/www-form-urlencoded': https://github.com/farmOS/farmOS.js/blob/3f0a27a42e7d2f79eb542151cdea77b4133076f7/src/connect/oauth.js#L177

But the refresh_token grant is sent as Content-Type: 'application/json': https://github.com/farmOS/farmOS.js/blob/3f0a27a42e7d2f79eb542151cdea77b4133076f7/src/connect/oauth.js#L62

Does it seem like this could be the issue? Have to run, but I think the next step would be to try manually crafting the refresh_token grant as a www/form-urlencoded from the browser and see if that works!

jgaehring commented 2 years ago

Does it seem like this could be the issue? Have to run, but I think the next step would be to try manually crafting the refresh_token grant as a www/form-urlencoded from the browser and see if that works!

Oh this sounds very plausible!

The request to refresh the token is already setting the Content-Type explicitly, so I think it would be a cinch to change that in the header. The only other consideration then is the request body. We can't just send it as JSON, but it should be easy enough to send as an instance of URLSearchParams, which it seems, if I read the axios docs correxctly, will automatically set the Content-Type for us. So instead of all those refreshOpts we're passing as the second argument to axios() currently, we just pass the params to axios.post() like such:

const refreshParams = new URLSearchParams();
refreshParams.append('grant_type', 'refresh_token');
refreshParams.append('client_id', clientId);
refreshParams.append('refresh_token', token);
axios.post(accessTokenUri, refreshParams);

No headers required! Just need to test it out and make sure it works.

jgaehring commented 2 years ago

Just pushed a WIP commit with these changes, but still haven't tested: jgaehring/farmOS.js@5ef1f2e.

jgaehring commented 2 years ago

Resolved by jgaehring/farmOS.js@f63d062.