Giter VIP home page Giter VIP logo

example-oauth2-server's Introduction

How to create an OAuth 2.0 Provider

This is an example of OAuth 2.0 server in Authlib. If you are looking for old Flask-OAuthlib implementation, check the flask-oauthlib branch.

Sponsors

If you want to quickly add secure token-based authentication to Python projects, feel free to check Auth0's Python SDK and free plan at auth0.com/overview.

Take a quick look

This is a ready to run example, let's take a quick experience at first. To run the example, we need to install all the dependencies:

$ pip install -r requirements.txt

Set Flask and Authlib environment variables:

# disable check https (DO NOT SET THIS IN PRODUCTION)
$ export AUTHLIB_INSECURE_TRANSPORT=1

Create Database and run the development server:

$ flask run

Now, you can open your browser with http://127.0.0.1:5000/, login with any name you want.

Before testing, we need to create a client:

create a client

Password flow example

Get your client_id and client_secret for testing. In this example, we have enabled password grant types, let's try:

$ curl -u ${client_id}:${client_secret} -XPOST http://127.0.0.1:5000/oauth/token -F grant_type=password -F username=${username} -F password=valid -F scope=profile

Because this is an example, every user's password is valid. Now you can access /api/me:

$ curl -H "Authorization: Bearer ${access_token}" http://127.0.0.1:5000/api/me

Authorization code flow example

To test the authorization code flow, you can just open this URL in your browser.

$ open http://127.0.0.1:5000/oauth/authorize?response_type=code&client_id=${client_id}&scope=profile

After granting the authorization, you should be redirected to ${redirect_uri}/?code=${code}

Then your app can send the code to the authorization server to get an access token:

$ curl -u ${client_id}:${client_secret} -XPOST http://127.0.0.1:5000/oauth/token -F grant_type=authorization_code -F scope=profile -F code=${code}

Now you can access /api/me:

$ curl -H "Authorization: Bearer ${access_token}" http://127.0.0.1:5000/api/me

For now, you can read the source in example or follow the long boring tutorial below.

IMPORTANT: To test implicit grant, you need to token_endpoint_auth_method to none.

Preparation

Assume this example doesn't exist at all. Let's write an OAuth 2.0 server from scratch step by step.

Create folder structure

Here is our Flask website structure:

app.py         --- FLASK_APP
website/
  app.py       --- Flask App Factory
  __init__.py  --- module initialization (empty)
  models.py    --- SQLAlchemy Models
  oauth2.py    --- OAuth 2.0 Provider Configuration
  routes.py    --- Routes views
  templates/

Installation

Create a virtualenv and install all the requirements. You can also put the dependencies into requirements.txt:

Flask
Flask-SQLAlchemy
Authlib

Hello World!

Create a home route view to say "Hello World!". It is used to test if things working well.

# website/routes.py
from flask import Blueprint
bp = Blueprint(__name__, 'home')

@bp.route('/')
def home():
    return 'Hello World!'
# website/app.py
from flask import Flask
from .routes import bp

def create_app(config=None):
    app = Flask(__name__)
    # load app sepcified configuration
    if config is not None:
        if isinstance(config, dict):
            app.config.update(config)
        elif config.endswith('.py'):
            app.config.from_pyfile(config)
    setup_app(app)
    return app

def setup_app(app):
    app.register_blueprint(bp, url_prefix='')
# app.py
from website.app import create_app

app = create_app({
    'SECRET_KEY': 'secret',
})

Create an empty __init__.py file in the website folder.

The "Hello World!" example should run properly:

$ FLASK_APP=app.py flask run

Define Models

We will use SQLAlchemy and SQLite for our models. You can also use other databases and other ORM engines. Authlib has some built-in SQLAlchemy mixins which will make it easier for creating models.

Let's create the models in website/models.py. We need four models, which are

  • User: you need a user to test and create your application
  • OAuth2Client: the oauth client model
  • OAuth2AuthorizationCode: for grant_type=code flow
  • OAuth2Token: save the access_token in this model.

Check how to define these models in website/models.py.

Once you've created your own website/models.py (or copied our version), you'll need to import the database object db. Add the line from .models import db just after from flask import Flask in your scratch-built version of website/app.py.

To initialize the database upon startup, if no tables exist, you'll add a few lines to the setup_app() function in website/app.py so that it now looks like:

def setup_app(app):
    # Create tables if they do not exist already
    @app.before_first_request
    def create_tables():
        db.create_all()

    db.init_app(app)
    app.register_blueprint(bp, url_prefix='')

You can try running the app again as above to make sure it works.

Implement Grants

The source code is in website/oauth2.py. There are four standard grant types:

  • Authorization Code Grant
  • Implicit Grant
  • Client Credentials Grant
  • Resource Owner Password Credentials Grant

And Refresh Token is implemented as a Grant in Authlib. You don't have to do anything on Implicit and Client Credentials grants, but there are missing methods to be implemented in other grants. Check out the source code in website/oauth2.py.

Once you've created your own website/oauth2.py, import the oauth2 config object from the oauth2 module. Add the line from .oauth2 import config_oauth just after the import you added above in your scratch-built version of website/app.py.

To initialize the oauth object, add config_oauth(app) to the setup_app() function, just before the line that starts with app.register_blueprint so it looks like:

def setup_app(app):
    # Create tables if they do not exist already
    @app.before_first_request
    def create_tables():
        db.create_all()

    db.init_app(app)
    config_oauth(app)
    app.register_blueprint(bp, url_prefix='')

You can try running the app again as above to make sure it still works.

@require_oauth

Authlib has provided a ResourceProtector for you to create the decorator @require_oauth, which can be easily implemented:

from authlib.flask.oauth2 import ResourceProtector

require_oauth = ResourceProtector()

For now, only Bearer Token is supported. Let's add bearer token validator to this ResourceProtector:

from authlib.flask.oauth2.sqla import create_bearer_token_validator

# helper function: create_bearer_token_validator
bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
require_oauth.register_token_validator(bearer_cls())

Check the full implementation in website/oauth2.py.

OAuth Routes

For OAuth server itself, we only need to implement routes for authentication, and issuing tokens. Since we have added token revocation feature, we need a route for revoking too.

Checkout these routes in website/routes.py. Their path begin with /oauth/.

Other Routes

But that is not enough. In this demo, you will need to have some web pages to create and manage your OAuth clients. Check that /create_client route.

And we have an API route for testing. Check the code of /api/me.

Finish

Here you go. You've got an OAuth 2.0 server.

Read more information on https://docs.authlib.org/.

License

Same license with Authlib.

example-oauth2-server's People

Contributors

alpacamax avatar azmeuk avatar felsgo avatar husudosu avatar kitsuneeesan avatar lepture avatar marcejohnson avatar nchudleigh avatar shimniok avatar wokoliu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

example-oauth2-server's Issues

Error following README: flask initdb command results in "no such command" error

To recreate on Linux Mint 18:

  1. git clone https://github.com/authlib/example-oauth2-server.git
  2. cd example-oauth2-server
  3. virtualenv env
  4. source env/bin/activate
  5. pip install -r requirements.txt
  6. export AUTHLIB_INSECURE_TRANSPORT=1
    NOTE: the comment in README says to "Set Flask and Authlib environment variables:" but only provides one command show in #6.
  7. flask initdb

The output from #7 is as follows:

Usage: flask [OPTIONS] COMMAND [ARGS]...
Try "flask --help" for help.

Error: No such command "initdb".

Invalid Client

Hi, following tutorial and receiving an invalid client message. Using the format as suggested:
$ curl -u ${client_id}:${client_secret} -XPOST http://127.0.0.1:5000/oauth/token -F grant_type=password -F username=${username} -F password=valid -F scope=profile

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
* Server auth using Basic with user ''
> POST /oauth/token HTTP/1.1
> Host: 127.0.0.1:5000
> Authorization: Basic Og==
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 453
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------329be3b09d236e51
> 
< HTTP/1.1 100 Continue
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: application/json
< Cache-Control: no-store
< Pragma: no-cache
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic error="invalid_client", error_description=""
< Content-Length: 27
< Server: Werkzeug/0.15.4 Python/3.6.8
< Date: Sat, 08 Jun 2019 00:36:34 GMT
< 
* Closing connection 0
{"error": "invalid_client"}

insufficient scope when requesting access to /api/me

I created the client with scope profile, I did the CURL to grant bearer token, I access with bearer token, and I get this:

{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token."
}

The reason why, after looking, is that the CURL command provided does not have a 'scope' variable in it. It must have -F scope=profile

It would be nice if you provided tests, so I could look at reference to the exact order and method of "success" according to working pytests or unit tests, rather than documentation. Documentation can be interpreted, but tests are either right or wrong.

authlib module not found error

I am getting a module not found error for authlib, i had followed the instructions as written

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/flask/cli.py", line 235, in locate_app
    __import__(module_name)
  File "/Users/raveenbeemsingh/Developer/projects/flask_oauth/example-oauth2-server/app.py", line 1, in <module>
    from website.app import create_app
  File "/Users/raveenbeemsingh/Developer/projects/flask_oauth/example-oauth2-server/website/app.py", line 3, in <module>
    from .models import db
  File "/Users/raveenbeemsingh/Developer/projects/flask_oauth/example-oauth2-server/website/models.py", line 3, in <module>
    from authlib.integrations.sqla_oauth2 import (
ModuleNotFoundError: No module named 'authlib'

invalid_scope - RFC conformance?

Invalid_scope error is responded when the request contains the scope different than that assigned to the user. Will this cause interoperability problem? RFC-6749, section 3.3 states the following (see bold text):

3.3. Access Token Scope

The authorization and token endpoints allow the client to specify the
scope of the access request using the "scope" request parameter. In
turn, the authorization server uses the "scope" response parameter to
inform the client of the scope of the access token issued.

The value of the scope parameter is expressed as a list of space-
delimited, case-sensitive strings. The strings are defined by the
authorization server. If the value contains multiple space-delimited
strings, their order does not matter, and each string adds an
additional access range to the requested scope.

 scope       = scope-token *( SP scope-token )
 scope-token = 1*( %x21 / %x23-5B / %x5D-7E )

The authorization server MAY fully or partially ignore the scope
requested by the client, based on the authorization server policy or
the resource owner's instructions. If the issued access token scope
is different from the one requested by the client, the authorization
server MUST include the "scope" response parameter to inform the
client of the actual scope granted.

If the client omits the scope parameter when requesting
authorization, the authorization server MUST either process the
request using a pre-defined default value or fail the request
indicating an invalid scope. The authorization server SHOULD
document its scope requirements and default value (if defined).

Authentication via a JavaScript client without CLIENT_SECRET ?

Hi good day,

thank you for releasing this example, really appreciate it.

I've went through a few times on how to get this working and i got it to work.

So i have a question:
Is it possible to do a JavaScript client authentication with just the CLIENT_ID only ?

Cos currently, we need CLIENT_SECRET and CLIENT_ID in order to authenticate with app.py.
Is there anyway we can customize app.py such that we can authenticate with just CLIENT_ID only ?
( if app.py can accept CLIENT_ID only, than a JAvaScript client will be able to authenticate with app.py using CLIENT_ID only ).

Thanks!

Does the 'client_secret_post' not support token 'revocation'?

image

image
Hi. I tried with 'client_secret_post', but only an error appears ('invalid_client').
(No changes have been made to the source file.)

but, It works if I try after changing to 'client_secret_basic'. (client_metadata)
image

work request format:
image

Can you make it work in the 'client_secret_post' way?
Maybe I'm trying to do something wrong, but I can't solve it even if I change various ways.

Use client_secret_post as auth method

Hey I'm trying to use client_secret_post to auth and get my token
curl -XPOST xxxxxxxxxxxxxx -F grant_type=password -F username=test -F password=valid -F scope=profile -F client_id=xxxxxxxxx -F client_secret=xxxxxxxxx

Just returns {"error": "invalid_client"} any idea ?

request to None in token endpoint

In the Authorization server, the method create_token_response called by the token endpoint processes a request object by has default request parameter set to None and nothing is passed to its call in the token endpoint in the routes file.
How can it make a response token out of a none request body?

获取到client_id和client_secret 后 无法访问获取token 和访问真正的api接口。

example 启动后创建了一个 Client 并拿到里面的 id 和 secret ,无法访问token .我的代码如下:

def localgettokenByrequests():
    r = requests.post(auth=HTTPBasicAuth('2SLED10pAVovtLhyXkTxaYDk', 'gUPxTr8L0mtqSAWzSefFjGtwgTBeDdJqTCxwVuNlMamoVLC6'),
                      url='http://127.0.0.1:5000/oauth/token?grant_type=client_credentials')
    print( r )
    tokenjson = r.json()
    print(tokenjson)
    print(tokenjson.get('access_token'))
    authorrization = "Bearer {}".format(tokenjson.get('access_token'))

if __name__ =="__main__":
    r = localgettokenByrequests()

我目前想用oauth2 加密我的api接口,我会发放id和secret给另外一个java 服务使用,java通过调用token 接口,得到token,然后再调用真的数据api。请问我是哪里用错了么。

Not routing to authorize after login

i am trying to run this example, able to run and get login screen.
Now i am entering the details in "Alexa" which is my client for this oauth server.

Able to get the login page from Alexa,
However i am not able to navigate to Authorize after proper signing in.

Please help
Regards,
Punit

Does not work on remote systems.

The examples gave me hope early on but they do not work when running on different host systems using domain names based on my tests on AWS Elastic Beanstalk instances.

Issues with the /authorize api

Well , this is very nice example to get me going with oauth in flask otherwise need to hardcode a lot. I am stuck at /authorize api here. I have read Usage section and I am able to successfully implement this , but as I am using restful apis , I need to expose this to the front end and therefore I have separated the apis. Now I need to know in Usage step # 6. In /authorize api

The API is like /authorize?code=<some_code>, This is the code But I am unable to figure out what code is actually the value of this code, To do this I need to send this code value to the front end so that it can get the access_token with that.

Insecure transport error Auth 2

I'm trying to use the api this way :

from authlib.client import OAuth2Session

session = OAuth2Session(client_id='example', scope=scope'test')

uri, state = session.authorization_url(url='http://127.0.0.1:5000/oauth/authorize', response_type='token')

urlencoded = str(set(uri) | set('=&;%+~,*'))

session.fetch_access_token(uri) # urlencoded

But I receive this error :

authlib.specs.rfc6749.errors.InsecureTransportError: insecure_transport:

No matter if I'm passing the uri or the url encoded the error continues.

Obs : I did set the insecure transport to true ! ( export OAUTHLIB_INSECURE_TRANSPORT=1)

Obs ² : Here I found the explanation to the error code, but if I really need a ssl to run it locally, how can I do this ?!

Obs ³ : I tried with curl, and I'm using virtualenv ambient with normal user

Document how to use

I have read the blog post, but it describes more in which steps the example-oauth2-server was made rather than how to use it.

I am trying to set up an OAuth2 server which clients can authenticate against.

I have installed example-oauth2-server on my server, now what?

I get "You are not authenticated, Type any username" and no matter what username I type I am always redirected to /.

I would have expected to be able to generate JWT tokens, can I do that?

website/routes.py lost time module

oauth2/example-oauth2-server/website/routes.py", line 58, in create_client
    client_id_issued_at = int(time.time())
NameError: name 'time' is not defined
127.0.0.1 - - [25/Nov/2019 20:18:51] "POST /create_client HTTP/1.1" 500 -

add

import time

in website/routes.py

Access Authorize Endpoint

I've following the tutorial, but i dont understand how to access /oauth/authorize endpoint, what parameters do I need to access the endpoint? because i got invalid_grant error response.
thanks!

Can't redirect to the result page after login user

Here is what I do:

  1. Directly create all necessary data into sqlite database.
  2. Access https://127.0.0.1:8000/
    Redirect the browser to https://127.0.0.1:5000/. Require input user name. I input user name. But the page shows the user name that I input to me. What I expects is that the page of '/api/me'.
  3. But if I visit https://127.0.0.1:8000/ again, I will get the page of '/api/me'.

Can this example act just like the other providers such as reddit.com?
First access one page, then input user name (and password perhaps), finally come out the result. (Suppose all data needed is already in the database)

Invalid response from remote

i run this example but i got some errors
result of client.py #45 'print resp'
-> Invalid response from remote
runtime error occurred at client.py #46
-> TypeError: sequence index must be integer, not 'str'

this is my step

  1. edit app.py redirect_uris 127.0.0.1:8000 -> serverip:8000
  2. run app.py
  3. open http://serverip:5000/ and login
  4. open http://serverip:5000/client copy client_id and secret
  5. edit client.py
    paste client_id and secret
    127.0.0.1:5000 -> serverip:5000
  6. run client.py
  7. open http://serverip:8000/
    i can see my client_id and my login name.
  8. allow access then error

versions
Python 2.7.3

pip freeze result
Flask==0.10.1
Flask-Login==0.2.9
Flask-OAuthlib==0.4.2
Flask-SQLAlchemy==1.0
Jinja2==2.7.1
MarkupSafe==0.18
MySQL-python==1.2.5
SQLAlchemy==0.9.1
Werkzeug==0.9.4
argparse==1.2.1
distribute==0.6.24
httplib2==0.8
itsdangerous==0.23
oauthlib==0.6.1
requests==2.1.0
uWSGI==1.9.21.1
wsgiref==0.1.2

App not working after fixing issue in #57

After creating __init__.py, issuing flask run, website gives 404 error

$ flask run
 * Serving Flask app "app.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 713-868-903
127.0.0.1 - - [26/Sep/2019 09:14:10] "GET / HTTP/1.1" 404 -

App isn't recognizing routes in routes.py because it is never imported and the blueprint object bp is never registered (as it is in the example code).

AttributeError: 'OAuthRemoteApp' object has no attribute 'authorized_response'

Hi good day,

I am playing with your example, and I received the error:

AttributeError: 'OAuthRemoteApp' object has no attribute 'authorized_response'

I got this error by:

  1. visiting: http://127.0.0.1:8000/ where I am redirected to http://127.0.0.1:5000/oauth/authorize.
  2. After clicking on "YES", I receive the error.

I have already created a user and got the CLIENT_ID and CLIENT_SECRET already. Prior running your example, I've also installed the required packages and versions by running sudo pip install -r requirements.txt

It appears that on client.py,
resp = remove.authorized_response() <- something is wrong with this line.

May I know how do i fix this ?

Thanks and great work!

TypeError: sequence index must be integer, not 'str'

There is another issue but it is closed. This is my first time reporting something so I'm not sure if leaving a comment on a closed issue actually does something.

Anyways I'm stilll having the same problem. The other issue mentions using oauthlib 0.6.0 but no succes. Any ideas?

#4

Invalid grant after get a token

I'm using Postman to test my requests. If I try to get a token, it works and Postman give me the Token, but in the response body I receive 'invalid_grant', it should not work, right ? how to fix it ?

My client :

image

I did try response_type as code, and code + token, grant_type as password and password + authentication_code ...

Error :

image

Token :

image

Unable to authenticate using client credentials

I'm trying to figure out how to use 2-legged workflows in this example. (I read test_oauth2/test_client_credential.py for reference). But it always returns "invalid client", even though the exact same ID and secret works fine for 3-legged workflow as explained in the documentation here. I have tried using both curl and postman to get access tokens using client credentials.
What I have tried so far:

  1. Start provider and create a user.
  2. Get client ID and secret from 127.0.0.1:5000/client
  3. curl -X POST -d "client_id=BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW&client_secret=vxHxrzmUYlEPJXaptZ8IlVqezfLwGWxV6mLXO3jShKEqoWfunU&grant_type=client_credentials" http://127.0.0.1:5000/oauth/token

Or using the OAuth 2.0 mechanism in Postman 5.5.3

DEBUG:flask_oauthlib:Fetched extra credentials, {}.
DEBUG:oauthlib.oauth2.rfc6749.endpoints.token:Dispatching grant_type client_credentials request to <oauthlib.oauth2.rfc6749.grant_types.client_credentials.ClientCredentialsGrant object at 0x110b191d0>.
DEBUG:oauthlib.oauth2.rfc6749.grant_types.client_credentials:Validating access token request, <oauthlib.Request url="http://127.0.0.1:5000/oauth/token", http_method="POST", headers="{u'Content-Length': u'92', u'Accept-Encoding': u'gzip, deflate', u'Host': u'127.0.0.1:5000', u'Accept': u'*/*', u'User-Agent': u'PostmanRuntime/7.1.1', u'Connection': u'keep-alive', u'Content-Type': u'application/x-www-form-urlencoded', u'Authorization': u'<SANITIZED>'}", body="{u'scope': u'email', u'grant_type': u'client_credentials', u'client_id': u'BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW'}">.
DEBUG:oauthlib.oauth2.rfc6749.grant_types.client_credentials:Authenticating client, <oauthlib.Request url="http://127.0.0.1:5000/oauth/token", http_method="POST", headers="{u'Content-Length': u'92', u'Accept-Encoding': u'gzip, deflate', u'Host': u'127.0.0.1:5000', u'Accept': u'*/*', u'User-Agent': u'PostmanRuntime/7.1.1', u'Connection': u'keep-alive', u'Content-Type': u'application/x-www-form-urlencoded', u'Authorization': u'<SANITIZED>'}", body="{u'scope': u'email', u'grant_type': u'client_credentials', u'client_id': u'BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW'}">.
DEBUG:flask_oauthlib:Authenticate client u'BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW'
DEBUG:flask_oauthlib:Authenticate client failed, secret not match.
DEBUG:oauthlib.oauth2.rfc6749.grant_types.client_credentials:Client authentication failed, <oauthlib.Request url="http://127.0.0.1:5000/oauth/token", http_method="POST", headers="{u'Content-Length': u'92', u'Accept-Encoding': u'gzip, deflate', u'Host': u'127.0.0.1:5000', u'Accept': u'*/*', u'User-Agent': u'PostmanRuntime/7.1.1', u'Connection': u'keep-alive', u'Content-Type': u'application/x-www-form-urlencoded', u'Authorization': u'<SANITIZED>'}", body="{u'scope': u'email', u'grant_type': u'client_credentials', u'client_id': u'BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW'}">.
DEBUG:oauthlib.oauth2.rfc6749.grant_types.client_credentials:Client error in token request. (invalid_client)  <oauthlib.Request url="http://127.0.0.1:5000/oauth/token", http_method="POST", headers="{u'Content-Length': u'92', u'Accept-Encoding': u'gzip, deflate', u'Host': u'127.0.0.1:5000', u'Accept': u'*/*', u'User-Agent': u'PostmanRuntime/7.1.1', u'Connection': u'keep-alive', u'Content-Type': u'application/x-www-form-urlencoded', u'Authorization': u'<SANITIZED>'}", body="{u'scope': u'email', u'grant_type': u'client_credentials', u'client_id': u'BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW'}">.
INFO:werkzeug:127.0.0.1 - - [05/Mar/2018 15:39:58] "POST /oauth/token HTTP/1.1" 401 -
POST /oauth/token
content-type: application/x-www-form-urlencoded
authorization: Basic Qk1hVlBvNzNQQ1R6emFVbzNUdUl1NmdYZE9ZOGd1NW9nT0V5OWNMVzp2eEh4cnptVVlsRVBKWGFwdFo4SWxWcWV6Zkx3R1d4VjZtTFhPM2pTaEtFcW9XZnVuVQ==
user-agent: PostmanRuntime/7.1.1
accept: */*
host: 127.0.0.1:5000
accept-encoding: gzip, deflate
content-length: 92
grant_type=client_credentialsscope=emailclient_id=BMaVPo73PCTzzaUo3TuIu6gXdOY8gu5ogOEy9cLW
HTTP/1.1 401
status: 401
content-type: application/json
content-length: 27
pragma: no-cache
cache-control: no-store
server: Werkzeug/0.9.4 Python/2.7.10
date: Mon, 05 Mar 2018 06:39:58 GMT
{"error": "invalid_client"}

I found a Bug for: class ClientAuthentication(object): ---->def authenticate_client_secret_basic(query_client, request):

def authenticate_client_secret_basic(query_client, request):
    """Authenticate client by ``client_secret_basic`` method. The client
    uses HTTP Basic for authentication.
    """
    client_id, client_secret = extract_basic_authorization(request.headers)
    if client_id and client_secret:
        client = _validate_client(query_client, client_id, request.state, 401)
        if client.check_token_endpoint_auth_method('client_secret_basic') \
                and client.check_client_secret(client_secret):
            log.debug(
                'Authenticate {} via "client_secret_basic" '
                'success'.format(client_id)
            )
            return client
    log.debug(
        'Authenticate {} via "client_secret_basic" '
        'failed'.format(client_id)
    )

func: client_id, client_secret = extract_basic_authorization(request.headers) After calling this function ‘client_id’ and 'client_secret' all is 'None',
This causes the client to be authenticated when the access_token is obtained, causing an "Invalid_Client" error.

@lepture Do you have any good solutions?

Fail to generate authorization code

I executed this on terminal or using postman :
curl -XPOST http://127.0.0.1:5000/oauth/authorize -F response_type=code -F client_id=QSgVk2cVtqI6qOpZv9pyCjVU -F redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Foauth%2Fauthorize

but the response always:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>

because i need to get the code, to get access_token with grant_type=authorization_code

Refresh Token

Hello

I followed the steps : after
curl -u "xxxx...":"xxxx..." -XPOST http://0.0.0.0:8080/oauth/token -F grant_type=password -F username="me" -F password=valid -F scope=profile

i got the response in Ubuntu terminal :

{"access_token": "xxxx..", "expires_in": 864000, \ "refresh_token": "xxxx..", "scope": "profile", "token_type": "Bearer"}

how to curl the refresh_token ? should we include something here ? :

include

I wish there would be a complete example

Thanks

Token Endpoint Auth Method

When selecting Token Endpoint Auth Method="none" a client secret is still issued. According to the documentation nonemeans that the client is public and has no secret.

Once the issue is solved,

Endpoint

@bp.route('/oauth/token', methods=['POST']) def issue_token(): return authorization.create_token_response()

does not work with grant password. But If I pass within the body client_id it works. However, according to RFC6749 the body should only contain grant_type, username, password and scope

I might have missed something...
Btw, Should I public client have its own id?

Max retries on token endpoint

When testing the OAuth session with requests, with this example server, everything works well until hitting the /oauth/token endpoint.
The following exeption is raised:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /oauth/token

Is there any setting to set to avoid this?

tokengetter method is not used

Hi!

Problably this isn't an issue but a misunderstanding on my side.
When I implement my oauth2-client according to all the examples, my function decorated with the 'tokengetter' decorator is never actually called.
As I understand it, this is a vital function for verifying the token i saved during the authentication-process before.
shouldn't it be called during every request on a protected Resource then?

/oauth/authorize redirects to /oauth/errors (argument of type 'NoneType' is not iterable)

I cloned the repo, installed the dependencies and followed step 1-5 in the readme. When opening 127.0.0.1:8000 I get directed to /oauth/authorize on port 5000. To this point, everything works as expected, but instead of the authorize page I get redirected to /oauth/errors with error argument of type 'NoneType' is not iterable

app.py (127.0.0.1:5000)

127.0.0.1 - - [26/Oct/2016 02:21:49] "GET /oauth/authorize?response_type=code&client_id=gjIsasqZPgxLbqkZ4xGMTQmfVnAzvG6EhjJoB92A&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Fauthorized&scope=email HTTP/1.1" 302 -
127.0.0.1 - - [26/Oct/2016 02:21:50] "GET /oauth/errors?error=argument+of+type+%27NoneType%27+is+not+iterable HTTP/1.1" 404 -

I'm assuming this could be related to oauthlib/oauthlib#436 but that's just a wild guess.

working with multiple scopes?

How do we do that? Do we enter it as a list in someway?

Specifically I am trying to reconfigure the example for OIDC, and I am trying to use scopes "openid" and "offline-access". But when I try to do a authorization request with an appauth-android test app, the multiple scope always breaks with a invalid character (a space between the scopes). A look at the "environ" parameter in flask/app.py showed that werkzeug.request has the query sort-of decoded, so the %20 of the space is just a " ". This breaks in authlib/common/urls.py url_decode.

the appauth_android test app has worked against other oidc backends, so I don't think its mis-encoding the scope header. I am unsure as to how to properly fix this though.

Anyway, if I write it as "scopeA+scopeB", I get past the error above, but then when it comes to validate the requested scopes, I get invalid scope when the string "scopeA+scopeB" is treated as one single scope, and doesn't match anything.

if I just use ONE scope it seems fine. Gets past the scope check (and into other errors :-p).

Set-up instructions no longer work

If you follow set up instruncions and download project. Create conda env. Install flask, flask-sqlalchemy and Authlib in that env. Then try to run app.py from pycharm, you get error.
I still had conda env, which was created in September and with which prject used to run, and with that env it still runs, but if I create new env following your instructions, I get error:
C:\Users\Ignas\Anaconda3\envs\ugpsts\python.exe -m flask run Traceback (most recent call last): File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\runpy.py", line 183, in _run_module_as_main mod_name, mod_spec, code = _get_module_details(mod_name, _Error) File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\runpy.py", line 142, in _get_module_details return _get_module_details(pkg_main_name, error) File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\runpy.py", line 109, in _get_module_details __import__(pkg_name) File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\site-packages\flask\__init__.py", line 21, in <module> from .app import Flask, Request, Response File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\site-packages\flask\app.py", line 25, in <module> from . import cli, json File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\site-packages\flask\cli.py", line 18, in <module> import ssl File "C:\Users\Ignas\Anaconda3\envs\ugpsts\lib\ssl.py", line 98, in <module> import _ssl # if we can't import it, let the error propagate ImportError: DLL load failed: The specified module could not be found.

Not getting refresh token in oauth/token api

I am calling /oauth/token to generate the token by passing the code. I set OAUTH2_REFRESH_TOKEN_GENERATOR=True in my flask app config. But I only get access_token in the response but not refresh_token.

/oauth/token raise 400 on server

the OAuth2 server raise a 400 status code while attempting to allow access from a client.

~> when I clicked on 'yes' formulair from url http://127.0.0.1:5000/oauth/authorize?response_type=code...etc
~> localhost:8000 throw an errror TypeError: sequence index must be integer, not 'str'

Indeed, the 'resp' is OAuthException

when I look at the log from the OAuth server, it responds to me with
{"error": "invalid_client"}', 400

While it doesn't work even if the id client, secret and so on are correct ?

I've try your example and my own implementation following your tutorial but I've got the same result, can you help me to figure out please!

thank you in advance

bad substitution

$ curl -u ${client_id}:${client_secret} -XPOST http://127.0.0.1:5000/oauth/token -F grant_type=password -F username=${username} -F password=valid -F scope=profile

If client_id or client_secret begin with a number, the command will error out with a bash error: "bad substitution". No request to server will be sent

Running the example without HTTPS

Using the os.environ['DEBUG'] = '1' or 'true' generates an "oauthlib.oauth2.rfc6749.errors.InsecureTransportError" error on the browser for the client.

I had to set the environment variable os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1', and it worked correctly.

"error": "invalid_client"

报错信息如下:我想利用refreshtoken获取时,出错了,没有能够得到预想中的accesstoken

ValueError: 'name' may not contain a dot '.' character. when execute flask run

$ flask run

  • Environment: production
    WARNING: This is a development server. Do not use it in a production deployment.
    Use a production WSGI server instead.
  • Debug mode: off
    Traceback (most recent call last):
    File "c:\users\janaka_w\appdata\local\programs\python\python36\Lib\runpy.py", line 193, in _run_module_as_main
    "main", mod_spec)
    File "c:\users\janaka_w\appdata\local\programs\python\python36\Lib\runpy.py", line 85, in run_code
    exec(code, run_globals)
    File "E:\PYTHON TEST PROJECT\example-oauth2-server-master\env-auth\Scripts\flask.exe_main
    .py", line 7, in
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 990, in main
    cli.main(args=sys.argv[1:])
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 596, in main
    return super().main(*args, **kwargs)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\core.py", line 1062, in main
    rv = self.invoke(ctx)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\core.py", line 1668, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\core.py", line 763, in invoke
    return __callback(*args, **kwargs)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\decorators.py", line 84, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\click\core.py", line 763, in invoke
    return __callback(*args, **kwargs)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 845, in run_command
    app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 321, in init
    self._load_unlocked()
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 346, in _load_unlocked
    self._app = rv = self.loader()
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 406, in load_app
    app = locate_app(self, import_name, None, raise_if_not_found=False)
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\cli.py", line 256, in locate_app
    import(module_name)
    File "E:\PYTHON TEST PROJECT\example-oauth2-server-master\app.py", line 1, in
    from website.app import create_app
    File "E:\PYTHON TEST PROJECT\example-oauth2-server-master\website\app.py", line 5, in
    from .routes import bp
    File "E:\PYTHON TEST PROJECT\example-oauth2-server-master\website\routes.py", line 11, in
    bp = Blueprint(name, 'home')
    File "e:\python test project\example-oauth2-server-master\env-auth\lib\site-packages\flask\blueprints.py", line 195, in init
    raise ValueError("'name' may not contain a dot '.' character.")
    ValueError: 'name' may not contain a dot '.' character.

Getting insecure_transport error even after setting AUTHLIB_INSECURE_TRANSPORT=1

Hi,

I am trying to test basic sequence of OAuth to learn the protocol and use the same on my server.
I have started the example-oauth2-server after calling export AUTHLIB_INSECURE_TRANSPORT=1 but still, I am seeing "insecure_transport" error in the first step of authorization code grant type. I also tried password grant type as you showed in your example and that too responded with the same error.

What could be the reason?

  • My authorization request looks like following:

curl http://127.0.0.1:8080/oauth/authorize?response_type=code&client_id=CLIENT-ID&redirect_uri=SOME-URL&scope=read

I get response "insecure_transport"
(tried with browser also and got same response)

  • My password grant type request is very similar to your example.

Tokens are not stored securely

As per the RFC, "[tokens] MUST be kept confidential in transit and storage." This can be accomplished pretty easily here by storing a hash of the token string instead of the token string itself, although perhaps that logic should be bubbled up into Flask-OAuthlib as well.

Which user id and password should be used to revoke a token?

I am trying to revoke a token and server asks user name and password.

I have tried to use everything I could think,

  1. user name: Client id
    password: valid

  2. uid: Client id
    password: none (just enter)

  3. client_id and client_secret in body

  4. passing client_id:client_secret with url as mentioned in the example at https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
    example: https://username:[email protected]/

Every time I get an invalid client error (with curl) or request to authenticate (with a browser)

OAuth 2.0 server from scratch step by step: missing __init__.py

Python 2.7.12
Flask 1.1.1
Werkzeug 0.16.0
Linux Mint 18

Following instructions in the Preparation section to "write an OAuth 2.0 server from scratch step by step," an error is generated after this step:

The "Hello World!" example should run properly:

$ FLASK_APP=app.py flask run

oauth-flask $ FLASK_APP=app.py flask run
 * Serving Flask app "app.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 713-868-903
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 338, in __call__
    self._flush_bg_loading_exception()
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 326, in _flush_bg_loading_exception
    reraise(*exc_info)
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 314, in _load_app
    self._load_unlocked()
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 330, in _load_unlocked
    self._app = rv = self.loader()
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 388, in load_app
    app = locate_app(self, import_name, name)
  File "/home/mes/Projects/oauth-flask/env/lib/python2.7/site-packages/flask/cli.py", line 247, in locate_app
    "\n\n{tb}".format(name=module_name, tb=traceback.format_exc())
NoAppException: While importing "app", an ImportError was raised:

Traceback (most recent call last):
  File "/home/mes/Projects/oauth-flask/env/local/lib/python2.7/site-packages/flask/cli.py", line 240, in locate_app
    __import__(module_name)
  File "/home/mes/Projects/oauth-flask/app.py", line 2, in <module>
    from website.app import create_app
ImportError: No module named website.app
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET /?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET /?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET /?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [26/Sep/2019 08:48:01] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
 * To enable the debugger you need to enter the security pin:
 * Debugger pin code: 713-868-903
127.0.0.1 - - [26/Sep/2019 08:48:13] "GET /?__debugger__=yes&cmd=printpin&s=umuwziR2LQoWAnXeTxCr HTTP/1.1" 200 -

The error goes away when an __init__.py is added to the website subdirectory which allows Python 2.7 to recognize the directory for importing modules.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.