Endpoints API authentication with Google - python

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.

Related

redirect uri mismatch in fastapi

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.

How to pass received (bearer) access token to generated REST Client in order to invoke secured API-Gateway Endpoint

Im fairly new to AWS and its Cognito and API-Gateway services.
I have created in AWS a Cognito-specific User Pool and an AWS-specific API-Gateway API with some API-Endpoints to be accessed via REST API calls. The API-Gateway "Authorizer" is set to "Cognito".
After that, I have exported the Swagger document/OpenAPI2.0 using the AWS-Console specific export function and generated with the Swagger Editor a Python REST Client API.
The generated REST Client SDK generated the Model-specific "GET" function, e. g.:
# create an instance of the API class
api_instance = swagger_client.DefaultApi()
user_id = 'user_id_example' # str |
try:
api_response = api_instance.user_get(user_id)
pprint(api_response)
except ApiException as e:
print("Exception when calling DefaultApi->user_get: %s\n" % e)
In order to get a correct result from the function call api_instance.user_get(user_id)
I need somehow pass the access token to that function.
The question is now, how do I pass the access token - which I have successfully obtained after the User signed-in - to the Python REST Client API in order to invoke an API-Endpoint function which has an "Cognito" authorizer set?
I saw many expamples how to realize this with Postman or CURL, but this is not what I'm looking for. I want to invoke my "Cognito" protected API-Endpoint in AWS API-Gateway with the generated REST API Client. I assume, there must be a way to put the received access token to the "Authorization" Header in the HTTP-Request call, before the generated REST Client function is invoked.
Any help is very appreciated.
I'm not sure if I've understood you correctly, but this might help you.
import requests
endpoint = ".../api/ip"
data = {"ip": "1.1.2.3"}
headers = {"Authorization": "Bearer MyBearerAuthTokenHere"}
print(requests.post(endpoint, data=data, headers=headers).json())
#Edit
You don't need to parse the response as json if it isn't. This is just an Sample.

Can i append a value to my flask request object in the #app.before_request and pass it forward to the endpoint view function?

I am developing some basic REST APIs in python. I am expecting an authorization token in the header of all requests except some unsecured requests like login and register. I am validating the token in #app.before_request and then I want to pass the decoded payload to the corresponding endpoint view function. But, I am not to attach the decoded info to the request object as I get "TypeError: 'Request' object does not support item assignment".
#app.before_request
def before_request():
print(request.endpoint)
if request.endpoint=="register" or request.endpoint=="login":
pass
else:
auth_header = request.headers.get('Authorization')
if auth_header:
auth_token = auth_header.split(" ")[1]
token=decode_auth_token(auth_token)
request["token"]=token
else:
return jsonify({"result":"","error":"No token present in header !"})
I am thinking of this implementation like an auth filter where all requests pass this filter. I can strike off the ill requests at this layer itself and also, I can fetch the user specific info which is needed in the net middleware.
I have a similar usecase (surprisingly similar, actually). I got around it by setting a custom property in the request object, much like your approach, although instead of using direct assignment (i.e. request["token"]=token), I used setattr(request, "token", token).
I got the tip from a bottle plugin which does something very similar:
https://github.com/agile4you/bottle-jwt/blob/master/bottle_jwt/auth.py#L258
You may even wanna try that, or some other plugin to further improve your application.
Flask offers g to propagate data in application context.
from flask import g
#app.before_request
def before_request():
g.token = vaue
#bp.route("/path", methods=["GET"]):
g.token
Reference:
https://flask.palletsprojects.com/en/2.2.x/appcontext/

"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")

Does OAuth2Decorator store user Credentials and renew Tokens?

I'm working with the OAuth2Decorator() and Pytgon I'm in that stage where i'm still unsure of something about the App Engine. The documentation is not providing any info or I simply can't follow it. So:
Does OAuth2Decorator() store user Crediantials?
Does OAuth2Decorator() retrieve new tokens automatically?
Conside this following example.:
decorator = OAuth2Decorator(...)
service = build("drive", "v2")
class AppHandler(BaseHandler):
#decorator.oauth_aware
def get(self):
if decorator.has_credentials():
init = service.files().list().execute(decorator.http())
items = init['items']
context = {'data': getitems(items)}
self.render_response('index.html',**context)
else:
url = decorator.authorize_url()
self.redirect(url)
The credentials get stored as CredentialsModel in the datastore.
Provided the access that's requested is 'offline' (I believe this is the default), then there will be a 'refresh token' stored alongside the temporary access-token. If a request is made with a credentials-wrapped Http client, then upon receiving a response that indicates the access token has expired, the client make a request to get a new access token automatically, and then the original request will be retried with the new access token, which will then be stored in place of the expired one.

Categories

Resources