1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
.. _flask_oauth2_resource_protector:
Resource Server
===============
Protects users resources, so that only the authorized clients with the
authorized access token can access the given scope resources.
A resource server can be a different server other than the authorization
server. Authlib offers a **decorator** to protect your API endpoints::
from flask import jsonify
from authlib.integrations.flask_oauth2 import ResourceProtector, current_token
from authlib.oauth2.rfc6750 import BearerTokenValidator
class MyBearerTokenValidator(BearerTokenValidator):
def authenticate_token(self, token_string):
return Token.query.filter_by(access_token=token_string).first()
require_oauth = ResourceProtector()
# only bearer token is supported currently
require_oauth.register_token_validator(MyBearerTokenValidator())
When the resource server has no access to the ``Token`` model (database), and
there is an introspection token endpoint in authorization server, you can
:ref:`require_oauth_introspection`.
Here is the way to protect your users' resources::
@app.route('/user')
@require_oauth('profile')
def user_profile():
# if Token model has `.user` foreign key
user = current_token.user
return jsonify(user)
If the resource is not protected by a scope, use ``None``::
@app.route('/user')
@require_oauth()
def user_profile():
user = current_token.user
return jsonify(user)
# or with None
@app.route('/user')
@require_oauth(None)
def user_profile():
user = current_token.user
return jsonify(user)
The ``current_token`` is a proxy to the Token model you have defined above.
Since there is a ``user`` relationship on the Token model, we can access this
``user`` with ``current_token.user``.
If the decorator is not your favorite, there is a ``with`` statement for you::
@app.route('/user')
def user_profile():
with require_oauth.acquire('profile') as token:
user = token.user
return jsonify(user)
.. _flask_oauth2_multiple_scopes:
Multiple Scopes
---------------
.. versionchanged:: v1.0
You can apply multiple scopes to one endpoint in **AND**, **OR** and mix modes.
Here are some examples:
.. code-block:: python
@app.route('/profile')
@require_oauth(['profile email'])
def user_profile():
user = current_token.user
return jsonify(user)
It requires the token containing both ``profile`` and ``email`` scope.
.. code-block:: python
@app.route('/profile')
@require_oauth(['profile', 'email']')
def user_profile():
user = current_token.user
return jsonify(user)
It requires the token containing either ``profile`` or ``email`` scope.
It is also possible to mix **AND** and **OR** logic. e.g.::
@app.route('/profile')
@require_oauth(['profile email', 'user'])
def user_profile():
user = current_token.user
return jsonify(user)
This means if the token will be valid if:
1. token contains both ``profile`` and ``email`` scope
2. or token contains ``user`` scope
Optional ``require_oauth``
--------------------------
There is one more parameter for ``require_oauth`` which is used to serve
public endpoints::
@app.route('/timeline')
@require_oauth(optional=True)
def timeline_api():
if current_token:
return get_user_timeline(current_token.user)
return get_public_timeline()
MethodView & Flask-Restful
--------------------------
You can also use the ``require_oauth`` decorator in ``flask.views.MethodView``
and ``flask_restful.Resource``::
from flask.views import MethodView
class UserAPI(MethodView):
decorators = [require_oauth('profile')]
from flask_restful import Resource
class UserAPI(Resource):
method_decorators = [require_oauth('profile')]
|