I am working with google cloud functions in python and I am facing the following difficulties:
I don't know how to add a token to the automatically generated endpoint for my HTTP cloud function :
Below is the code I deployed :
from flask import Flask, request
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
PROJECT = "projectname"
app = Flask(__name__)
#app.route('/', methods=['PUT', 'POST'])
#auth.login_required
def httpfunction(request):
data = request.stream.read()
return data
The generated URL is :
https://europe-west1-projectname.cloudfunctions.net/httpfunction
I want to add a token to it in order to avoid inappropriate persons to make use it , so basically here I want to avoid POST and PUT requests from unauthorized users.
Any ideas how to do this please ?
HTTPBasicAuth is not using tokens but username and password. To query the endpoint with username and password you should first modify your code to something like this:
from flask import Flask, request
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
users = {
"Obama": "WhiteHouse1",
"Trump": "WhiteHouse2"
}
#auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
app = Flask(__name__)
#app.route('/', methods=['PUT', 'POST'])
#auth.login_required
def httpfunction(request):
data = request.stream.read()
return data
Than you can query the endpoint with providing the username and password. For example a query with cURL would look like this:
curl -u Trump:WhiteHouse2 -X POST https://europe-west1-projectname.cloudfunctions.net/httpfunction
For queries using tokens, take a look at HTTPTokenAuth.
Look into the Stack Overflow thread Secure Google Cloud Functions http trigger with auth for other ways of authentication when using Cloud Functions. Implementation is described in Node.js but the idea can be implemented with python as well.
Related
I've been trying to use Flask-dance's OAuth2ConsumerBlueprint to make requests to the Fitbit API. So far, I've managed to get the authorization page to come up on my Flask app but I haven't been able to route to a page where I can view the data yet.
In browser, when I try to make a request I get the output of the CURL response {"errors":[{"errorType":"system","fieldName":"n/a","message":"Authorization Error: Invalid authorization token type"}],"success":false}
My goal right now is just to be able to view the user's fitbit dashboard in my app through the API by making a request through the app to "https://api.fitbit.com/1/user/-/profile.json"
This is my code so far. If anyone could offer some guidance as to where I'm going wrong with the Oauth2.0 authorization code flow, flask, or the fitbit api I would greatly appreciate it.
from flask import Flask, redirect, url_for, render_template
from flask_dance import OAuth2ConsumerBlueprint
import os
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
CLIENT_ID = 'CLIENT_ID' # OAuth 2.0 Client ID
CLIENT_SECRET = CLIENT_SECRET'
scope = ["activity",
"nutrition",
"heartrate",
"location",
"nutrition",
"profile",
"settings",
"sleep",
"social",
"weight",
]
# Flask OAuth2 Custom Blueprint for Fitbit API
app = Flask(__name__)
fitbit_blueprint = OAuth2ConsumerBlueprint(
"fitbit-api", __name__,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
base_url="https://www.fitbit.com",
token_url="https://api.fitbit.com/oauth2/token",
authorization_url="https://www.fitbit.com/oauth2/authorize",
scope=scope
)
app.register_blueprint(fitbit_blueprint, url_prefix="/login")
app.secret_key = "supersecret" # Replace this
app.token = fitbit_blueprint.token
print(app.token)
#app.route("/")
def index():
#return redirect(url_for("fitbit-api.login"))
return render_template('index.html')
#app.route("/success")
def access():
return "Success"
#app.route('/login')
def login():
return redirect(url_for("fitbit-api.login"))
# Redirect URI = http://127.0.0.1:
if __name__ == '__main__':
app.run(host="localhost", port=5000, debug=True)
You can not access the (user profile) resource before you get authorized from fitbit. Once you got authorized, you will need to exchange your authorization code with a Token Pair and save the tokens - namely Access Token and Refresh Token - somewhere in your code.
Solely being able to reach and pass the authorization page does not mean that you've been authorized. You can check if your authorization is completed within your fitbit profile:
Open your Fitbit profile -> My Dashboard -> Settings. From the left panel choose Applications. There you should be able to see a list of authorized apps: If you don't, you haven't changed your authorization code with a token pair yet!
You can only then be able to make requests to the user-profile endpoint in which case you have to build up an http post with your valid saved Access Token in its header as in https://dev.fitbit.com/build/reference/web-api/oauth2/#making-requests.
I am having trouble implementing authentication verification in an GraphQL Server built with FastAPI. Before, we were using REST but now we are switching to GraphQL and I am wondering how I can implement this. Before, we had different routers and with FastAPI it is easy to check authentication based on routes using dependencies as in here. We are sending a Token in the Authorization Header which we are decoding in the backend and getting back the user_id which we can then use in our different endpoints.
I am wondering how this might work using GraphQL here. We use the Graphene and I had a look at Starlettes Authentication Examples as well as there intro into setting up GraphQl
import binascii
from fastapi import FastAPI
from starlette.authentication import (
AuthenticationBackend, AuthenticationError, SimpleUser, AuthCredentials
)
from starlette.graphql import GraphQLApp
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from schemas.root import my_schema
class BasicAuthBackend(AuthenticationBackend):
async def authenticate(self, request):
if "Authorization" not in request.headers:
raise AuthenticationError('No auth credentials')
auth = request.headers["Authorization"]
try:
id_token = auth.split('Bearer ')[1]
decoded_token = auth.verify_id_token(id_token)
except (ValueError, UnicodeDecodeError, binascii.Error) as exc:
raise AuthenticationError('Invalid basic auth credentials')
user_id = decoded_token['uid']
return AuthCredentials(["authenticated"]), user_id
middleware = [
Middleware(AuthenticationMiddleware, backend=BasicAuthBackend())
]
my_schema = Schema(
query=RootQuery,
mutation=RootMutation,
)
api = FastAPI(title=f"MyGraphQLServer", middleware=middleware)
api.add_route("/graphql", GraphQLApp(schema=my_schema))
For example, imagine that I now would only like to authenticate mutation Requests but not query requests. Furthermore I want to access the user_id in each of my resolvers. What would be the best way to do this?
In FastAPI documentation or starlette documentation they use add_route, that is the way to add a route in Starlette without declaring the specific operation (as would be with .get(), .post(), etc). But it has some disadvantages, we can't add dependencies like we do in FastAPI, example below
app.add_route(
"/graphql",
GraphQLApp(schema=graphene.Schema(query=Query),
executor_class=AsyncioExecutor),
dependencies=(Depends(SomeAuthorizationStuffHere)),
)
So we need to do in FastAPI, i created a simple app with HTTPBasicAuth, you can expand this with other Method's you just need to include the router(s)
from fastapi import Query, Depends, Request, FastAPI, APIRouter
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import graphene
from graphene import Field, Schema, String, ObjectType
from starlette.graphql import GraphQLApp
router = APIRouter()
app = FastAPI()
security = HTTPBasic()
class Query(ObjectType):
hello = Field(String, name=String())
def resolve_hello(root, info, name):
coverage = info.context["request"].state.some_dep
return f"Hello {some_dep.some_method(name)}"
graphql_app = GraphQLApp(schema=Schema(query=Query))
#router.api_route("/gql", methods=["GET", "POST"])
async def graphql(request: Request):
return await graphql_app.handle_graphql(request=request)
app.include_router(router, dependencies=[Depends(security)])
Sorry, new to Flask-Dance.
I'm using the template code from:
https://github.com/singingwolfboy/flask-dance-google/blob/master/google.py
import os
from flask import Flask, redirect, url_for
from flask_dance.contrib.google import make_google_blueprint, google
app = Flask(__name__)
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "supersekrit")
app.config["GOOGLE_OAUTH_CLIENT_ID"] = os.environ.get("GOOGLE_OAUTH_CLIENT_ID")
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET")
google_bp = make_google_blueprint(scope=["profile", "email"])
app.register_blueprint(google_bp, url_prefix="/login")
#app.route("/")
def index():
if not google.authorized:
return redirect(url_for("google.login"))
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
As I understand , if google.authorized is NOT true, it redirects to the route "google.login".
I'm wondering is there a way to know if I have been redirected as a result of being unauthorized from O-auth vs just being directed to the login url.
Thanks
Since you are new to Flask-Dance, I would like to recommend my library Authlib instead. You can check the Google login example for Flask at:
https://github.com/authlib/demo-oauth-client/tree/master/flask-google-login
I have a flask web application and it has multiple blueprints:
restserver
webserver
Most of the endpoints in both webserver and restserver require the user to be logged-in. I use flask-login extension, which provides #login_required decorator.
Is it possible to have different login_view for different blueprints?
# for web blueprint
login_mananger.login_view = '/web/login'
.. so on
One of the most important specialities of RESTful is statelessness, it means the server won't "remember" any information from clients, the requests from clients should have contained all the needed informations, including auth informations.
Back to your question, you don't have to use Flask-Login for RESTful service authentication, and you should not use cookies or sessions because of the diversity of the clients. You can DIY the HTTP authentication of course, but Flask-HTTPAuth is what you really need.
Here is a simplest example of Flask-HTTPAuth:
from flask.ext.httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
#auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username=username).first()
if not user:
return False
g.current_user = user
# You should accomplish the verify_password function by yourself in the User model.
return user.verify_password(password)
According to the doc of Flask-HTTPAuth:
The callback function takes two arguments, the username and the
password and must return True or False.
You should notice that the initialization of the Flask-HTTPAuth just in the blueprint rather than the whole application since this authentication just used in your restserver blueprint.
Then there is a function just like Flask-login's #login_required which provided by Flask-HTTPAuth:
#app.route('/private')
#auth.login_required
def private_page():
return "Only for authorized people!"
This "login_required" callback function will be called when authentication is succesful. Since all of your restserver's route should be protected, you can use a "before_request" handler for applying the protection to the whole blueprint. Assuming that your restserver blueprint's name is "rest" and you have saved the user object in "g" just like what we do before:
from flask import jsonify
#rest.before_request
#auth.login_required
def before_request():
if not g.current_user:
return jsonify({'error':'Unauthorized.'})
I think this will do the trick. What I wrote above is just the simplest example and you can do much better in many ways, such as replacing the user credentials with token after first request. I believe that you will figure it out.
If my answer is helpful, it will be my great honour if you can "upvote" for this answer, thanks.
In your case, you need to place the login manager declaration in the same file as the flask app instance. This is commonly an __init__.py file with the app = Flask(__name__).
At the top, import LoginManager class
from flask_login import LoginManager
Then tie it to the app instance.
login_manager = LoginManager()
login_manager.init_app(app)
(This was not asked but just incase someone needs it) Lets say you have admins and normal users and you are authenticating from different tables:
#login_manager.user_loader
def load_user(user_id):
x = Users.query.get(str(user_id))
if x == None:
x = Admins.query.get(str(user_id))
return x
Finally after importing blueprints you can define the login views for each in a dictionary
login_manager.blueprint_login_views = {
'admin': '/admin/login',
'site': '/login',
}
When I make a redirect from main.py, it works, but when I try to redirect from within a method that it calls, nothing happens. There is no error, the program simply does nothing.
main.py
from githubauth import GetAuthTokenHandler
class AuthUser(webapp2.RequestHandler):
"""
If no environment variable exists with the access token in it,
auth the user as an admin. If it does exist, auth them as a regular
user.
"""
def get(self):
if not ACCESS_TOKEN:
# No access token exists, auth user as admin
get_auth_token = GetAuthTokenHandler()
get_auth_token.get()
githubauth.py
import webapp2
class GetAuthTokenHandler(webapp2.RequestHandler):
"""Redirect users to github to get an access request token."""
def get(self):
self.redirect('http://api.github.com/authorize')
It depends on what kind of authorization you're doing with Github, there are two ways to do that, OAuth token authorization and Web Application Flow.
OAuth Token Authorization
If you're doing OAuth authorization, you don't have to create a request handler to fetch Github auth token, request handler is for serving specific url on your server, for this kind of task, you should use urlfetch().
So the whole flow should be like the following code:
import webapp2
from google.appengine.api import urlfetch
def getAuthToken():
github_auth_url = "http://api.github.com/authorizations"
result = urlfetch.fetch(github_auth_url)
return result
class AuthUser(webapp2.RequestHandler):
def get(self):
if not ACCESS_TOKEN:
# No access token exists, auth user as admin
get_auth_token = getAuthToken()
# do something with your token...
Redirect Authorization (Web Application Flow)
This is the case if you have applied a client id, and want to be authorized by users as a standalone web application, the steps of this kind authorization is more complicated than former one:
Redirect users to request GitHub access
GitHub redirects back to your site
If you don't know about this flow, take a look at Github OAuth - Web Application Flow
Let's see how could we do within Google App Engine
Redirect users to request Github access
This is the part which involved in your sample, simply redirect user to the authorize url with specified parameters
from urllib import urlencode
class AuthUser(webapp2.RequestHandler):
def get(self):
# ... do something ...
# Github configuration
github_client_id = "Your github client id..."
github_redirect_url = "Your url for github redirecting users back to your GAE"
github_scope = "Gtihub scopes...."
github_authorize_url = "http://github.com/login/oauth/authorize"
github_authorize_parameters = {
'client_id': github_client_id,
'redirect_url': github_redirect_url,
'scope': github_scop
}
if not ACCESS_TOKEN:
# if no access_token found on your site, redirect users to Github for authorization
url_to_redirect = "%s?%s" % (github_authorize_url, urlencode(github_authorize_parameters))
self.redirect(url_to_redirect)
Github redirects users back to your site
Github will redirect users back to your site based on the previous parameter redirect_url, so you will have to prepare another request handler for receiving redirection from Github.
(You can do this is the same request handler, but it will mess your code)
The redirection back from the step 1 will contains one parameter, code, you will need it to exchange for an access token.
from urllib import urlencode
class GithubRequestHandler(webapp2.RequestHandler):
def get(self):
# this handler need to be bind to redirect_url
# get authentication code
github_code = self.request.get('code')
# prepare data to exchange access token
github_token_url = "https://github.com/login/oauth/access_token"
github_token_parameters = {
'client_id': 'Your Github client id',
'client_secret': 'Your Github client secret',
'code': github_code}
# exchange access token
data = urlfetch.fetch(github_token_url, payload=urlencode(github_token_parameter), method='POST')
# data will perform in the following form:
# access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&scope=user%2Cgist&token_type=bearer
# extract access_token from the string
# save the access_token to user's model
P.S.
the code is kinda of simulation of your application flow, it needs some tuning to be able to run on production :)
You try to create a webapp2 request handler, but it cannot be done this way. get_auth_token is not a WSGI webapp2 handler instance.
If you do not change githubauth.py you have to change your main.py.
class AuthUser(webapp2.RequestHandler):
def get(self):
if not ACCESS_TOKEN:
self.redirect(to your GetAuthTokenHandler)
This will result in two redirects if you do not have an access token.
RequestHandler needs to be instantiated with a request and a response for things to work properly.
That said, instantiating one and calling methods on it from inside the handler-method of another is pretty weird.