Dash Cognito Authentication Logout - python

I have managed to use this package here to authorize login for my users. However, I am having trouble designing a logout system.
So far, what I’ve done is code up the following method in the class defined here. It essentially calls the revoke endpoint documented here. The revoke endpoint returns a 200 response.
def logout_request(self):
if self.is_authorized():
client_id = self.cognito_bp.client_id
client_secret = self.cognito_bp.client_secret
token = session.get("cognito_oauth_token")["refresh_token"]
resp = cognito.post(
"/oauth2/revoke",
params={"token": token},
headers={"Content-Type": "application/x-www-form-urlencoded"},
auth=HTTPBasicAuth(client_id, client_secret)
)
assert resp.ok, resp.text
del self.cognito_bp.token
session.clear()
return render_template("logout.html")
else:
return self.login_request()
Then, in the application.py folder, I have a Flask route defined:
#application.route("/logout", methods=["GET", "POST"])
def logout_user():
return auth.logout_request()
However, for some reason, the system still keeps me logged in. I feel like I need to delete a cookie server side. Any ideas on how to accomplish this? Jumping ahead, how would I be able to design a multi page concept given that I have written an explicit server route for the “logout” endpoint?

Related

Cookie is not created when calling the endpoint in FastAPI

I have encountered an issue, as I have to create a cookie in the backend, which I will later use to send a request from the frontend. Both apps are on the same domain. This is the general idea behind it: https://levelup.gitconnected.com/secure-frontend-authorization-67ae11953723.
Frontend - Sending GET request to Backend
#app.get('/')
async def homepage(request: Request, response_class=HTMLResponse):
keycloak_code = 'sksdkssdk'
data = {'code': keycloak_code}
url_post = 'http://127.0.0.1:8002/keycloak_code'
post_token=requests.get(url=url_post, json = data )
return 'Sent'
if __name__ == '__main__':
uvicorn.run(app, host='local.me.me', port=7999,debug=True)
Backend
#app.get("/keycloak_code")
def get_tokens(response: Response, data: dict):
code = data['code']
print(code)
....
requests.get(url='http://local.me.me:8002/set')
return True
#app.get("/set")
async def createcookie(response: Response):
r=response.set_cookie(key='tokic3', value='helloworld', httponly=True)
return True
if __name__ == '__main__':
uvicorn.run(app, host='local.me.me', port=8002, log_level="debug")
When I open the browser and access http://local.me.me:8002/set, I can see that the cookie is created.
But when I make a GET request from my frontend to backend to the same URL, the request is received—as I can see in the terminal—but the backend does not create the cookie. Does anyone know what I might be doing wrong?
I have tried different implementations from FastAPI docs, but none has similar use cases.
127.0.0.1 and localhost (or local.me.me in your case) are two different domains (and origins). Hence, when making a request you need to use the same domain you used for creating the cookie. For example, if the cookie was created for local.me.me domain, then you should use that domain when sending the request. See related posts here, as well as here and here.
You also seem to have a second FastAPI app (listenning on a different port) acting as your frontend (as you say). If that's what you are trying to do, you would need to use Session Objects in Python requests module, or preferably, use a Client instance from httpx library, in order to persist cookies across requests. The advantage of httpx is that it offers an asynchronous API as well, using the httpx.AsyncClient(). You can find more details and examples in this answer, as well as here and here.

Google signin API using oauth Post Method in python flask server

I got question about implementing google login. I was able to implement Google Login button on my react app using an open source library called react-google-login. I was able to set up the backend server using python flask. I host my api method on the api on Heroku: http://arrangement-server.herokuapp.com/login.
And my react app runs successfully locally, and I am able to use login-in button. But, my issue is that my server displays the following error:
Method not Allowed.
Is this because it is post method?
Why is it my Server shows me that that method is not allowed?
Even though on the client side it runs fine, and I am able to see user profile and user information.
Here's the code to my backend server, you can find it at Github:
#app.route("/login", methods=['POST'])
def login():
data = request.json
session['access_token'] = data['access_token'], ''
return jsonify({'message':'You are logged in.'})
Your "login" endpoint will accept only "POST" HTTP requests. Because of this line:
#app.route("/login", methods=['POST'])
When you try to open your page in a browser - the browser will send the "GET" HTTP request to that URL.
That is why you are getting "Method Not Allowed" error.
Take a look at my answer on upwork for more details.
Your heroku server is only a backend server.
And the route "/login" accepts only POST request.
So you can't send the GET request to this route on web browser.
If you want to look at the response with this route, you can send the POST request by using POSTMAN.
I think this part
#app.route("/")
def home_page():
access_token = session.get('access_token')
if access_token is None:
return redirect(url_for('login'))
Always force user to visit login page with GET method. Unfortunately you don't have method and route defined to handle this GET method.

Jupyterhub Custom Authenticator

I am a little stuck with writing a custom authenticator for jupyterhub. Most probably because I do not understand the inner workings of the available REMOTE_USER authenticator. I am not sure if it is applicable in my case... anyhow... this is what I'd like to do:
My general idea: I have a server that authenticates a user with his or her institutional login. After logging into the institution server/website, the users' data are encoded -- only some details to identify the user. They are then redirected to a the jupyterhub domain in the following way
https://<mydomain>/hub/login?data=<here go the encrypted data>
Now, if a request gets sent like this to my jupyterhub-domain, I'd like to decrypt the submitted data, and authenticate the user.
My trial:
I tried it with the following code. But it seems I am too nooby... :D
So please, pedantic comments are welcome :D
from tornado import gen
from jupyterhub.auth import Authenticator
class MyAuthenticator(Authenticator):
login_service = "my service"
authenticator_login_url="authentication url"
#gen.coroutine
def authenticate(self,handler,data=None):
# some verifications go here
# if data is verified the username is returned
My first problem... clicking the button on the login page, doesn't redirect me to my Authentication URL... it seems the variable authenticator_login_url from the login template is set somewhere else...
Second problem... a request made to .../hub/login?data=... is not evaluated by the authenticator (it seems...)
So: Has somebody any hints for me how to go about this?
As you see I followed the tutorials here:
https://universe-docs.readthedocs.io/en/latest/authenticators.html
So the following code does the job, however, I am always open to improvements.
So, what I did was redirect an empty login attempt to the login-url and deny access. If data is presented, check the validity of the data. If verified, user can login.
from tornado import gen, web
from jupyterhub.handlers import BaseHandler
from jupyterhub.auth import Authenticator
class MyAuthenticator(Authenticator):
login_service = "My Service"
#gen.coroutine
def authenticate(self,handler,data=None):
rawd = None
# If we receive no data we redirect to login page
while (rawd is None):
try:
rawd = handler.get_argument("data")
except:
handler.redirect("<The login URL>")
return None
# Do some verification and get the data here.
# Get the data from the parameters send to your hub from the login page, say username, access_token and email. Wrap everythin neatly in a dictionary and return it.
userdict = {"name": username}
userdict["auth_state"] = auth_state = {}
auth_state['access_token'] = verify
auth_state['email'] = email
#return the dictionary
return userdict
Simply add the file to the Python path, so that Jupyterhub is able to find it and make the necessary configurations in your jupyterhub_config.py file.

Using OAuth to authenticate Office 365/Graph users with Django

We are creating an application for use in our organization, but we only want people in our organization to be able to use the app. We had the idea of using Microsoft's OAuth endpoint in order to authenticate whether a user is part of our org or not. The idea is to bring up a sign in screen where the user can enter their Office 365 username and password, which will then allow them to use our app upon submission of their credentials.
Our app is running on Django, and I've only found a solution to this problem using Flask and Microsoft's Graph API connect sample for Python (See code snippet below). This sample uses a similar idea to the one above to log in to the app. Are there any similar methods of authentication for Django?
import requests
from flask import Flask, redirect, url_for, session, request, render_template
from flask_oauthlib.client import OAuth
# read private credentials from text file
client_id, client_secret, *_ = open('_PRIVATE.txt').read().split('\n')
if (client_id.startswith('*') and client_id.endswith('*')) or \
(client_secret.startswith('*') and client_secret.endswith('*')):
print('MISSING CONFIGURATION: the _PRIVATE.txt file needs to be edited ' + \
'to add client ID and secret.')
sys.exit(1)
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
# since this sample runs locally without HTTPS, disable InsecureRequestWarning
requests.packages.urllib3.disable_warnings()
msgraphapi = oauth.remote_app( \
'microsoft',
consumer_key=client_id,
consumer_secret=client_secret,
request_token_params={'scope': 'User.Read Mail.Send'},
base_url='https://graph.microsoft.com/v1.0/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://login.microsoftonline.com/common/oauth2/v2.0/token',
authorize_url='https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
)
#app.route('/login')
def login():
"""Handler for login route."""
guid = uuid.uuid4() # guid used to only accept initiated logins
session['state'] = guid
return msgraphapi.authorize(callback=url_for('authorized', _external=True), state=guid)
#app.route('/login/authorized')
def authorized():
"""Handler for login/authorized route."""
response = msgraphapi.authorized_response()
if response is None:
return "Access Denied: Reason={0}\nError={1}".format( \
request.args['error'], request.args['error_description'])
# Check response for state
if str(session['state']) != str(request.args['state']):
raise Exception('State has been messed with, end authentication')
session['state'] = '' # reset session state to prevent re-use
# Okay to store this in a local variable, encrypt if it's going to client
# machine or database. Treat as a password.
session['microsoft_token'] = (response['access_token'], '')
# Store the token in another session variable for easy access
session['access_token'] = response['access_token']
me_response = msgraphapi.get('me')
me_data = json.loads(json.dumps(me_response.data))
username = me_data['displayName']
email_address = me_data['userPrincipalName']
session['alias'] = username
session['userEmailAddress'] = email_address
return redirect('main')
You should be able to use just about any OAUTH 2.0 library for Python. I've not worked with Django but I know there are several out there for Python.
I came across django-azure-ad-auth which seems to be exactly what you're looking for.
I also found a general OAUTH library called django-allauth which seems to have a lot of activity. It doesn't have a built-in provider but the model they use for providers seems simple enough that you may be able to extend it without too much trouble.

In flask, how to route a link generated by OAuth 2?

I'm building a web app with flask. I used
#app.route('/')
def home():
.....
return render_template('home.html',url=url)
to generate the index page. There is a link(the second parameter, url) on the index page which leads to weibo (the chinese "twitter") for OAuth 2. After clicking the link and inputing the weibo username and password, I'm bounced back to a link like www.myflaskwebappaddress.com/?code=valueofcode.
My question is how can I catch the value of code and do something with the code in another page.
I tried the following:
#app.route(url_for('/', code=<tokencode>))
def show_data(tokencode):
.....want to use weibo api here using the tokencode.
But it doesn't work.
Everything after the ? is a query parameter; Flask parses those and provides them as part of the request.args object:
#app.route(url_for('/')
def home():
tokencode = request.args.get('code')
if tokencode is not None:
# there was a `?code=...` query parameter.
Also see The Request Object in the Flask quickstart.
Routing is only dependent on the path and the method of the request, the parameters don't play in that, so you cannot create a separate route for requests with that parameter.
You could, instead, use a before_request handler to look out for the code:
#app.before_request
def check_token():
tokencode = request.args.get('code')
if tokencode is not None:
# there was a `?code=...` query parameter.
If this function returns None, the normal routing takes place. But you can also return valid response; in that case that is the response returned to the end user and normal routing is skipped entirely.
This also means you can use any route on your site with a ?code=<tokencode> query parameter and have the before_request hook function handle it, not just your homepage.

Categories