Skip to content

Instantly share code, notes, and snippets.

@ties
Created September 7, 2021 10:07
Show Gist options
  • Save ties/b5f5572f2578b6b5b6c304fee019ad0a to your computer and use it in GitHub Desktop.
Save ties/b5f5572f2578b6b5b6c304fee019ad0a to your computer and use it in GitHub Desktop.
import argparse
import logging
import os
import requests
import sys
import urllib.parse
from typing import Optional
import pyotp
from lxml import etree
logging.basicConfig()
LOG = logging.getLogger(__name__)
def get_token(url: str,
username: str,
password: str,
otp_secret: Optional[str] = None):
# Get the initial form for SSO token
s = requests.Session()
res = s.get(url)
root = etree.HTML(res.text)
csrf = root.xpath('//input[@name="org.scalatra.CsrfTokenSupport.key"]')[0].get('value')
LOG.debug("cfrf token: %s", csrf)
auth_resp = s.post(
url,
headers={
"referer": url
},
data={
"org.scalatra.CsrfTokenSupport.key": csrf,
"username": username,
"password": password,
"originalUrl": url
},
allow_redirects=False)
if "Two-step verification" in auth_resp.text:
if not otp_secret:
raise ValueError("Two-factor verification was enabled for account but secret was not provided.")
root = etree.HTML(auth_resp.text)
csrf = root.xpath('//input[@name="org.scalatra.CsrfTokenSupport.key"]')[0].get('value')
# Try to create totp instance
totp = pyotp.TOTP(otp_secret)
totp_code = totp.now()
LOG.debug("sending totp code: %s", totp_code)
auth_resp = s.post(
urllib.parse.urljoin(url, "two-factor-authentication"),
headers={
"referer": auth_resp.url
},
data={
"org.scalatra.CsrfTokenSupport.key": csrf,
"securityCode": totp_code
},
allow_redirects=False)
# The HTTP 200 for 2fa is handled above. Both paths that arrive here have
# a HTTP 302 for success.
if auth_resp.status_code != 302:
raise ValueError("Authentication failed.")
print(f"crowd.token_key={auth_resp.cookies['crowd.token_key']}")
def main():
parser = argparse.ArgumentParser(
description="""Get a ripe.token cookie value.
Username is taken from RIPE_PORTAL_USERNAME env far.
Password is taken from RIPE_PORTAL_PASSWORD env var.""")
parser.add_argument('--url',
help='Login form URL (e.g. https://access.ripe.net/',
default='https://access.ripe.net/')
parser.add_argument('--username',
help='Username (default: from environment)',
default=os.environ.get('RIPE_PORTAL_USERNAME', ''))
parser.add_argument('--password',
help='Password (default: from environment)',
default=os.environ.get('RIPE_PORTAL_PASSWORD', ''))
parser.add_argument('--2fa-secret',
dest="otp_secret",
help='2FA secreet (default: from environment)',
default=os.environ.get('RIPE_PORTAL_2FA_SECRET', ''))
parser.add_argument('-v',
dest="verbose",
action="store_true")
args = parser.parse_args()
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
if not args.username or not args.password:
parser.print_help()
sys.exit(2)
else:
get_token(args.url, args.username, args.password, args.otp_secret)
if __name__ == "__main__":
main()
requests
beautifulsoup4
pyotp
lxml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment