Something went wrong on our end
-
magnus2142 authoredmagnus2142 authored
login.py 8.62 KiB
from urllib import request
import os
from dotenv import load_dotenv
from RequestData import RequestData
import requests
import json
from bs4 import BeautifulSoup
import time
from getpass import getpass
ctx = ''
session_id = ''
canary = ''
auth_method_id = ''
hpg_request_id = ''
flowtoken = ''
login_username = ''
otc = None
def login():
print("Logging in...")
# TODO: Let user choose between feide log in or ID-gate
feide_login()
# -------------------- FEIDE LOGIN PART -------------------- #
def feide_login():
session = requests.Session()
request_data = RequestData()
scrape_login_page(session, request_data)
response = post_login_request(session, request_data, get_credentials())
scrape_login_response(response, request_data)
response = post_adfs_request(session, request_data)
scrape_adfs_response(response, request_data)
response = post_microsoft_login(session, request_data)
scrape_microsoft_login_response(response, request_data)
begin_auth(session, request_data)
response = process_auth(session, request_data, otc)
scrape_process_auth(response, request_data)
post_auth_saml_SSO(session, request_data)
write_to_env_data(session)
print('Login successful!')
def get_credentials():
# TODO: Give user an option to save their username and password
# so they don't have to write their login everytime they want to login
username = getpass("Username: ")
password = getpass()
credentials = {
'username': username,
'password': password
}
return credentials
def scrape_login_page(session, request_data):
html = session.get('http://innsida.ntnu.no/blackboard')
soup = BeautifulSoup(html.text, 'lxml')
auth_state = soup.find('input', {'name': 'AuthState'})['value']
request_data.params = (
('org', 'ntnu.no'),
('AuthState', auth_state)
)
def post_login_request(session, request_data, credentials):
request_data.data = {
'feidename': credentials['username'],
'password': credentials['password']
}
global login_username
login_username = credentials['username']
return session.post('https://idp.feide.no/simplesaml/module.php/feide/login', params=request_data.params, data=request_data.data)
def scrape_login_response(response, request_data):
soup = BeautifulSoup(response.text, 'lxml')
try:
samlResponse = soup.find(
'input', {'name': 'SAMLResponse'})['value']
relayState = soup.find(
'input', {'name': 'RelayState'})['value']
except TypeError:
raise TypeError("Username or password is wrong...")
else:
request_data.data = {
'SAMLResponse': samlResponse,
'RelayState': relayState
}
def post_adfs_request(session, request_data):
return session.post('https://adfs.ntnu.no/adfs/ls/', data=request_data.data)
def scrape_adfs_response(response, request_data):
soup = BeautifulSoup(response.text, 'lxml')
wa = soup.find('input', {'name': 'wa'})['value']
wresult = soup.find('input', {'name': 'wresult'})['value']
wctx = soup.find('input', {'name': 'wctx'})['value']
request_data.data = {
'wa': wa,
'wresult': wresult,
'wctx': wctx
}
def post_microsoft_login(session, request_data):
return session.post(
'https://login.microsoftonline.com/login.srf', data=request_data.data)
def scrape_microsoft_login_response(response, request_data):
soup = BeautifulSoup(response.text, 'lxml')
script_tag = soup.find('script')
# Remove certaint parts of the script content to be able to use json.loads
script_tag = script_tag.contents[0][20:len(script_tag.contents[0]) - 7]
script_content = json.loads(script_tag)
global ctx
global session_id
global canary
global auth_method_id
global hpg_request_id
ctx = script_content['sCtx']
flowtoken = script_content['sFT']
session_id = script_content['sessionId']
canary = script_content['canary']
auth_methods = script_content['arrUserProofs']
auth_method_id = find_default_auth_method(auth_methods)
print(auth_method_id)
if(auth_method_id == None):
auth_method_id = choose_auth_method(auth_methods)
hpg_request_id = response.headers['x-ms-request-id']
request_data.data = {
"AuthMethodId": auth_method_id,
"Method": "BeginAuth",
"ctx": ctx,
"flowToken": flowtoken
}
request_data.data = json.dumps(request_data.data)
def find_default_auth_method(auth_methods):
for auth_method in auth_methods:
if auth_method['isDefault'] == True:
return auth_method['authMethodId']
def choose_auth_method(auth_methods):
print('Choose auth method:\n')
choice = 1
for auth_method in auth_methods:
print(str(choice) + ". " + auth_method['authMethodId'])
choice += 1
user_input = input()
return auth_methods[int(user_input) - 1]['authMethodId']
def begin_auth(session, request_data):
global type
if auth_method_id == 'PhoneAppNotification':
begin_phone_app_auth(session, request_data)
elif auth_method_id == 'OneWaySMS' or auth_method_id == 'PhoneAppOTP':
begin_one_time_code_auth(session, request_data)
def begin_phone_app_auth(session, request_data):
response = session.post(
'https://login.microsoftonline.com/common/SAS/BeginAuth', data=request_data.data)
j = json.loads(response.text)
app_auth_wait(session, request_data, j['CorrelationId'], j['FlowToken'])
def begin_one_time_code_auth(session, request_data):
global otc
global flowtoken
response = session.post(
'https://login.microsoftonline.com/common/SAS/BeginAuth', data=request_data.data)
SMS_code = input('Enter code: ')
otc = SMS_code
j = json.loads(response.text)
request_data.data = {
'AdditionalAuthData': SMS_code,
'AuthMethodId': auth_method_id,
'Ctx': j['Ctx'],
'FlowToken': j['FlowToken'],
'Method': 'EndAuth',
'PollCount': 1,
'SessionId': session_id
}
request_data.headers = {
'client-request-id': j['CorrelationId']
}
data = json.dumps(request_data.data)
response = session.post('https://login.microsoftonline.com/common/SAS/EndAuth', data=data, headers=request_data.headers)
j = json.loads(response.text)
flowtoken = j['FlowToken']
def app_auth_wait(session, request_data, client_request_id, flow_token):
global flowtoken
request_data.data = {
"Method":"EndAuth",
"SessionId": session_id,
"FlowToken": flow_token,
"Ctx": ctx,
"AuthMethodId": auth_method_id,
"PollCount": 1
}
request_data.headers = {
'client-request-id': client_request_id,
}
data = json.dumps(request_data.data)
for tries in range(10):
response = session.post(
'https://login.microsoftonline.com/common/SAS/EndAuth', data=data, headers=request_data.headers)
j = json.loads(response.text)
if j['Success'] == True:
print('User accepted')
flowtoken = j['FlowToken']
break
request_data.data = {
"Method": "EndAuth",
"SessionId": session_id,
"FlowToken": j['FlowToken'],
"Ctx": ctx,
"AuthMethodId": auth_method_id,
"PollCount": str(tries + 1)
}
data = json.dumps(request_data.data)
print('User still havent authorized')
time.sleep(2)
def process_auth(session, request_data, otc=None):
request_data.data = {
'request': ctx,
'mfaAuthMethod': auth_method_id,
'canary': canary,
'login': login_username,
'flowToken': flowtoken,
'hpgrequestid': hpg_request_id,
'otc': otc
}
return session.post('https://login.microsoftonline.com/common/SAS/ProcessAuth', data=request_data.data)
def scrape_process_auth(response, request_data):
soup = BeautifulSoup(response.text, 'lxml')
saml_response = soup.find('input', {'name': 'SAMLResponse'})['value']
request_data.data = {
'SAMLResponse': saml_response
}
def post_auth_saml_SSO(session, requets_data):
return session.post(
'https://ntnu.blackboard.com/auth-saml/saml/SSO', data=requets_data.data)
def write_to_env_data(session):
BB_ROUTER = session.cookies['BbRouter']
bb_router_values = BB_ROUTER.split(',')
xsrf = bb_router_values[len(bb_router_values) - 1].split(':')
xsrf_value = xsrf[1]
f = open('.env', 'w')
f.write("BB_ROUTER=" + BB_ROUTER + "\n")
f.write("XSRF=" + xsrf_value + "\n")
f.close()
if __name__ == "__main__":
load_dotenv()
login()