I'm trying to understand what is the best way a POST request can be redirected to a GET request.
for example -
POST /redirect HTTP/1.1
Host: www.example1.com
url=www.example2.com
and i've created the following flask to help me with that :
from flask import Flask,request, redirect
app = Flask(__name__)
#app.route('/redirect',methods=['POST'])
def redire():
url = request.form['url']
return redirect('https://www.example2.com', code=307)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
the "issue" in my case, is that the request that is being sent to
https://www.example2.com
is also a POST request which is not what i wanted.
Consider that I don't "care" about the body that needs to be sent to the
https://www.example2.com
endpoint, what is the best way to do so without any user intervention (meaning that I'm aiming for an auto redirect).
Note: I've tried to do it via PHP but I can't seem to figure it out.
Apologies if something is not clear.
In order to redirect a POST request to a GET request, you need to use code=303 because it requires the client to use the GET method to retrieve the requested resource.
#app.route('/redirect',methods=['POST'])
def redire():
url = request.form['url']
return redirect('https://www.example2.com', code=303)
the server
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/redirect', methods=['POST'])
def redire():
return redirect('http://127.0.0.1:8888/get')
#app.route('/get', methods=['GET'])
def iam_get():
return {"code": "ok"}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
the client
import requests
data = requests.get("http://127.0.0.1:8888/get")
print(data.text)
data = requests.post("http://127.0.0.1:8888/redirect")
print(data.text)
the result as follows
{"code":"ok"}
{"code":"ok"}
I'm very beginner with authlib and trying to understand its concepts.
I try to understand, how can I save and reuse fetched tokens with authlib.
I created small FastAPI project:
from fastapi import FastAPI
from starlette.config import Config
from starlette.middleware.sessions import SessionMiddleware
from starlette.requests import Request
from authlib.integrations.starlette_client import OAuth
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="some-random-secret")
config = Config(".env")
oauth = OAuth(config)
oauth.register(
name="some_service",
client_id="client_id",
client_secret="client_secret",
authorize_url="https://some-service.com/auth",
access_token_url="https://some-service.com/token",
client_kwargs={
"token_endpoint_auth_method": "client_secret_post",
},
)
#app.get("/login")
async def login(request: Request):
redirect_uri = "https://myservice.com/auth"
return await oauth.some_service.authorize_redirect(request, redirect_uri)
#app.get("/auth")
async def auth(request: Request):
token = await oauth.some_service.authorize_access_token(request)
# I suppose that I should save somehow token here
return token
#app.get("/account")
async def get_account(request: Request):
account_url = "https://some-service.com/account"
resp = await oauth.some_service.get(account_url)
return resp.json()
I want to get account info. So, further steps will be:
GET /login
I'm giving access to use my account and will be redirected back to my service.
GET /auth?oauth_params1=foo&oauth_params2=bar
There will be fetched tokens from token provider. I know that I'm wrongly supposing that token will somehow saved somewhere.
GET /account
And there I'm expecting that with OAuth client I can send previously fetched token. But, I'm getting next error:
authlib.integrations.base_client.errors.MissingTokenError: missing_token:
I also know that I should provide token like that:
oauth.some_service.get(account_url, token=previously_fetched_token)
But, I don't want to ask every time token from some-service I want to reuse token. How to do that?
Am I wrong that this issue is the part of authlib scope? Should I find solution with cache or database mechanisms?
p.s.: I'm really beginner with FastAPI too...
The token is an object with several values-
{
"oauth_token": "TOKEN ID",
"oauth_token_secret": "SECRET TOKEN",
"user_id": "USER ID",
"screen_name": "USER SCREEN NAME"
}
You have several options-
Use a database model that has those values. Use the "user_id" as the primary key, as the "screen_name" can be changed by users.
JSON encode the whole object and stash it somewhere.
Shove it into a cookie object so it's sent back with each request. The nice part of this is you don't have to worry about storing the oauth token at all, but it means you can't do anything with it outside of user requests.
I'm attempting to create a Flask application in Python that communicates with a RESTful API that uses OAuth2.0, but I'm having trouble sending requests. I'm successfully getting an access token from the server, but when I try to make a GET or POST request, I always get a 404 as a response. I'm new to RESTful API's so I don't know what I could possibly be doing wrong here or what a 404 means in this context.
Here is the .py file where I attempt to send a GET and POST request to the API. TokenHandler is just a helper class I made that keeps track of token related info, and the print(response) lines always print "Response [404]".
from flask import Flask, render_template, request, redirect
from src.apitest import TokenHandler
import requests
from oauthlib.oauth2 import LegacyApplicationClient
from requests_oauthlib import OAuth2Session
import json
app = Flask(__name__)
token_handler = TokenHandler()
#app.route('/find_customer')
def find_customer():
return render_template('customer_find_page.html')
#app.route('/find_customer', methods=['POST'])
def find_customer_get():
customer_id = request.form['text0']
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=token_handler.CLIENT_ID))
oauth.fetch_token(token_url=token_handler.TOKEN_ENDPOINT,
username=token_handler.USERNAME, password=token_handler.PASSWORD, client_id=token_handler.CLIENT_ID,
client_secret=token_handler.CLIENT_SECRET,
scope=token_handler.SCOPE)
response = oauth.get("https://sedonacloudtest.com/api/customers/"+customer_id)
print(response)
return redirect('/find_customer')
#app.route('/add_customer')
def add_customer():
return render_template('customer_add_page.html')
#app.route('/add_customer', methods=['POST'])
def add_customer_post():
customer_id = request.form['text0']
customer_name = request.form['text1']
data = {"customer_id": customer_id, "customer_name": customer_name}
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=token_handler.CLIENT_ID))
oauth.fetch_token(token_url=token_handler.TOKEN_ENDPOINT,
username=token_handler.USERNAME, password=token_handler.PASSWORD, client_id=token_handler.CLIENT_ID,
client_secret=token_handler.CLIENT_SECRET,
scope=token_handler.SCOPE)
response = oauth.post("https://sedonacloudtest.com/api/customers", data=data)
print(response)
return redirect('/add_customer')
if __name__ == "__main__":
app.run()
Right now I have a program that receives an SMS message, posts the body of the message, and then forwards the SMS to another number. However I'm receiving an error from Twilio about the "Scheme validation." The code functions exactly as it should, but I'd like to fix the error.
Initially I had the following code:
import RUAlertsTwilioWEBSERVER
import twilio.twiml
import time
import praw
from flask import Flask, request, redirect
from twilio.rest import TwilioRestClient
from passwords import *
from twilio import twiml
def login():
r = praw.Reddit(app_ua)
r.set_oauth_app_info(app_id, app_secret, app_uri)
r.refresh_access_information(app_refresh)
return r
r=RUAlertsTwilioWEBSERVER.login()
client = TwilioRestClient(account_sid, auth_token)
app = Flask(__name__)
#app.route("/", methods=['GET', 'POST'])
def AlertService():
TheMessage=request.form.get("Body")
if (TheMessage != None):
print(TheMessage)
client.messages.create(to=ePhone,from_=tPhone,body=str(TheMessage))
r.submit(*submit to reddit code*)
return str(TheMessage)
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
The Twilio debugger gives me
Content is not allowed in prolog.
Warning - 12200
Schema validation warning
The provided XML does not conform to the Twilio Markup XML schema.
I tried to get the XML needed for the post by changing my code to the following (Only the relevant part)
#app.route("/", methods=['GET', 'POST'])
def AlertService():
TheMessage=request.form.get("Body")
if (TheMessage != None):
print(TheMessage)
resp = twiml.Response()
XML = resp.say(TheMessage)
client.messages.create(to=ePhone,from_=tPhone,body=XML)
r.submit(*submit to reddit code*)
return str(resp)
return str(TheMessage)
This code didn't work so I changed body=XML to body=str(XML). However now it just sends the XML as the body and I receive the error:
cvc-complex-type.2.4.a: Invalid content was found starting with element 'Say'. One of '{Sms, Message, Redirect}' is expected.
Warning - 12200
Schema validation warning
The provided XML does not conform to the Twilio Markup XML schema.
How can I fix this?
Twilio evangelist here.
<Say> is not a valid TwiML verb to include in response to a request made to a Message Request URL. Its only valid for Voice Requests.
If you want to send a message back to the person who sent the text message to Twilio use the <Message> verb.
resp = twilio.twiml.Response()
resp.message(message)
Also, it looks like you are sending the TwiML as the message of the new outbound SMS message. I think you can replace that with just the Body param.
client.messages.create(to=ePhone,from_=tPhone,body=TheMessage)
Hope that helps.
I'm trying to do the equivalent of Response.redirect as in C# - i.e.: redirect to a specific URL - how do I go about this?
Here is my code:
import os
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return 'Hello World!'
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
You have to return a redirect:
import os
from flask import Flask,redirect
app = Flask(__name__)
#app.route('/')
def hello():
return redirect("http://www.example.com", code=302)
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
See the documentation on flask docs. The default value for code is 302 so code=302 can be omitted or replaced by other redirect code (one in 301, 302, 303, 305, and 307).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from flask import Flask, redirect, url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for('foo'))
#app.route('/foo')
def foo():
return 'Hello Foo!'
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Take a look at the example in the documentation.
From the Flask API Documentation (v. 2.0.x):
flask.redirect(location, code=302, Response=None)
Returns a response object (a WSGI application) that, if called, redirects the client to the target location. Supported codes are 301, 302, 303, 305, and 307. 300 is not supported because it’s not a real redirect and 304 because it’s the answer for a request with a request with defined If-Modified-Since headers.
New in version 0.6: The location can now be a unicode string that is
encoded using the iri_to_uri() function.
Parameters:
location – the location the response should redirect to.
code – the redirect status code. defaults to 302.
Response (class) – a Response class to use when instantiating a response. The default is werkzeug.wrappers.Response if unspecified.
I believe that this question deserves an updated. Just compare with other approaches.
Here's how you do redirection (3xx) from one url to another in Flask (0.12.2):
#!/usr/bin/env python
from flask import Flask, redirect
app = Flask(__name__)
#app.route("/")
def index():
return redirect('/you_were_redirected')
#app.route("/you_were_redirected")
def redirected():
return "You were redirected. Congrats :)!"
if __name__ == "__main__":
app.run(host="0.0.0.0",port=8000,debug=True)
For other official references, here.
flask.redirect(location, code=302)
Docs can be found here.
Flask includes the redirect function for redirecting to any url. Futhermore, you can abort a request early with an error code with abort:
from flask import abort, Flask, redirect, url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for('hello'))
#app.route('/hello'):
def world:
abort(401)
By default a black and white error page is shown for each error code.
The redirect method takes by default the code 302. A list for http status codes here.
its pretty easy if u just want to redirect to a url without any status codes or anything like that u can simple say
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/')
def redirect_to_link():
# return redirect method, NOTE: replace google.com with the link u want
return redirect('https://google.com')
here is the link to the Flask Docs for more explanation
For this you can simply use the redirect function that is included in flask
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/')
def hello():
return redirect("https://www.exampleURL.com", code = 302)
if __name__ == "__main__":
app.run()
Another useful tip(as you're new to flask), is to add app.debug = True after initializing the flask object as the debugger output helps a lot while figuring out what's wrong.
There are two ways you can redirect to a URL in Flask.
You want to for example, redirect a user to another route after he or she login, etc.
You might also want to redirect a user to a route that expect some variable example: #app.route('/post/<string:post_id>')
Well, to implement flask redirect for case # 1, its simple, just do:
from flask import Flask,redirect,render_template,url_for
app = Flask(__name__)
#app.route('/login')
def login():
# if user credentials are valid, redirect to user dashboard
if login == True:
return redirect(url_for(app.dashboard))
else:
print("Login failed !, invalid credentials")
return render_template('login.html',title="Home Page")
#app.route('/dashboard')
def dashboard():
return render_template('dashboard.html',title="Dashboard")
To implement flask redirect for case #2, do the following
from flask import Flask,redirect,render_template,url_for
app = Flask(__name__)
#app.route('/home')
def home():
# do some logic, example get post id
if my_post_id:
# **Note:** post_id is the variable name in the open_post route
# We need to pass it as **post_id=my_post_id**
return redirect(url_for(app.open_post,post_id=my_post_id))
else:
print("Post you are looking for does not exist")
return render_template('index.html',title="Home Page")
#app.route('/post/<string:post_id>')
def open_post():
return render_template('readPost.html',title="Read Post")
Same thing can be done in view
Please Note: when redirecting always use the app.home or app.something.. (route or view function name) instead of using redirect("/home").
Reason is, if you modify the route example from "/home" to "/index/page" for some reason, then your code will break
You can use like this:
import os
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
# Redirect from here, replace your custom site url "www.google.com"
return redirect("https://www.google.com", code=200)
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Here is the referenced link to this code.
How to Redirect Users / Requests in Flask
Throwing an Error inside of your API handler function will redirect your user to an error handler, which can handle redirection. Alternatively you can just call redirect like everyone else is saying, but this is another way of redirecting unauthorized users. To demonstrate what I mean, I've provided an example below.
In a case where Users should be Authorized
First lets assume you have a protected route of which you protected like this.
def handle_api_auth(func):
"""
**handle_api_auth**
wrapper to handle public api calls authentications
:param func: a function to be wrapped
:return: wrapped function
"""
#functools.wraps(func)
def auth_wrapper(*args, **kwargs):
api_key: Optional[str] = request.headers.get('x-api-key')
secret_token: Optional[str] = request.headers.get('x-secret-token')
domain: Optional[str] = request.base_url
if is_request_valid(api_key=api_key, secret=secret_token, domain=domain):
return func(*args, **kwargs)
# NOTE: throwing an Error Here will redirect your user to an error handler or alteratively you can just call redirect like everyone else is saying, but this is another way of redirecting unathorized users
message: str = "request not authorized"
raise UnAuthenticatedError(status=error_codes.un_auth_error_code, description=message)
return auth_wrapper
Definition of is_request_valid is as follows
#app_cache.cache.memoize(timeout=15 * 60, cache_none=False) # timeout equals fifteen minutes // 900 seconds
def is_request_valid(api_key: str, secret: str, domain: str) -> bool:
"""
**is_api_key_valid**
validates api keys on behalf of client api calls
:param api_key: str -> api_key to check
:param secret: str -> secret token
:param domain: str -> domain registered for the api_key and secret_token
:return: bool -> True if api_key is valid
"""
organization_id: str = config_instance.ORGANIZATION_ID
# NOTE: lets assumy api_keys_view.get_api_key will return the api keys from some database somewhere
response = api_keys_view.get_api_key(api_key=api_key, organization_id=organization_id)
response_data, status_code = response
response_dict = response_data.get_json()
if not response_dict.get('status'):
return False
api_instance: dict = response_dict.get('payload')
if not isinstance(api_instance, dict):
return False
domain: str = domain.lower().strip()
# NOTE accessing the keys this way will throw ValueError if keys are not available which is what we want
# Any Error which gets thrown Ridirects the Users from the path the user is on to an error handler.
is_secret_valid: bool = hmac.compare_digest(api_instance['secret_token'], secret)
is_domain_valid: bool = hmac.compare_digest(api_instance['domain'], domain)
_request_valid: bool = is_secret_valid and is_domain_valid
return not not api_instance.get('is_active') if _request_valid else False
Define your Error Handlers like this
from flask import Blueprint, jsonify, request, redirect
from werkzeug.exceptions Unauthorized
error_handler = BluePrint('error_handlers', __name__)
#error_handler.app_errorhandler(Unauthorized)
def handle_error(e : Unauthorized) -> tuple:
"""default unath handler"""
return jsonify(dict(message=e.description)), e.code if request.headers.get('content-type') == 'application/json' else redirect('/login')
handle other errors the same and note that in-case the request was
not a json the user gets redirected to a login page
if json the user gets sent an unathecated response then its
up to the front end to handle Unath Errors..