mirror of
https://github.com/ihabunek/toot.git
synced 2024-09-22 04:25:55 -04:00
parent
3f44d560c8
commit
7886199295
11
README.rst
11
README.rst
@ -33,7 +33,8 @@ Running ``toot <command> -h`` shows the documentation for the given command.
|
||||
=================== ===============================================================
|
||||
Command Description
|
||||
=================== ===============================================================
|
||||
``toot login`` Log into a Mastodon instance, saves access keys for later use.
|
||||
``toot login`` Log into a Mastodon instance.
|
||||
``toot 2fa`` Log into a Mastodon instance using two factor authentication.
|
||||
``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``, you should instead run:
|
||||
|
||||
.. 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-Mastodon/List-of-Mastodon-instances.md
|
||||
|
||||
The application and user access tokens will be saved in two files in your home directory:
|
||||
|
||||
* ``~/.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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -2,17 +2,19 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
|
||||
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 tokens)")
|
||||
print(" toot login - log into a Mastodon instance")
|
||||
print(" toot 2fa - log into a Mastodon instance using 2FA (experimental)")
|
||||
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 factor authentication (experimental)",
|
||||
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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user