Introduction
Introduction Statistics Contact Development Disclaimer Help
Experimental two factor authentication support - toot - Unnamed repository; edi…
Log
Files
Refs
LICENSE
---
commit 7886199295bda15965999daa736a5e8e165e01d6
parent 3f44d560c8c1159df2cf318cc71a9d235fde6f55
Author: Ivan Habunek <[email protected]>
Date: Tue, 18 Apr 2017 16:40:26 +0200
Experimental two factor authentication support
issue #3
Diffstat:
README.rst | 11 +++++++++--
toot/api.py | 2 +-
toot/console.py | 83 +++++++++++++++++++++++++++++--
3 files changed, 90 insertions(+), 6 deletions(-)
---
diff --git a/README.rst b/README.rst
@@ -33,7 +33,8 @@ Running ``toot <command> -h`` shows the documentation for the…
=================== =========================================================…
Command Description
=================== =========================================================…
- ``toot login`` Log into a Mastodon instance, saves access keys for late…
+ ``toot login`` Log into a Mastodon instance.
+ ``toot 2fa`` Log into a Mastodon instance using two factor authentica…
``toot logout`` Log out, deletes stored access keys.
``toot auth`` Display stored authenitication tokens.
``toot whoami`` Display logged in user details.
@@ -53,13 +54,19 @@ Before tooting, you need to login to a Mastodon instance:
toot login
+**Two factor authentication** is supported experimentally, instead of ``login`…
+
+.. code-block::
+
+ toot 2fa
+
You will be asked to chose an instance_ and enter your credentials.
.. _instance: https://github.com/tootsuite/documentation/blob/master/Using-Mas…
The application and user access tokens will be saved in two files in your home…
-* ``~/.config/toot/app.cfg``
+* ``~/.config/toot/instances/<name>`` - created for each mastodon instance once
* ``~/.config/toot/user.cfg``
You can check whether you are currently logged in:
diff --git a/toot/api.py b/toot/api.py
@@ -118,7 +118,7 @@ def login(app, username, password):
# If auth fails, it redirects to the login page
if response.is_redirect:
- raise AuthenticationError("Login failed")
+ raise AuthenticationError()
return _process_response(response)
diff --git a/toot/console.py b/toot/console.py
@@ -2,17 +2,19 @@
from __future__ import unicode_literals
from __future__ import print_function
+import json
+import logging
import os
+import requests
import sys
-import logging
+from argparse import ArgumentParser, FileType
from bs4 import BeautifulSoup
from builtins import input
from datetime import datetime
from future.moves.itertools import zip_longest
from getpass import getpass
from itertools import chain
-from argparse import ArgumentParser, FileType
from textwrap import TextWrapper
from toot import api, config, DEFAULT_INSTANCE, User, App
@@ -89,11 +91,65 @@ def login_interactive(app):
return user
+def two_factor_login_interactive(app):
+ """Hacky implementation of two factor authentication"""
+
+ print("Log in to " + green(app.instance))
+ email = input('Email: ')
+ password = getpass('Password: ')
+
+ sign_in_url = app.base_url + '/auth/sign_in'
+
+ session = requests.Session()
+
+ # Fetch sign in form
+ response = session.get(sign_in_url)
+ response.raise_for_status()
+
+ soup = BeautifulSoup(response.content, "html.parser")
+ form = soup.find('form')
+ inputs = form.find_all('input')
+
+ data = {i.attrs.get('name'): i.attrs.get('value') for i in inputs}
+ data['user[email]'] = email
+ data['user[password]'] = password
+
+ # Submit form, get 2FA entry form
+ response = session.post(sign_in_url, data)
+ response.raise_for_status()
+
+ soup = BeautifulSoup(response.content, "html.parser")
+ form = soup.find('form')
+ inputs = form.find_all('input')
+
+ data = {i.attrs.get('name'): i.attrs.get('value') for i in inputs}
+ data['user[otp_attempt]'] = input("2FA Token: ")
+
+ # Submit token
+ response = session.post(sign_in_url, data)
+ response.raise_for_status()
+
+ # Extract access token from response
+ soup = BeautifulSoup(response.content, "html.parser")
+ initial_state = soup.find('script', id='initial-state')
+
+ if not initial_state:
+ raise ConsoleError("Login failed: Invalid 2FA token?")
+
+ data = json.loads(initial_state.get_text())
+ access_token = data['meta']['access_token']
+
+ user = User(app.instance, email, access_token)
+ path = config.save_user(user)
+ print("Access token saved to: " + green(path))
+
+
def print_usage():
print("toot - interact with Mastodon from the command line")
print("")
print("Usage:")
- print(" toot login - log into a Mastodon instance (stores access tok…
+ print(" toot login - log into a Mastodon instance")
+ print(" toot 2fa - log into a Mastodon instance using 2FA (experim…
print(" toot logout - log out (delete stored access tokens)")
print(" toot auth - display stored authentication tokens")
print(" toot whoami - display logged in user details")
@@ -221,6 +277,24 @@ def cmd_login(args):
return app, user
+def cmd_2fa(args):
+ parser = ArgumentParser(prog="toot 2fa",
+ description="Log into a Mastodon instance using 2 …
+ epilog="https://github.com/ihabunek/toot")
+ parser.parse_args(args)
+
+ print()
+ print(yellow("Two factor authentication is experimental."))
+ print(yellow("If you have problems logging in, please open an issue:"))
+ print(yellow("https://github.com/ihabunek/toot/issues"))
+ print()
+
+ app = create_app_interactive()
+ user = two_factor_login_interactive(app)
+
+ return app, user
+
+
def cmd_logout(app, user, args):
parser = ArgumentParser(prog="toot logout",
description="Log out, delete stored access keys",
@@ -363,6 +437,9 @@ def run_command(command, args):
if command == 'login':
return cmd_login(args)
+ if command == '2fa':
+ return cmd_2fa(args)
+
if command == 'auth':
return cmd_auth(app, user, args)
You are viewing proxied material from vernunftzentrum.de. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.