I using flask-login for authorizing in my site.
If I login once, flask do not offer login again, but I want users to be logged in every time they log on to the site.
P.S.: I try to use remember=False in login_user() function, but it didn't help.
What can be the correction that need to be done?
I now see case for fresh loging like password change etc It is provided for by the use of fresh_login_required
flask_login.login_fresh()
This returns True if the current login is fresh. So your views protected with login required you can do something like
if not login_fresh():
#redirect to your login page with a request to reauthenticate
or (and am using change-password just as an example you can use it on any and all views that require fresh login everytime)
from flask.ext.login import fresh_login_required
#app.route("/change-password")
#fresh_login_required
def change_password():
#do stuff here
If the user is not authenticated, LoginManager.unauthorized() is called as normal. If they are authenticated, but their session is not fresh, it will call LoginManager.needs_refresh() instead. (In that case, you will need to provide a LoginManager.refresh_view.) which you can do as below as per the docs
login_manager.refresh_view = "accounts.reauthenticate"
login_manager.needs_refresh_message = (
u"To protect your account, please reauthenticate to access this page."
)
login_manager.needs_refresh_message_category = "info"
If what you are looking at is logging someone out after lets say 5 minutes of inactivity for which this question and this question gives you a very good answer, so you would do it like this
from datetime import timedelta
from flask import session, app
#make the session permanent and set expiry period
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)
#everytime a user visits, modify the session so that you know they are still active
#app.before_request
def func():
session.modified = True
You can make the lifetime very small for a start for testing purposes
Related
I started to use SQLModel that is created by the same person as FastAPI, but I cannot seem to find how to combine authentication/authorization to get the logged-in user and then get current user from that with SQLModel.
I can authenticate a user by just checking it against a database as below, but then how can I keep this session alive so that I can do other stuff with it such as get current user?
I am purely testing with localhost:8000/docs, so I thought maybe I need to create some Jinja2 templates to test it out in a browser, but not sure?
def login_test(input_email: str, input_password: str, session: Session = Depends(get_session)):
# Check if user exists or not
if not check_user_exists(input_email=input_email):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail="User does not exist, please create an account")
# Check if password is ok
if not check_hashed_password(input_password, session.query(User).filter(User.email == input_email).first().password):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail="Password is incorrect")
# Login based on email and password if both ok
return {"Message": "Login successful"}```
I'm trying to download some files from behind a SSO (Single Sign-On) site. It seems to be SAML authenticated, that's where I'm stuck. Once authenticated I'll be able to perform API requests that return JSON, so no need to interpret/scrape.
Not really sure how to deal with that in mechanicalsoup (and relatively unfamiliar with web-programming in general), help would be much appreciated.
Here's what I've got so far:
import mechanicalsoup
from getpass import getpass
import json
login_url = ...
br = mechanicalsoup.StatefulBrowser()
response = br.open(login_url)
if verbose: print(response)
# provide the username + password
br.select_form('form[id="loginForm"]')
print(br.get_current_form().print_summary()) # Just to see what's there.
br['UserName'] = input('Email: ')
br['Password'] = getpass()
response = br.submit_selected().text
if verbose: print(response)
At this point I get a page telling me javascript is disabled and that I must click submit to continue. So I do:
br.select_form()
response = br.submit_selected().text
if verbose: print(response)
That's where I get a complaint about state information being lost.
Output:
<h2>State information lost</h2>
State information lost, and no way to restart the request<h3>Suggestions for resolving this problem:</h3><ul><li>Go back to the previous page and try again.</li><li>Close the web browser, and try again.</li></ul><h3>This error may be caused by:</h3><ul><li>Using the back and forward buttons in the web browser.</li><li>Opened the web browser with tabs saved from the previous session.</li><li>Cookies may be disabled in the web browser.</li></ul>
The only hits I've found on scraping behind SAML logins are all going with a selenium approach (and sometimes dropping down to requests).
Is this possible with mechanicalsoup?
My situation turned out to require Javascript for login. My original question about getting into SAML auth was not the true environment. So this question has not truly been answered.
Thanks to #Daniel Hemberger for helping me figure that out in the comments.
In this situation MechanicalSoup is not the correct tool (due to Javascript) and I ended up using selenium to get through authenication then using requests.
My goal is to log-in to Gmail, serialize the cookies, exit the browser, then open a new browser, load the saved cookies, and check my email without needing to enter my log-in details. Pretty straight-forward, and I've been able to do this with almost every website I've tried. However, Gmail forces me to sign in again, each time.
Here's my code:
from splinter import Browser
import selenium
import pickle
def export_cookies(browser, the_name):
yummy = browser.cookies.all(verbose=True)
location = 'cookies/' + the_name
pickle_save(yummy, location)
print(the_name, "saved", len(yummy))
def pickle_save(obj, location):
file_name = location
file_object = open(file_name, 'wb')
pickle.dump(obj, file_object)
file_object.close()
def pickle_load_account(cookie_file_name):
try:
return pickle.load(open(cookie_file_name, "rb"))
except FileNotFoundError:
return 0
def browser_add_cookies(browser, cookies):
if len(cookies) > 0:
for cookie in cookies:
browser.cookies.add({cookie['name']: cookie['value']})
print("-----", len(cookies), " cookies added, reloading")
browser.visit('https://mail.google.com/mail/u/0/#inbox')
else:
print("No cookies to load. Error.")
browser = Browser('firefox')
browser.visit('https://mail.google.com/mail/u/0/#inbox')
cookie_file = "cookies/name"
load_cookies = pickle_load_account(cookie_file)
browser_add_cookies(browser, load_cookies)
browser.visit('https://mail.google.com/mail/u/0/#inbox')
input("Save cookies?")
export_cookies(browser, "name")
This code assumes the cookies were already saved, but then re-saves them in the end, so a second run (if you sign in manually the first time).
My guess is that Gmail somehow uses a more advanced method of cookie recognition?
You are replicating cookies on for one domain. You need to replicate for below domains as well
accounts.google.com
mail.google.com
And may be even more, see the screenshot below on fresh login
Example code for a browser wrapper to make set cookies easier:
class SplinterBrowserWrapper:
def __init__(self, splinter_browser):
b = self.browser = splinter_browser
b.visit('https://' + ROOT_DOMAIN)
def add_cookies(self, cookies: dict):
self.browser.cookies.add(cookies)
for cookie in self.list_cookies():
cookie['domain'] = ROOT_DOMAIN
self.add_single_cookie_dict(cookie)
self.browser.reload()
def add_single_cookie_dict(self, single_cookie_dict):
self.browser.driver.add_cookie(single_cookie_dict)
def get_cookies(self):
return self.browser.cookies.all()
def list_cookies(self):
return self.browser.driver.get_cookies()
routine:
visit mail.google.com
mail.google.com ask accounts.google.com: this guy login or not?
accounts answer: NO!
mail: OK, just another wandering guest...
guest is not happy, tries to set cookies via splinter:
visit mail.google.com and set cookies
cookies are set, but bound with sub-domain mail.google.com (run browser.driver.get_cookies() and check the returned list)
sub-domain accounts.google.com still no cookies
mail ask accounts the same question and get the same NO answer
emmm...
guest knows the trick now, guest tries again:
visit accounts.google.com and set cookies
visit mail.google.com
mail ask accounts, get answer YES
mail login
N~I~C~E~
another routine, another website called bilibili:
visit www.bilibili.com
www ask account.bilibili.com
account has no logged in cookies, account redirect browser to passport.bilibili.com
passport ask user for username & password
passport get correct auth, set cookies, and redirect back to home page as login
splinter chanllege bilibili, round 1:
visit www.bilibili.com, set cookies, reload page, not login
visit account.bilibili.com, be redirected to passport.bilibili.com
set cookies on passport
but passport not want cookies, passport NEED username & password
passport stay put, refuse to redirect to account or www
account still no cookies
visit account again, redirected to passport again, dead loop
splinter challenge round 2:
visit www.bilibili.com, set cookies, cookies are bound with www.bilibili.com
dig into underlying selenium driver, list all cookies with browser.driver.get_cookies(), every single cookie in this list is a dict, with keys name, value, path, domain, secure, httpOnly
iterate this cookies list, for every cookie dict, change value of domain to the parent domain bilibili.com, with no sub-domain
set these manipulated cookies back into selenium drive one by one, via browser.driver.add_cookie
now account see these cookies bound with its parent domain bilibili.com
account is obedient, account accpet these cookies, account now answer YES when anyone ask if user is login
reload the page and user login
I am working with the Spotipy Python library to connect to the Spotify web API. I want to get access to my Spotify's user account via Authorization Code Flow. I am using Python 3.5, Spotipy 2.4.4, Google Chrome 55.0.2883.95 (64-bit) and Mac OS Sierra 10.12.2
First, I went to the Spotify Developer website to register the program to get a Client ID, a Client Secret key and enter a redirect URI (https://www.google.com) on my white-list.
Second, I set the environment variables from terminal:
export SPOTIPY_CLIENT_ID='my_spotify_client_id'
export SPOTIPY_CLIENT_SECRET='my_spotify_client_secret'
export SPOTIPY_REDIRECT_URI='https://www.google.com'
Then I try to run the example code typing 'python3.5 script.py my_username' from terminal. Here is the script:
import sys
import spotipy
import spotipy.util as util
scope = 'user-library-read'
if len(sys.argv) > 1:
username = sys.argv[1]
else:
print "Usage: %s username" % (sys.argv[0],)
sys.exit()
token = util.prompt_for_user_token(username, scope)
if token:
sp = spotipy.Spotify(auth=token)
results = sp.current_user_saved_tracks()
for item in results['items']:
track = item['track']
print track['name'] + ' - ' + track['artists'][0]['name']
else:
print "Can't get token for", username
When running this code, it takes me to a log in screen on my browser. I enter my Spotify's credential to grant access to my app. But when I finally click on 'log in' (or 'Iniciar sesiĆ³n' in Spanish) nothing happens. I tried to log in with my Facebook account but it does not work either. It seems that I get a Bad Request every time that I click on 'log in'. Here is a capture of Chrome:
The process is incomplete because when trying to enter the redirect URI back on terminal I get another Bad Request:
raise SpotifyOauthError(response.reason)
spotipy.oauth2.SpotifyOauthError: Bad Request
I tried to restart my computer, clean my browser cookies, use another different browser but did not work. It seems that I am not the only one having this problem. Is it maybe a bug? Please, avoid answers like "read the documentation of the API going here". Thank you.
Solved it. In order to run the Authorization Code Flow example code provided in the Spotipy's documentation correctly, I specified the redirect URI in line 13 of the example script when calling util.prompt_for_user_token, even when I had done this previously when setting environment variables:
token = util.prompt_for_user_token(username, scope, redirect_uri = 'https://example.com/callback/')
Likewise, do not use https://www.google.com or similar web address as your redirect URI. Instead, try 'https://example.com/callback/' or 'http://localhost/' as suggested here. Do not forget that the redirected URL once you are logged in must have the word code included.
Cheers,
I'm using the Acegi plugin in my Grails app. After a user registers, he is redirected to an action that is protected. Therefore, he is shown the login form.
However, I would prefer if the user was automatically logged in as soon as he completes registration. It seems like I could achieve this by redirecting to the action that the login form uses
redirect(uri:"/j_acegi_security_check?j_username=${username}&j_password=${passed}")
But this would send a HTTP request to the client (and back to the server) which shows the user's password. Is there a way I can login automatically in a secure fashion?
Thanks,
Don
If you generate the controller classes for the spring security plugin (grails generate-registration) you'll see the following lines in RegisterController which does just what you want:
class RegisterController {
def daoAuthenticationProvider
...
def save = {
...
def auth = new AuthToken(person.username, params.passwd)
def authtoken = daoAuthenticationProvider.authenticate(auth)
SecurityContextHolder.context.authentication = authtoken
redirect uri: '/'
}
Be sure that params.passwd is the plain-text password (i.e. not hashed) and it works like a charm.
I haven't tried this with non-test code, but this is the method that I created to log a user in within my integration tests (after building/saving the appropriate users/roles in my test setup):
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoAuthenticationProvider
import org.springframework.security.providers.UsernamePasswordAuthenticationToken
import org.springframework.security.context.SecurityContextHolder
...
def logInAsUser(username, password) {
daoAuthenticationProvider.getUserCache().removeUserFromCache(username)
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password)
SecurityContextHolder.context.authentication = daoAuthenticationProvider.authenticate(token)
}
I construct and inject the authentication token in the security context. There might be a little more that you need to do to get your user logged in and past security, but this would be the start of whatever that is.
I'll actually need to do exactly what you're asking in a week or two for my current app, post back if you figure it out fully before I do :).
This is Burt Beckwith's answer (not mine)
(It was left as a comment by Burt, but I think it deserves to be more prominent)
If you don't have the password, you can load the user via
def user = User.findByUsername(username)
and setting the authority array in the 3-parameter constructor. Create the auths via
GrantedAuthority[] auths = user.authorities.collect { new GrantedAuthorityImpl(it.authority) }
Then you can omit the call to authenticate() and use:
SecurityContextHolder.context.authentication = new UsernamePasswordAuthenticationToken(username, 'unknown', auths)