pysnippet / fastapi-oauth2

Easy to integrate OAuth2 authentication with support for several identity providers.
https://docs.pysnippet.org/fastapi-oauth2
MIT License
49 stars 7 forks source link

🐛 Bug Report - Authorization Code Flow interrupted by error #23

Closed marcvs closed 10 months ago

marcvs commented 10 months ago

Bug description

I use a social_core based configuration for my OP, which I know works well, e.g. with django.

I configured all endpoints manually, because I didn't get autoconfig to work.

I use your exapmles/demontration for testing

Visiting http://localhost:8000/oauth2/helmholtz/authorize with my browser triggers the login flow, and (rightly) returns me to http://localhost:8000/oauth2/helmholtz/token?code=HFKP5DlIOdJQ6eWGgt-TK_PRCB4yqFDMJ9O_vkLfA8k&state=yXuBLKjQvrOfxpsVMArBfGGtiYZXBYEo

Somewhere there I loose track, because I can't enable proper logging (input appreciated)

Errors I'm shown are added below.

Reproduction steps

  1. Go to http://localhost:8000/oauth2/<provider/authorize
  2. Follow the web flow
  3. End up at http://localhost:8000/oauth2/<provider/token?code=foo&state=bar
  4. See error

Logs

Serverside:

[12:20:29] [...ython3/dist-packages/httpx/_client.py:1734]   DEBUG - HTTP Request: POST https://login.helmholtz.de/oauth2/token "HTTP/1.1 200 OK"
INFO:     127.0.0.1:41182 - "GET /oauth2/helmholtz/token?code=Do3Nq7UEAnSxvEG7dA11CyNpa7LFntvVng__Ct3O9aw&state=gsUfmfelbEbBHCcIZpchidCLkWssTAsl HTTP/1.1" 400 Bad Request

Browserside:

{"detail":"unsupported operand type(s) for +: 'NoneType' and 'str'"}

Browsers

Firefox, Chrome

OS

Windows, Linux

ArtyomVancyan commented 10 months ago

Hi @marcvs, thanks for the report. Please provide some oauth2 client credentials so I can reproduce the issue and help you. If it is confidential and you cannot share it in public or at all, please provide the custom backend implementation you are using. Also, I am curious if you are able to authenticate with the same oauth2 backend and credentials in a Django application, so we can verify the cause of the issue is fastapi-oauth2.

marcvs commented 10 months ago

Hi @ArtyomVancyan, I'll send a fork of the fastapi-oauth2 repo with my changes in a bit. I'd mail the client_secret straight to you, since I would not like to put it on github. Could you send me a contact?

marcvs commented 10 months ago

I simply extended config.py here: https://github.com/marcvs/fastapi-oauth2/tree/test/authcode-flow

marcvs commented 10 months ago

I didn't implement Django for this client, but other clients are implemented for it. I think that part works nicely, because we do get back the code parameter. My guess is that things go wrong is either before or when getting the access token by using the code. Just: I was not able to turn on logging for urllib or werkzeug

ArtyomVancyan commented 10 months ago

Hi @ArtyomVancyan, I'll send a fork of the fastapi-oauth2 repo with my changes in a bit. I'd mail the client_secret straight to you, since I would not like to put it on github. Could you send me a contact?

Please reach me by mailing: artyom@pysnippet.org. I will debug it locally and keep you aware. Also, I need an account for logging in at the https://login.helmholtz.de/oauth2-as/oauth2-authz-web-entry step. If you have an account for testing and development, please give that credentials as well.

ArtyomVancyan commented 10 months ago

Thanks for trusting and sharing the client's secret so I could test it locally. The issue is because of the setting method of the BaseAuth class, which is a parent for the used OpenIdConnectAuth class. It was supposed to see the set variables such as OIDC_ENDPOINT, AUTHORIZATION_URL, etc., but it does not. To fix this issue, you can use the following backend:

class HelmholtzOpenIdConnect(OpenIdConnectAuth):
    name = "helmholtz"
    OIDC_ENDPOINT = "https://login.helmholtz.de/oauth2"
    ID_TOKEN_ISSUER = "https://login.helmholtz.de/oauth2"
    ACCESS_TOKEN_URL = "https://login.helmholtz.de/oauth2/token"
    AUTHORIZATION_URL = "https://login.helmholtz.de/oauth2-as/oauth2-authz"
    REVOKE_TOKEN_URL = "https://login.helmholtz.de/oauth2/revoke"
    USERINFO_URL = "https://login.helmholtz.de/oauth2/userinfo"
    JWKS_URI = "https://login.helmholtz.de/oauth2/jwk"

    def setting(self, name, default=None):
        return getattr(self, name, default)
marcvs commented 10 months ago

Wow, wonderful! That fixed my problem! Thanks for resonding so quickly!

One other question: Those configuration entries are all available via the .well-known/openid-configuration endpoint. What am I missing that this auto-config does not work?

ArtyomVancyan commented 10 months ago

Those issues come from the bad behavior of the setting method. A method called oidc_config gets all the configurations for you and uses the setting method. I did not dive into their codes that much, but it is not enough to set only OIDC_ENDPOINT for OpenIdConnectAuth type of backends.

Wow, wonderful! That fixed my problem! Thanks for resonding so quickly!

Nice to hear :)