So I hit a login route(python) from my frontend(react) that returns a auth_url to login using oauth. Then it needs to wait for the user to sign on and redirect back to frontend. Atm I can't figure out how to do both.
Either my frontend fetches the url, then window.location.replace(url), you sign on, then since the login route is only sending the url, there is no redirect back to frontend. Once logged on it just shows backend with a page showing auth_url
Or my frontend just makes the call to log on, and the backend redirects to auth_url without a way to redirect after completion to the frontend.
I can’t find any examples online of redirecting twice so it seems my logic is wrong. Is putting the url in a header the thing people do here?
My attempt:
async def test(url):
#redirect to oauth to sign in
await url
return redirect(url)
async def test2(testfinish):
# after oauth is complete, redirect to my frontend
await testfinish
return redirect('http://localhost:3000/')
#app.route("/login")
def login():
session["flow"] = _build_auth_code_flow(scopes=SCOPE)
auth_url=session["flow"]["auth_uri"]
return test2(test(auth_url))
Related
I'm stuck in a loop, I have a web app, the login and signup was made using Flask on the Backend and Jinja for templating, no JS was used for these two routes, I used WTForms for form validation and Flask Login to handle login .
Now, when user login he is redirected to a dashboard, the dashboard is a single page application that use React, I send HTTP request to my API which is built in Flask (same app, but different blueprints).
The problem is that these APIs routes are avalable only for authenticated users, so I used a token system to authenticate API calls from the client to the server .
To do so, I created a request loader for my login manager, that using a token parameter in the query would decode it , get the email and password and return the respective User, otherwise if it fail it returns None
#login_manager.request_loader
def load_user(request):
if "token" in request.args:
decoded_token = base64.standard_b64decode(request.args["token"])
email = decoded_token.split(b':',1)[0]
password = decoded_token.split(b':',1)[1]
possible_user = User.select().where(User.email == email)[0]
if possible_user.password.encode() == password:
return possible_user
else:
return None
else:
return None
return None
This token is sent to the user when he login and get redirected to the dashboard :
User login successfULY
Get redirected to dashboard
Dashboard make an ajax call to "/user-data" , which is protected, this route should use current_user to encode the token and send it back to dashboard single page app to use it in further API calls .
The problem:
When I request the "/user-data" through AJAX from dashboard, current_user is empty thus, the call return a 401 unauthorized request even though the user did login, and in the login route, when I print current_user I get the current user logged in as expected . So my question is how can I keep some way to exhcange credentials between login and "user-data" route ? I tried storing the data in a session in the login route then re-use it in the '/user-data' but the session becomes empty whenever 'user-data' is called .
Here's 'user-data' route :
#auth_bp.route("/user-data")
#login_required
def user_data():
# Return Base 64 encode username:password to use in API calls!
print("Current user")
print(current_user)
print(session)
code = base64.standard_b64encode(current_user.email.encode() + b':' + current_user.password.encode())
print(base64.standard_b64decode(code))
return code
I have been following this Flask pyjwt guide, however my web app is somewhat similar to Miguel's microblog example that uses render_template() and redirect(url_for(...)) for navigation.
I have implemented an encoding and decoding service in my application, however I do not know how to properly return the encoded JWT token to the user using redirect()
My code is as follows:
#app.route('/', methods=['GET', 'POST'])
def login():
login_form = LoginForm()
username = login_form.username.data
password = login_form.password.data
if is_user_valid(username, password):
return redirect(url_for('home'), auth_service.encode_token(username))
render_template('login.html', form=login_form)
My problem is placing the auth token inside the redirect method causes a page to appear saying "Redirecting... you should be redirected, if not click here", which I do not want. I do not particularly wish to change my redirect methods to something similar to make_response(jsonify(...)) as I would then need to handle these responses in the front end when this is a simple login page.
How should I be returning the auth token to the client correctly?
Typically you attach it to response headers which is what your redirect method returns.
response = redirect(url_for('home'))
response.headers['X-JWT-TOKEN'] = auth_service.encode_token(username)
return response
However, I'm not sure if this is gonna be very useful in your setup when using render_template, because you can not easily access these headers on the frontend.
You have two alternatives here:
Make your login endpoint an API. This way you can return the token for the client-side code to handle and store it somehow.
Drop the JWT and stick to using sessions. I know that this is not what you expected, but outside of building APIs JWT-based auth is not very helpful.
Maybe a stupid question here:
Is Requests(A python HTTP lib) support Django 1.4 ?
I use Requests follow the Official Quick Start like below:
requests.get('http://127.0.0.1:8000/getAllTracks', auth=('myUser', 'myPass'))
but i never get authentication right.(Of course i've checked the url, username, password again and again.)
The above url 'http://127.0.0.1:8000/getAllTracks' matches an url pattern of the url.py of a Django project, and that url pattern's callback is the 'getAllTracks' view of a Django app.
If i comment out the authentication code of the 'getAllTracks' view, then the above code works OK, but if i add those authentication code back for the view, then the above code never get authenticated right.
The authentication code of the view is actually very simple, exactly like below (The second line):
def getAllTracks(request):
if request.user.is_authenticated():
tracks = Tracks.objects.all()
if tracks:
# Do sth. here
Which means if i delete the above second line(with some indents adjustments of course), then the requests.get() operation do the right thing for me, but if not(keep the second line), then it never get it right.
Any help would be appreciated.
In Django authentication works in following way:
There is a SessionMiddleware and AuthenticationMiddleware. The process_request() of both these classes is called before any view is called.
SessionMiddleware uses cookies at a lower level. It checks for a cookie named sessionid and try to associate this cookie with a user.
AuthenticationMiddleware checks if this cookie is associated with an user then sets request.user as that corresponding user. If the cookie sessionid is not found or can't be associated with any user, then request.user is set to an instance of AnonymousUser().
Since Http is a stateless protocol, django maintains session for a particular user using these two middlewares and using cookies at a lower level.
Coming to the code, so that requests can work with django.
You must first call the view where you authenticate and login the user. The response from this view will contain sessionid in cookies.
You should use this cookie and send it in the next request so that django can authenticate this particular user and so that your request.user.is_authenticated() passes.
from django.contrib.auth import authenticate, login
def login_user(request):
user = authenticate(username=request.POST.get('username'), password=request.POST.get('password'))
if user:
login(request, user)
return HttpResponse("Logged In")
return HttpResponse("Not Logged In")
def getAllTracks(request):
if request.user.is_authenticated():
return HttpResponse("Authenticated user")
return HttpResponse("Non Authenticated user")
Making the requests:
import requests
resp = requests.post('http://127.0.0.1:8000/login/', {'username': 'akshar', 'password': 'abc'})
print resp.status_code
200 #output
print resp.content
'Logged In' #output
cookies = dict(sessionid=resp.cookies.get('sessionid'))
print cookies
{'sessionid': '1fe38ea7b22b4d4f8d1b391e1ea816c0'} #output
response_two = requests.get('http://127.0.0.1:8000/getAllTracks/', cookies=cookies)
Notice that we pass cookies using cookies keyword argument
print response_two.status_code
200 #output
print response_two.content
'Authenticated user' #output
So, our request.user.is_authenticated() worked properly.
response_three = requests.get('http://127.0.0.1:8000/hogwarts/getAllTracks/')
Notice we do not pass the cookies here.
print response_three.content
'Non Authenticated user' #output
I guess, auth keyword for Requests enables HTTP Basic authentication which is not what is used in Django. You should make a POST request to login url of your project with username and password provided in POST data, after that your Requests instance will receive a session cookie with saved authentication data and will be able to do successful requests to auth-protected views.
Might be easier for you to just set a cookie on initial authentication, pass that back to the client, and then for future requests expect the client to send back that token in the headers, like so:
r = requests.post('http://127.0.0.1:8000', auth=(UN, PW))
self.token = r.cookies['token']
self.headers = {'token': token}
and then in further calls you could, assuming you're in the same class, just do:
r = requests.post('http://127.0.0.1:8000/getAllTracks', headers=self.headers)
Hi I added reCAPTCHA to a page so far that it can verify a client. Now I want a more graceful failure when isHuman = Falsewith a redirect to the form page in such case with a message on the form page that reCAPTCHA failed. Can you inform what to do rather than just outputting "captcha failed?" so that it can redirect to the form page with Google App Engine's request handler? Here's my code:
def post(self, view): #edit
challenge = self.request.get('recaptcha_challenge_field')
response = self.request.get('recaptcha_response_field')
remoteip = os.environ['REMOTE_ADDR']
cResponse = captcha.submit(
challenge,
response,
CAPTCHA_PRV_KEY,
remoteip)
if cResponse.is_valid==True:
isHuman=True
else:
isHuman=False
self.response.out.write('captcha failed')
return
If you are just looking for a way to redirect back to your original posting page you can use:
self.redirect('your_url')
You could also include a GET variable in the url so that the page is aware that the CAPTCHA failed.
from bottle import route, run, debug, error, request, template
#route('/home')
#route('/home/')
def login():
return template('templates/login')
#route('/home', method='POST')
#route('/home/', method='POST')
def welocme():
data = request.POST
if data:
password = data.get('password')
check_pass = 'password'
if password == check_pass:
return template('templates/welcome')
else:
return template('templates/login')
else:
return template('templates/login')
My requirement is:
I will get a login and welcome page on same url. Login page has only one password field.
My problem:
If I get login and go to welcome page again on refreshing, it send me to login page. But ideally it should be on the welcome page only.
#error(404)
def error404(error):
return 'http://google.com'
My second problem: I want to redirect on a particular url on 404.
If the user goes to the "/home" page, don't you want to check to see if they're logged in before showing them the login screen? It appears like you assume they're not already logged in if the HTTP method is not POST.
I don't know much about your framework, but I assume if the login is successful you should set a cookie, and then you should check the cookie for HTTP GETs to see if the user is authenticated.
Your second question is answered here.
You might also want to take a look at Beaker's cookie sessions and use that to keep your application's state between requests.
If I understand the question correctly, the only way to get to render the welcome template is via POST.
You could change this so that GET requests check whether someone is logged in. If that fails, then redirect them to the login page.