So I've got redis feature and tornado running on my server and whenever I open my websocket chat through a login, the terminal displays the following message
Error: Authentication missing
I'm not sure why this is happening because there are cookies in the authentication part of the app,
# Save user when authentication was successful.
def on_user_find(result, user=user):
##todo: We should check if email is given even though we can assume.
if result == "null" or not result:
# If user does not exist, create a new entry.
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
else:
# Update existing user.
# #todo: Should use $set to update only needed attributes?
dbuser = tornado.escape.json_decode(result)
dbuser.update(user)
user = dbuser
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
# Save user id in cookie.
self.set_secure_cookie("user", user["email"])
self.application.usernames[user["email"]] = user.get("name") or user["email"]
And in the websocket.py (where I run the script) I've made it so that the websocket handle checks if there are cookies available first before user access the app,
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
def open(self, user):
self.login = self.get_secure_cookie("user")
if not self.login:
# self.login = "anonymous"
print "Not authorized"
self.disconnect()
return
Yet it's still displaying the error, I've searched online and checked several SO answers but they don't show any solid solution in regards to this question. So far the most I've gotten is that I have to access the websocket header to put the above code inside, but I have no clue how I would do that. Help?
Related
I want to write a simple slack bot, which responds a given string to # mentions, however I am not able to make the official documentation code to work.
I gave all OAuth permission to the bot and have the following code:
from slack import RTMClient
#RTMClient.run_on(event="message")
def gravity_bot(**payload):
data = payload['data']
print(data.get('text'))
try:
rtm_client = RTMClient(
token="my_token_auth_code",
connect_method='rtm.start'
)
print("Bot is up and running!")
rtm_client.start()
except Exception as err:
print(err)
I think the connection is established, as the "Bot is up and running" message appears, however on the slack channel to bot seems to be offline, also I am not able to get any response in the terminal, not for direct messages, not for channel messages even after inviting the bot to given channels.
Sorry couldn't let this one go.. I figured it out and here are the steps:
Create a "Classic" app in Slack (this is the only way to get the appropriate scopes), just click this link: https://api.slack.com/apps?new_classic_app=1
From the "Add features and functionality" tab click on "bots":
Click the "Add Legacy Bot User" button (this will add the "rtm.stream" scope that you need, but that you cannot add manually)
From the basic information page, install your app in a workspace
From the OAuth & Permissions page, copy the "Bot User OAuth Access Token" (the bottom one)
Run the following code (slightly modified version of the code in the docs)
from slack_sdk.rtm import RTMClient
# This event runs when the connection is established and shows some connection info
#RTMClient.run_on(event="open")
def show_start(**payload):
print(payload)
#RTMClient.run_on(event="message")
def say_hello(**payload):
print(payload)
data = payload['data']
web_client = payload['web_client']
if 'Hello' in data['text']:
channel_id = data['channel']
thread_ts = data['ts']
user = data['user']
web_client.chat_postMessage(
channel=channel_id,
text=f"Hi <#{user}>!",
thread_ts=thread_ts
)
if __name__ == "__main__":
slack_token = "<YOUR TOKEN HERE>"
rtm_client = RTMClient(token=slack_token)
rtm_client.start()
Previous answer:
Hmm, this is tricky one... According to the docs this only works for "classic" Slack apps, so that might be the first pointer. It explicitly says that you should not upgrade your app. Furthermore, you'll need to set the right permissions (god knows which ones) by selecting the "bot" scope.
Honestly, I haven't been able to get this running. Looks like Slack is getting rid of this connection method, so you might have more luck looking into the "Events API". I know it's not the ideal solution because its not as real-time, but it looks better documented and it will stay around for a while. Another approach could be polling. Its not sexy but it works...
My guess is that your problem is that there is not a valid connection, but there is no proper error handling in the Slack library. The message is printed before you actually connect, so that doesn't indicate anything.
While working with Flask, Flask-Login and Flask-Session, I ran into the following problem:
When clicking on a link on an external site (cross-domain), that links to a route that's secured with #login_required, flask-logins _load_user-function is unable to retrieve the user information (since no cookie was sent with the cross-domain-request). (Without using the decorator, current_user.is_authenticated appears to be False.)
(from flask_login.login_manager.py)
if user is None:
config = current_app.config
cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME)
header_name = config.get('AUTH_HEADER_NAME', AUTH_HEADER_NAME)
has_cookie = (cookie_name in request.cookies and
session.get('_remember') != 'clear')
if has_cookie:
cookie = request.cookies[cookie_name]
user = self._load_user_from_remember_cookie(cookie)
elif self._request_callback:
user = self._load_user_from_request(request)
elif header_name in request.headers:
header = request.headers[header_name]
user = self._load_user_from_header(header)
In case the decorator was used, the request will be redirected to /?next=/protectedView. If just using current_user.is_authenticated, it will result False.
The session cookie gets re-created and overwritten every time I click on refresh. However, if I click in the URL bar and hit enter, the user loader is able to access the remember_cookie and load user data without creating a new session.
Is there any simple and convenient way to allow loading user data when entering a protected route from within another domain?
Update: I found out that setting REMEMBER_COOKIE_SECURE to None instead of Strict will cause flask-login to login the user. The link now redirects to the correct page.
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.
I just got my authentication credentials for Instagram and I've been trying to use the Python wrapper, but I can't get basic queries to work. For example, after loading my client ID, secret and access token:
api = InstagramAPI(client_id=client_id, client_secret=client_secret, access_token=access_token)
print('got api %s'%(api))
user_name = 'natgeo'
search_result = api.user_search(q=user_name)
print('username %s gets search result %s'%
(user_name, search_result))
returns
username natgeo gets search result []
which shouldn't be the case, since natgeo is a valid Instagram account. However, searching for my username works fine for some reason. Any idea what I'm doing wrong?
It's likely that you're in sandbox mode. This means that you can only access the first 20 items of your own profile. Move your app out of sandbox mode to have full access to the API.
I am using python social auth with django app. In email validation, the link received on the mail works fine in the same browser from which authentication was initiated but it show Missing needed parameter state in different browser.Did anybody fix this issue ?
The issue is discussed here Issue #577
This is because there's no partial pipeline data in other browser!
Christopher Keefer has worked on monkey-patch for this by fetching the session_key for Django session table. There's also a blog article here on this , refer Step 3 of this article.
# Monkey patching - an occasionally necessary evil.
from social import utils
from social.exceptions import InvalidEmail
from django.core import signing
from django.core.signing import BadSignature
from django.contrib.sessions.models import Session
from django.conf import settings
def partial_pipeline_data(backend, user=None, *args, **kwargs):
"""
Monkey-patch utils.partial_pipeline_data to enable us to retrieve session data by signature key in request.
This is necessary to allow users to follow a link in an email to validate their account from a different
browser than the one they were using to sign up for the account, or after they've closed/re-opened said
browser and potentially flushed their cookies. By adding the session key to a signed base64 encoded signature
on the email request, we can retrieve the necessary details from our Django session table.
We fetch only the needed details to complete the pipeline authorization process from the session, to prevent
nefarious use.
"""
data = backend.strategy.request_data()
if 'signature' in data:
try:
signed_details = signing.loads(data['signature'], key=settings.EMAIL_SECRET_KEY)
session = Session.objects.get(pk=signed_details['session_key'])
except BadSignature, Session.DoesNotExist:
raise InvalidEmail(backend)
session_details = session.get_decoded()
backend.strategy.session_set('email_validation_address', session_details['email_validation_address'])
backend.strategy.session_set('next', session_details.get('next'))
backend.strategy.session_set('partial_pipeline', session_details['partial_pipeline'])
backend.strategy.session_set(backend.name + '_state', session_details.get(backend.name + '_state'))
backend.strategy.session_set(backend.name + 'unauthorized_token_name',
session_details.get(backend.name + 'unauthorized_token_name'))
partial = backend.strategy.session_get('partial_pipeline', None)
if partial:
idx, backend_name, xargs, xkwargs = \
backend.strategy.partial_from_session(partial)
if backend_name == backend.name:
kwargs.setdefault('pipeline_index', idx)
if user: # don't update user if it's None
kwargs.setdefault('user', user)
kwargs.setdefault('request', backend.strategy.request_data())
xkwargs.update(kwargs)
return xargs, xkwargs
else:
backend.strategy.clean_partial_pipeline()
utils.partial_pipeline_data = partial_pipeline_data
This fixes the problem to much extent, still its not perfect. It will fail if session_key gets deleted/changed in the database. Django updates session_key each time the session data changes. So in case any other user logs in the same browser the session_key gets changed and user can't verify with the email link.
Omab has mentioned in the comment on issue,
I see the problem now, and even if I think that this could be solved with a re-write of the email validation pipeline, this affects all the pipeline functions that use the partial mechanism, so, I'm already working on a restructure of the pipeline serialization functionality that will improve this behavior. Basically the pipeline data will be dumped to a DB table and a hash code will be used to identify the processes which can be stopped and continue later, removing the dependency of the session.
Looking for update on this.