From 31c1ba4024f1894b0c6c73d6045c2e2563b3d6fd Mon Sep 17 00:00:00 2001 From: The Dumb Terminal Date: Sat, 14 May 2022 07:46:04 +0100 Subject: [PATCH 1/5] adding redirect --- flask_restx/templates/swagger-ui.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flask_restx/templates/swagger-ui.html b/flask_restx/templates/swagger-ui.html index b233e018..74298ba3 100644 --- a/flask_restx/templates/swagger-ui.html +++ b/flask_restx/templates/swagger-ui.html @@ -51,8 +51,10 @@ window.onload = function() { const ui = window.ui = new SwaggerUIBundle({ url: "{{ specs_url }}", - {% if config.SWAGGER_UI_OAUTH_CLIENT_ID -%} - oauth2RedirectUrl: "{{ url_for('restx_doc.static', filename='oauth2-redirect.html', _external=True) }}", + {% if config.SWAGGER_UI_OAUTH_REDIRECT_URL -%} + oauth2RedirectUrl: "{{ config.SWAGGER_UI_OAUTH_REDIRECT_URL }}", + {% else -%} + oauth2RedirectUrl: "{{ url_for('restx_doc.static', filename='oauth2-redirect.html', _external=True) }}", {%- endif %} validatorUrl: "{{ config.SWAGGER_VALIDATOR_URL }}" || null, dom_id: "#swagger-ui", From 84a56c73dc1be0d871647cd84e9a9522b71c1748 Mon Sep 17 00:00:00 2001 From: The Dumb Terminal Date: Sat, 14 May 2022 08:50:23 +0100 Subject: [PATCH 2/5] support other oauth callback urls --- examples/github_auth.py | 107 ++++++++++++++++++++++++++ flask_restx/templates/swagger-ui.html | 2 +- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 examples/github_auth.py diff --git a/examples/github_auth.py b/examples/github_auth.py new file mode 100644 index 00000000..139edbaa --- /dev/null +++ b/examples/github_auth.py @@ -0,0 +1,107 @@ +from flask import Flask +from flask_restx import Api, Resource, fields +from werkzeug.middleware.proxy_fix import ProxyFix +import os + +app = Flask(__name__) + +app.config["SWAGGER_UI_OAUTH_REDIRECT_URL"] = os.environ.get("SWAGGER_UI_OAUTH_REDIRECT_URL") + +app.wsgi_app = ProxyFix(app.wsgi_app) + +authorizations = { + 'OAuth2': { + 'type': 'oauth2', + 'flow': 'accessCode', + 'authorizationUrl': 'https://github.com/login/oauth/authorize', + 'tokenUrl': '/', + 'scopes': { + 'user:email': 'Read user email addresses' + } + } +} + +api = Api(app, version="1.0", title="Todo API", description="A simple TODO API", authorizations=authorizations) + +ns = api.namespace("todos", description="TODO operations") + +TODOS = { + "todo1": {"task": "build an API"}, + "todo2": {"task": "?????"}, + "todo3": {"task": "profit!"}, +} + +todo = api.model( + "Todo", {"task": fields.String(required=True, description="The task details")} +) + +listed_todo = api.model( + "ListedTodo", + { + "id": fields.String(required=True, description="The todo ID"), + "todo": fields.Nested(todo, description="The Todo"), + }, +) + + +def abort_if_todo_doesnt_exist(todo_id): + if todo_id not in TODOS: + api.abort(404, "Todo {} doesn't exist".format(todo_id)) + + +parser = api.parser() +parser.add_argument( + "task", type=str, required=True, help="The task details", location="form" +) + + +@ns.route("/") +@api.doc(responses={404: "Todo not found"}, params={"todo_id": "The Todo ID"}) +class Todo(Resource): + """Show a single todo item and lets you delete them""" + + @api.doc(description="todo_id should be in {0}".format(", ".join(TODOS.keys()))) + @api.marshal_with(todo) + def get(self, todo_id): + """Fetch a given resource""" + abort_if_todo_doesnt_exist(todo_id) + return TODOS[todo_id] + + @api.doc(responses={204: "Todo deleted"}) + def delete(self, todo_id): + """Delete a given resource""" + abort_if_todo_doesnt_exist(todo_id) + del TODOS[todo_id] + return "", 204 + + @api.doc(parser=parser) + @api.marshal_with(todo) + def put(self, todo_id): + """Update a given resource""" + args = parser.parse_args() + task = {"task": args["task"]} + TODOS[todo_id] = task + return task + + +@ns.route("/") +class TodoList(Resource): + """Shows a list of all todos, and lets you POST to add new tasks""" + + @api.marshal_list_with(listed_todo) + def get(self): + """List all todos""" + return [{"id": id, "todo": todo} for id, todo in TODOS.items()] + + @api.doc(parser=parser) + @api.marshal_with(todo, code=201) + def post(self): + """Create a todo""" + args = parser.parse_args() + todo_id = "todo%d" % (len(TODOS) + 1) + TODOS[todo_id] = {"task": args["task"]} + return TODOS[todo_id], 201 + + +if __name__ == "__main__": + app.run(debug=True) diff --git a/flask_restx/templates/swagger-ui.html b/flask_restx/templates/swagger-ui.html index 74298ba3..902a2eb2 100644 --- a/flask_restx/templates/swagger-ui.html +++ b/flask_restx/templates/swagger-ui.html @@ -55,7 +55,7 @@ oauth2RedirectUrl: "{{ config.SWAGGER_UI_OAUTH_REDIRECT_URL }}", {% else -%} oauth2RedirectUrl: "{{ url_for('restx_doc.static', filename='oauth2-redirect.html', _external=True) }}", - {%- endif %} + {% endif -%} validatorUrl: "{{ config.SWAGGER_VALIDATOR_URL }}" || null, dom_id: "#swagger-ui", presets: [ From 3572d8e8f31ebf03cd9c76eb7aa640873811e0fc Mon Sep 17 00:00:00 2001 From: The Dumb Terminal Date: Sat, 14 May 2022 09:26:25 +0100 Subject: [PATCH 3/5] added dummy auth handler --- examples/github_auth.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/github_auth.py b/examples/github_auth.py index 139edbaa..912a1f7a 100644 --- a/examples/github_auth.py +++ b/examples/github_auth.py @@ -14,7 +14,7 @@ 'type': 'oauth2', 'flow': 'accessCode', 'authorizationUrl': 'https://github.com/login/oauth/authorize', - 'tokenUrl': '/', + 'tokenUrl': '/auth', 'scopes': { 'user:email': 'Read user email addresses' } @@ -24,6 +24,7 @@ api = Api(app, version="1.0", title="Todo API", description="A simple TODO API", authorizations=authorizations) ns = api.namespace("todos", description="TODO operations") +auth_ns = api.namespace("auth", description="Auth operations") TODOS = { "todo1": {"task": "build an API"}, @@ -102,6 +103,13 @@ def post(self): TODOS[todo_id] = {"task": args["task"]} return TODOS[todo_id], 201 +@auth_ns.route("/") +class Auth(Resource): + """Dummy Auth handler""" + + def post(self): + """Finish auth""" + return True if __name__ == "__main__": app.run(debug=True) From 331e314ece04900fdad40b3c1367296f549e106e Mon Sep 17 00:00:00 2001 From: The Dumb Terminal Date: Fri, 26 Jul 2024 21:28:55 +0100 Subject: [PATCH 4/5] still check for auth ID --- flask_restx/templates/swagger-ui.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flask_restx/templates/swagger-ui.html b/flask_restx/templates/swagger-ui.html index 902a2eb2..7c44bf7f 100644 --- a/flask_restx/templates/swagger-ui.html +++ b/flask_restx/templates/swagger-ui.html @@ -51,10 +51,12 @@ window.onload = function() { const ui = window.ui = new SwaggerUIBundle({ url: "{{ specs_url }}", - {% if config.SWAGGER_UI_OAUTH_REDIRECT_URL -%} - oauth2RedirectUrl: "{{ config.SWAGGER_UI_OAUTH_REDIRECT_URL }}", - {% else -%} - oauth2RedirectUrl: "{{ url_for('restx_doc.static', filename='oauth2-redirect.html', _external=True) }}", + {% if config.SWAGGER_UI_OAUTH_CLIENT_ID -%} + {% if config.SWAGGER_UI_OAUTH_REDIRECT_URL -%} + oauth2RedirectUrl: "{{ config.SWAGGER_UI_OAUTH_REDIRECT_URL }}", + {% else -%} + oauth2RedirectUrl: "{{ url_for('restx_doc.static', filename='oauth2-redirect.html', _external=True) }}", + {% endif -%} {% endif -%} validatorUrl: "{{ config.SWAGGER_VALIDATOR_URL }}" || null, dom_id: "#swagger-ui", From 089328742d0aaf7e95c44498e127c1294a139770 Mon Sep 17 00:00:00 2001 From: The Dumb Terminal Date: Fri, 26 Jul 2024 23:06:31 +0100 Subject: [PATCH 5/5] added docs and required env var --- examples/github_auth.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/github_auth.py b/examples/github_auth.py index 912a1f7a..e5dccb1b 100644 --- a/examples/github_auth.py +++ b/examples/github_auth.py @@ -1,3 +1,11 @@ +# Example GitHub OAuth application +# +# With the flask-restx project checked out locally, run as follows from the project's root: +# SWAGGER_UI_OAUTH_REDIRECT_URL="xxx" FLASK_APP=examples/github_auth.py FLASK_ENV=development SWAGGER_UI_OAUTH_CLIENT_ID=xxx PYTHONPATH=. flask run +# +# Ensure that SWAGGER_UI_OAUTH_REDIRECT_URL and SWAGGER_UI_OAUTH_CLIENT_ID match your GitHub app's OAuth settings. +# + from flask import Flask from flask_restx import Api, Resource, fields from werkzeug.middleware.proxy_fix import ProxyFix @@ -6,6 +14,7 @@ app = Flask(__name__) app.config["SWAGGER_UI_OAUTH_REDIRECT_URL"] = os.environ.get("SWAGGER_UI_OAUTH_REDIRECT_URL") +app.config["SWAGGER_UI_OAUTH_CLIENT_ID"] = os.environ.get("SWAGGER_UI_OAUTH_CLIENT_ID") app.wsgi_app = ProxyFix(app.wsgi_app)