redirect uri mismatch in fastapi - python

I hope everyone is fine. I am trying to implement google sso on my fastapi app. after entering the user credentials is entered and it gets redirected while redirecting i am getting this error
google_sso = GoogleSSO("client-id", "client-secret", "http://127.0.0.1:8000/google/callback/")
#app1.get("/google/login")
async def google_login():
"""Generate login url and redirect"""
return await google_sso.get_login_redirect()
#app1.get("/google/callback")
async def google_callback(request: Request):
"""Process login response from Google and return user info"""
user = await google_sso.verify_and_process(request)
print("Hellooooooooooooooo")
print(user, "11111111111111")
return {
"id": user.id,
"picture": user.picture,
"display_name": user.display_name,
"email": user.email,
"provider": user.provider,
}
I have shared the URL configuration in google dashboard in below screenshot
enter image description here
the error i have mentioned below
oauthlib.oauth2.rfc6749.errors.CustomOAuth2Error: (redirect_uri_mismatch) Bad Request

The problem could lay in the process_login() function which is getting called in the verify_and_process() function in your /callback api.
Let's take a look inside the process_login() function (https://tomasvotava.github.io/fastapi-sso/sso/base.html#fastapi_sso.sso.base.SSOBase.verify_and_process):
async def process_login(self, code: str, request: Request) -> Optional[OpenID]:
"""This method should be called from callback endpoint to verify the user and request user info endpoint.
This is low level, you should use {verify_and_process} instead.
"""
url = request.url
current_url = str(url).replace("http://", "https://")
current_path = f"https://{url.netloc}{url.path}"
I guess the (redirect_uri_mismatch) error is because you are using a HTTP redirect_url in your GoogleSSO() call:
google_sso = GoogleSSO("client-id", "client-secret", "http://127.0.0.1:8000/google/callback/")
Inside the process_login() function the HTTP of the redirect url inside the url of your request is replaced with HTTPS:
url = request.url
current_url = str(url).replace("http://", "https://")
After that replacement you have a mismatch with your redirect url, because
https://127.0.0.1:8000/google/callback/
is not
http://127.0.0.1:8000/google/callback/
They are two different urls.
Solution could be that you secure your server with HTTPS via self signed certificates.
(That one is very simple: https://dev.to/rajshirolkar/fastapi-over-https-for-development-on-windows-2p7d)
Btw. did you register you app in the google cloud (https://developers.google.com/identity/sign-in/web/sign-in)? Because you are using "client-id" and "client-secret" as parameters.

try it use 127.0.0.1:8000/google/callback #remove /
or
fix url #app1.get("/google/callback/") #add /

This is because the port number is changing in the redirect URI, everytime you run the application.
So everytime you run it it becomes:
http://localhost:65280/
http://localhost:65230/
http://localhost:63280/
And so forth. I dont have a solution for you yet. Working on it myself right now.

Related

How to save and reuse tokens with authlib

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.

Endpoints API authentication with Google

I want to authenticate the request coming to my api such that its a valid google account.
#endpoints.api(name='echo',
version='v1',
allowed_client_ids=[API_EXPLORER_CLIENT_ID,
"*****-************.apps.googleusercontent.com"],
auth_level=AUTH_LEVEL.REQUIRED)
class EchoApi(remote.Service):
#endpoints.method(
# This method takes a ResourceContainer defined above.
ECHO_RESOURCE,
# This method returns an Echo message.
EchoResponse,
path='echo',
http_method='POST',
name='echo',
api_key_required=True)
def echo(self, request):
print endpoints.get_current_user()
print request
output_content = ' '.join([request.content] * request.n)
return EchoResponse(content=output_content)
Above is the code that i implemented then also if i send a request without authorization header the request still reaches backend
You must check if get_current_user() returns None.
auth_level does not actually do anything in this version of the Framework.

facebook messenger Weebhok subscription error

I'd like to connect a chatbot made in python and deployed on aws lambda to a facebook page.
this is my code to verifly connection to facebook
def webhook(event, context):
# #debug
print("event:" )
print(event)
# print("context")
# print(context)
#handle webhook challenge
if keys_exist(event, ["queryStringParameters"]):
print("Veriflying stuff")
v_token = str(find_item(event, 'hub.verify_token'))
print("token :")
print (v_token)
challenge = int(find_item(event, 'hub.challenge'))
print ("challenge")
print(challenge)
if (os.environ['verify_token'] == v_token):
print ("returning stuff")
return (challenge)
But facebook says The URL couldn't be validated. Callback verification failed with the following errors: HTTP Status Code = 502; HTTP Message = Bad Gateway
I have created the urls with serverless. It works well when i do a get request from the browser.
I have given the same url in the facebook Webhook page. And made sure the validation and Verify Token are correct.
I have tried a few things I saw online. But i dont understand a few of them like this one
Facebook Messenger API "URL COULD NOT BE VALIDATED"
I dont understand if I nned a cert file for this?
AND "The URL could not be validated", facebook api error says to give path to a php. Which I dont even use?
I have found the problem.
Facebook also now required the status code of the request.
if keys_exist(event, ["queryStringParameters","hub.verify_token","hub.challenge"]):
print("subscribe to webhook invoked")
v_token = str(find_item(event, 'hub.verify_token'))
challenge = find_item(event, 'hub.challenge')
if ("strongtoken" == v_token):
response = {
"statusCode": 200,
"body": str(challenge)
}
print(challenge)
return response

Handling two step authentication using Python requests module

My requirement is to capture cookie from authentication server and i'm able to achieve it using requests module. However, this piece of code breaks (doesn't return any cookies) when "two step verification" is enabled. Basically if i enable two-step-verification, it will send secure code to registered devices which can be used as second level of authentication. Appreciate any pointers.
import requests
auth = 'https://auth.corp.xyz.com/authenticate'
params = {"accoundId": "xyz",
"accountPassword": "abc",
"appIdKey": "xxxxxxxxxxxxx"
}
s = requests.session()
r = s.post(auth,params = params)
print r.request.headers['Cookie']
'd01_ac=9eba0c6ec6f1c09bafb29be785ad9d2373d588d3b1633a418ab932166ea5f832ae036a4f2a89a675adc6e31ebdc7c251215a4a354a085865f6c775fb4cd7544d4c64a429f27fce08bc99058d40497202a4abeedb51f73a53f246fe148560GZVX; dslang=US-EN; myacinfo=DAWTKNV2eed01036859507613988fcc6e5540e93e248a4cbee7243868b1bf5f53b3d698002b4823e872d35c848af2be146529062c6ee05e320367fadfe0e5ae507dd3c07baab03e878c12949efbeb9471111e2b0930c38c05a714f3cd8896c8b63a1201468909a0de019534f1652d49f7e7f280a82627920dbdc8a1ea413f570d16b87c063963712d5932d61a658ff380407f96f34c90e58c223d39ca90f11cc76ca521a3babf4c9f21ea0d35b77ece1b0b175ec21bd74d2d9b108759c38b71d285dde24d21c9d2eac3dfe2e4bd01df6968ebc12fe6bf132d80a0232d6b8ee61a7bfe134b86f6662316431313062326531613231MVRYV2'
The idea of 2 step authentication is next:
You app says to the server. I want to authenticate a user. Give me a
token.
Here is a token. Redirect your user to this url and pass a token
with it.
Users logs in on the server
Server redirects user to your site by providing data about user and
a 'token' you gave so you can match the token with an exact user who
wanted to authenticate.
Does it make sense?

"Invalid Response from Facebook" when Authenticating with Facebook using Flask-Oauthlib

I'm consistently getting an "Invalid response from Facebook" error when authenticating over Facebook with Oauthlib when building off of the sample code here.
I've outlined the sections of relevant code below.
Setup:
Setting up the Oauth request object.
Not pictured: Navigational routes and Flask app initialization.
from flask_oauthlib.client import OAuth, OAuthException
oauth = OAuth()
facebook = oauth.remote_app('facebook',
base_url='https://graph.facebook.com/',
request_token_url=None,
access_token_url='/oauth/access_token',
authorize_url='https://www.facebook.com/dialog/oauth',
consumer_key=config.get("app_id"),
consumer_secret=config.get("app_secret"),
request_token_params={'scope': 'public_profile,email'}
)
#facebook.tokengetter
def get_facebook_token():
if current_user.is_authenticated():
return current_user.get_facebook_token()
else:
return None
Login handler:
Sending users here in order to begin the process, with the url for the facebook callback appended to the root URL.
#app.route('/facebook/login')
def facebook_login():
return facebook.authorize(callback="http://example.com%s" % url_for('facebook_callback'))
Facebook callback, source of the issue:
From here I can garner that a code (presumably the token) is returned but Oauthlib fails to parse it correctly.
#app.route('/facebook/callback')
def facebook_callback(response):
response = facebook.authorized_response()
if response is None:
flash("You denied the request to sign in.", "error")
return redirect(url_for('index'))
if isinstance(response, OAuthException):
flash("Access denied: %s" % response.message, "error")
return redirect(url_for('index'))
# Request fails here, returns the redirect above.
From dumping the request args I can see fairly clearly that after being directed to Facebook and successfully connecting, there is a very long token being returned to the callback along the lines of '?code=1234567890-abcdefghijklmnop', however actually trying to authenticate with this fails with "Invalid response from Facebook".
Here is a sample request dump:
ImmutableMultiDict([('code', 'AQAPedwWavTb_cBx6NVy-qy3AL5YPr780oG5tA1LfITpVwkk-kr_4I0lG6B-vlhcYEubhuKhEM46bPs-1WpWUpJzcWfhkQ64lIkdLm9uSHSqeBHBM_6zw7SDpvVmGK-JKWBpAqRJuBKenl9zslQizthox96104iiul0uYQY67cmZgPXZi9uL-mcgZ5dRj387eKJIjNninBXxwCGgFkg5kLVHYt7t0ktUH58stYlxn2f98AXuSlrIvWsA5NeHsVbM8XY0XQrDrNbCvjDmEwHQGkZ3uZRbyaecN7MAi0bM0TrZzpuQ8j3M34DnQp_v9n4ktM4')])
Having used similar code based off of the Twitter sample that works, I'm thinking this could be a possible library bug due to Facebook API changes, but I would appreciate any pointers!
For anyone who stumbles upon this from Google in the future, I solved this in a solution that can be read here.
Hey there, I solved this issue in a very hacky way which I would not
recommend for production environments, but I eventually found the
issue a few days after my last message.
When you ask Facebook for an access token, it does NOT give you an
access token in the way you might expect. What I assumed to be a
failure on Facebook's side was instead a (perhaps intentional)
formatting error.
What you might expect:
http://example.com/callback?access_token=00000000000
or
http://example.com/callback with the access token passed as a POST
argument in the headers.
What actually happens is that Facebook responds like this:
http://example.com/callback?#access_token=0000000000
Because of this, it is -impossible- for any server side language
to parse it, as the access token will now only be visible to the
browser itself. It is not passed to the backend whatsoever.
Capturing the request:
#app.route('/facebook/translate', methods=['GET'])
def facebook_translate():
# Facebook responds with the access token as ?#access_token,
# rather than ?access_token, which is only accessible to the browser.
# This part is where things get really, really dumb.
return ''' <script type="text/javascript">
var token = window.location.href.split("access_token=")[1];
window.location = "/facebook/callback?access_token=" + token;
</script> '''
Proceeding as usual:
#app.route('/facebook/callback', methods=['GET', 'POST'])
def facebook_callback():
access_token = request.args.get("access_token")
if access_token == "undefined":
flash("You denied the request to sign in.", "error")
return redirect(url_for('index'))
graph = facebooksdk.GraphAPI(access_token)
profile = graph.get_object("me")

Categories

Resources