Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

{"error": "invalid_client"} #52

Closed
lucacorti opened this issue Sep 8, 2013 · 23 comments
Closed

{"error": "invalid_client"} #52

lucacorti opened this issue Sep 8, 2013 · 23 comments

Comments

@lucacorti
Copy link
Contributor

Hello,

I'm following the tutorial for Django Rest Framework. I set up my application in the admin and I'm sending a POST request just like in the tutorial, but always get

{"error": "invalid_client"}

as a response:

$ curl -vK user -X POST -d "grant_type=password&username=myuser&password=mypass" http://localhost:8000/o/token/
* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8000 (#0)
* Server auth using Basic with user 'RgmI&_Y A5mdjuAh2T/]m_ZeD|u'[8`$r|`k'!'
> POST /o/token/ HTTP/1.1
> Authorization: Basic UmdtSSZfWSBBNW1kanVBaDJUL11tX1plRHx1J1s4YCRyfGBrJyE6dnZdWWw/LUB3YWNBI2Q+NEcyNWEkbkZCclogJGQ/e1ZtYm4rMl5GYnRNSl9ZTitiVHB6b3UjXkRdaFhMeGxNL28jJz95MCV0ZXQ8Z3w8OEZTZHVPMkIvN3Y6dG5EUz99ZGxla3B2dTchWTssJDA8VEQ1UmwucA==
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: localhost:8000
> Accept: */*
> Content-Length: 55
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 55 out of 55 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 400 BAD REQUEST
< Date: Sun, 08 Sep 2013 17:30:37 GMT
< Server: WSGIServer/0.1 Python/2.7.5
< Content-Language: it
< Vary: Accept-Language, Cookie
< Pragma: no-cache
< Cache-Control: no-store
< X-Frame-Options: SAMEORIGIN
< Content-Type: application/json;charset=UTF-8
< 
* Closing connection #0
{"error": "invalid_client"}
@synasius
Copy link
Contributor

synasius commented Sep 9, 2013

Hi @lucacorti

The problem is that you should escape all special chars and wrap the auth string in double quotes since our token generator includes characters like space, , ", $.

For example, if your client_id is alv"lks'as\dk$k as then when used in curl it should be "alv\"lks'as\\dk\$k as"

Documentation is missing this information, also we need to improve this step in the tutorial and make it simpler, because users shouldn't deal with this stuff while learning how to use our library. I'll work on that asap.

Meanwhile, you should try again the tutorial changing Step 3: instead of our generated client_id and client_secret try set your own using a safe set of characters that don't need to be escaped, for instance "test:test"

Thanks for reporting 😄

@lucacorti
Copy link
Contributor Author

I'm not sure This is the real issue. I've also tried to obtain the token via AFNetworking with simular results.
Maybe there is a quoting issue there too.

Unfortunately I'm not able to perform further tests since I was in a hurry and had to use a different system to get it working.

thanks

Luca

Il giorno 09/set/2013, alle ore 09:36, Federico Frenguelli notifications@github.com ha scritto:

Hi @lucacorti

The problem is that you should escape all special chars and wrap the auth string in double quotes since our token generator includes characters like space, , ", $.

For example, if your client_id is alv"lks'as\dk$k as then when used in curl it should be "alv"lks'as\dk$k as"

Documentation is missing this information, also we need to improve this step in the tutorial and make it simpler, because users shouldn't deal with this stuff while learning how to use our library. I'll work on that asap.

Meanwhile, you should try again the tutorial changing Step 3: instead of our generated client_id and client_secret try set your own using a safe set of characters that don't need to be escaped, for instance "test:test"

Thanks for reporting


Reply to this email directly or view it on GitHub.

@twhtanghk
Copy link

I experienced the similar problem to create a server application with type=confidential, grant_type=authorization-code via the application web interface. Then, modify the existing web application written in passport-oauth2 successfully authenticated with django-oauth2-provider mentioned in django-rest-framework and try to authenticate server integrated with django-oauth-toolkit. Trace the server code to line 57 of oauth2_validators.py about the condition "request.client.client_type != Application.CLIENT_CONFIDENTIAL". According to section 3.2.1 of rfc, the application type should be confidential instead of other than confidential. I would like to know if the condition could be rewritten as "request.client.client_type == Application.CLIENT_CONFIDENTIAL". Please clarify.

Thanks,
Tommy Tang

@synasius
Copy link
Contributor

@lucacorti
we're are really interested in your use case.

I can add some information to the debug logger output to see what's in the Authorization Basic header. That way we could understand if the authorization credentials are passed in correctly.

What do you think?

@synasius
Copy link
Contributor

Hi @twhtanghk

the condition "request.client.client_type != Application.CLIENT_CONFIDENTIAL" is correct.

Method authenticate_client_id ensures that the client_id belong to a non-confidential client. This method is only called when authenticate_client fails to authenticate using Basic auth credentials, therefore it return True only if the client is not CONFIDENTIAL.

Could you please add more details so we can identify what is the problem in your authentication process?
Also, are you using basic auth? Credentials are properly escaped?

I will add more debug output in authenticate_client and authenticate_client_id to help

@synasius
Copy link
Contributor

@twhtanghk
I added support for request-body authentication for confidential clients. See aa78209 .
Please remember that this method is NOT RECOMMENDED by the RFC. You can read more here http://tools.ietf.org/html/rfc6749#section-2.3.1

@lucacorti @twhtanghk
I changed the default generator for client_id and client_secret: now it uses a safe set of characters that don't need to be escaped. Though this is a restriction on what states the RFC, I hope that this change allows our library to be more compatible with heterogeneous client implementations. I kindly ask you to test it if you can!

@lucacorti
Copy link
Contributor Author

@synasius
This is what django-oauth2-provider does, it passes the clientid/secret via the request body. It worked for me, but then I had issues with it wrt scopes and general consensus on the net was positive towards django-oauth-toolkit .

In the debug log I posted earlier, you can see:

Server auth using Basic with user 'RgmI&_Y A5mdjuAh2T/]m_ZeD|u'[8`$r|`k'!'
> POST /o/token/ HTTP/1.1
> Authorization: Basic UmdtSSZfWSBBNW1kanVBaDJUL11tX1plRHx1J1s4YCRyfGBrJyE6dnZdWWw/LUB3YWNBI2Q+NEcyNWEkbkZCclogJGQ/e1ZtYm4rMl5GYnRNSl9ZTitiVHB6b3UjXkRdaFhMeGxNL28jJz95MCV0ZXQ8Z3w8OEZTZHVPMkIvN3Y6dG5EUz99ZGxla3B2dTchWTssJDA8VEQ1UmwucA==

the user is correct. If you base64 decode the Authorization basic header you will find:

RgmI&_Y A5mdjuAh2T/]m_ZeD|u'[8`$r|`k'!:vv]Yl?-@wacA#d>4G25a$nFBrZ $d?{Vmbn+2^FbtMJ_YN+bTpzou#^D]hXLxlM/o#'?y0%tet<g|<8FSduO2B/7v:tnDS?}dlekpvu7!Y;,$0<TD5Rl.p

Which I double checked with the client id an secret in the admin. Basically I just installed django-oauth2-toolkit, setup the client application in the admin following the tutorial instructions and used curl as per the docs to make the call to obtain a token. Nothing fancy, I think you can reproduce this fairly easily. Unfortunately I'm not able to perform further tests right now.

Do you need any more information?

thanks

Luca

@synasius
Copy link
Contributor

@lucacorti

Can you please check if the Authorization Grant Type in the Application is set to "Resource Owner password-based"?

@lucacorti
Copy link
Contributor Author

@synasius Yes, it was, as per the tutorial.

@synasius
Copy link
Contributor

@lucacorti first of all, thanks for the time you're spending on this! We appreciate it! really

I went through the tutorial several times but I couldn't replicate the problem. It always works for me

At this point you can:

  • retry the tutorial using simpler client_id and client_secret
  • retry the tutorial with another client, I can suggest requests-oauthlib
  • enable oauthlib logger in django configuration and report the output, just add:
LOGGING = {
    'version': 1,
    # ...
    'handlers': {
        # ...
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        # ....
        'oauthlib': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

@synasius
Copy link
Contributor

@lucacorti

hey, I put on a test DOT instance on heroku. Can you please if it's working for you?

curl -X POST -d"grant_type=password&username=devel&password=devel" http://devel_client_id:devel_client_secret@dotk.herokuapp.com/o/token/

@lucacorti
Copy link
Contributor Author

Seems to be working...

$ curl -v -X POST -d "grant_type=password&username=devel&password=devel" http://devel_client_id:devel_client_secret@dotk.herokuapp.com/o/token/
* About to connect() to dotk.herokuapp.com port 80 (#0)
*   Trying 107.20.206.76...
* connected
* Connected to dotk.herokuapp.com (107.20.206.76) port 80 (#0)
* Server auth using Basic with user 'devel_client_id'
> POST /o/token/ HTTP/1.1
> Authorization: Basic ZGV2ZWxfY2xpZW50X2lkOmRldmVsX2NsaWVudF9zZWNyZXQ=
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: dotk.herokuapp.com
> Accept: */*
> Content-Length: 49
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 49 out of 49 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-store
< Content-Type: application/json;charset=UTF-8
< Date: Thu, 12 Sep 2013 21:14:16 GMT
< Pragma: no-cache
< Server: gunicorn/18.0
< Content-Length: 176
< Connection: keep-alive
< 
* Connection #0 to host dotk.herokuapp.com left intact
{"token_type": "Bearer", "expires_in": 36000, "scope": "write read groups", "access_token": "bpRMKlGR42j2nbmoYbjkCStTtjo8M0", "refresh_token": "SIP6qDwpDvtDRaUwqsSUvMc8ZinjWO"}

@lucacorti
Copy link
Contributor Author

Ok, I setup a test environment and the logging as you suggested, I now get this eror on the console:

Dispatching grant_type password request to <oauthlib.oauth2.rfc6749.grant_types.resource_owner_password_credentials.ResourceOwnerPasswordCredentialsGrant object at 0x10faed510>.
Authenticating client, <oauthlib.common.Request object at 0x10faede90>.
Client authentication failed, <oauthlib.common.Request object at 0x10faede90>.
Client error in token request, .

of course if i change clientid/secret to something naïve the client authenticates:

$ curl -K user -X POST -d "grant_type=password&username=myuser&password=mypassword&scope=read" http://localhost:8000/o/token/
{"access_token": "lqQTsVkga3I63fvZNiZx84RZ7yfjTx", "token_type": "Bearer", "expires_in": 36000, "refresh_token": "AsYUFGtuTcJE6lacdvCUL7vZ6aqns5", "scope": "read"}

$ cat user
user = "abcd:efghi"

so this really seems an issue with quoting on oauthlib/oauth-toolkit side.

@twhtanghk
Copy link

Without modifying the condition, the flow is listed below. Any idea if the authorization request is not going through type=confidential, grant_type=authorization-code scheme.

HTTP/1.0 200 OK
Date: Mon, 16 Sep 2013 03:03:57 GMT
Server: WSGIServer/0.1 Python/2.7.3
Vary: Cookie
Content-Type: text/html; charset=utf-8
Set-Cookie: csrftoken=PrI88VPP3OnOTo04HNZSJFR0fNsAMnkc; expires=Mon, 15-Sep-2014 03:03:57 GMT; Max-Age=31449600; Path=/

HTTP/1.0 302 FOUND
Date: Mon, 16 Sep 2013 03:04:00 GMT
Server: WSGIServer/0.1 Python/2.7.3
Vary: Cookie
Content-Type: text/html; charset=utf-8
Location: http://localhost:8000/auth/ogcio/callback?code=jIvbl8BS2xlvin8hYjZEhr5p0DTCb9

HTTP/1.1 500 Internal Server Error
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Date: Mon, 16 Sep 2013 03:04:00 GMT
Connection: keep-alive
Transfer-Encoding: chunked

  • callstack:
    Thread-41 - pid3269_seq22
    authenticate_client_id [oauth2_validators.py:57]
    validate_token_request [authorization_code.py:356]
    create_token_response [authorization_code.py:228]
    create_token_response [token.py:93]
    wrapper [base.py:61]
    create_token_response [backends.py:89]
    create_token_response [mixins.py:102]
    post [base.py:135]
    dispatch [base.py:86]
    dispatch [views.py:120]
    bound_func [decorators.py:21]
    wrapped_view [csrf.py:77]
    _wrapper [decorators.py:25]
    view [base.py:68]
    get_response [base.py:115]
    call [wsgi.py:255]
    call [handlers.py:72]
    run [handlers.py:85]
    handle [simple_server.py:124]
    init [SocketServer.py:638]
    init [basehttp.py:150]
    finish_request [SocketServer.py:323]
    process_request_thread [SocketServer.py:582]
    run [threading.py:504]
    __bootstrap_inner [threading.py:551]
    __bootstrap [threading.py:524]

Thanks
Tommy Tang

@synasius
Copy link
Contributor

@lucacorti
fine! thnx for working on that! 😃

@twhtanghk
Sorry but I can't understand what is the problem you're reporting? Can you please open a new issue on the tracker, with more info? thank you

@lucacorti
Copy link
Contributor Author

@synasius I'm sending a pull request, the RFC says the client should urlencode the client_id/secret, but you are not urldecoding it. I think this is a bug.

@ghost
Copy link

ghost commented Nov 2, 2014

Just a small comment here. I had the same problem with the "invalid client" response, but this was due to me running the app on Apache not on the internal server provided by Django Rest Framework. In Apache I needed to turn on "WSGIPassAuthorization On" to have the right headers passed.

@fgau
Copy link

fgau commented May 11, 2015

@twolff-iow thanks for your comment, I had the same problem on my apache. problem solved with "WSGIPassAuthorization On". thank you!

@bquinn
Copy link

bquinn commented Mar 9, 2016

@twolff-iow that caught me as well, thanks for the tip! It would be great if that was added to the documentation somewhere.

@RaulBejarano
Copy link

@ghost that solved my problem, thanks!

@MarkPare
Copy link

MarkPare commented Dec 8, 2016

@ghost, thanks for the comment. Solved my issue.

@satyaprahlad
Copy link

Really frustrated with this. None of those are wroking even in my local host.

@n2ygk
Copy link
Member

n2ygk commented Jun 9, 2022

@prahlad1989 Please take a look at #1161 to see if this helps. There are two breaking changes with release 2.0.0:

  • PKCE is now required
  • client_secret is now hashed so you have to get the secret before it is saved

Some fixes and documentation improvements have been merged into the master branch which will help once released. Just waiting on a couple of PRs to be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants