Failing to authenticate SharePoint API with username and password for all Python plugins - ssl-certificate

My use case is to get some files from company's sharepoint (Online) site. I have been granted read access for my username password to connect through SharePoint API. For the calls I will have to pass proxy and company SSL verification.
I have tried using a number of APIs such as sharepy, Office365-REST-Python-Client, HttpNtlmAuth, HTTPBasicAuth, but all of them giving me [SSL: CERTIFICATE_VERIFY_FAILED] error.
I am not sure if passing certificate to these APIs is possible or not.
Is there any other plugin that I can try for my scenario?

For this plugin, as a work-around I have done monkey patching for the common functions that send requests to the APIs. Following are the examples of few such functions:
class SharePointApi:
"""SharePoint aceess api."""
def __init__(self):
self.base_url = configReader.get('SHAREPOINT', 'URL')
self.ctx_auth = AuthenticationContext(self.base_url)
self.ctx_auth.provider = SamlTokenProvider(self.base_url, username, password)
self.ctx_auth.provider.acquire_service_token = self._patched_acquire_service_token
self.ctx_auth.provider.acquire_authentication_cookie = self._patched_acquire_authentication_cookie
self.ctx_auth.provider.get_realm_from_target_url = self._patched_get_realm_from_target_url
self.ctx_auth.provider.get_app_only_access_token = self._patched_get_app_only_access_token
def _patched_acquire_authentication_cookie(self, options):
"""Retrieve SPO auth cookie"""
url = options['endpoint']
session = requests.session()
session.post(url, data=self.ctx_auth.provider.token, headers={'Content-Type': 'application/x-www-form-urlencoded'}
, verify=False
)
logger.debug_secrets("session.cookies: %s", session.cookies)
cookies = requests.utils.dict_from_cookiejar(session.cookies)
logger.debug_secrets("cookies: %s", cookies)
if 'FedAuth' in cookies and 'rtFa' in cookies:
self.ctx_auth.provider.FedAuth = cookies['FedAuth']
self.ctx_auth.provider.rtFa = cookies['rtFa']
return True
self.ctx_auth.provider.error = "An error occurred while retrieving auth cookies"
logger.error(self.ctx_auth.provider.error)
return False
def _patched_get_realm_from_target_url(self):
response = requests.head(url=self.ctx_auth.provider.url, headers={'Authorization': 'Bearer'}, verify=False, proxies=proxies)
return self.ctx_auth.provider.process_realm_response(response)
def _patched_get_app_only_access_token(self, target_host, target_realm):
resource = self.ctx_auth.provider.get_formatted_principal(self.ctx_auth.provider.SharePointPrincipal, target_host, target_realm)
client_id = self.ctx_auth.provider.get_formatted_principal(self.ctx_auth.provider.client_id, None, target_realm)
sts_url = self.ctx_auth.provider.get_security_token_service_url(target_realm)
oauth2_request = self.ctx_auth.provider.create_access_token_request(client_id, self.ctx_auth.provider.client_secret, resource)
response = requests.post(url=sts_url, headers={'Content-Type': 'application/x-www-form-urlencoded'}, data=oauth2_request, verify=False, proxies=proxies)
return response.json()

Related

Is it possible to convert HTTPBearer.credentials to Pydantic BaseModel in FastAPI?

I'm using FastAPI's HTTPBearer class to receive authorization tokens on request headers.
This is my dependencies file as per the FastAPI docs, which is used to retrieve the token on any request to the API.
classroom_auth_scheme = HTTPBearer(
scheme_name="Google Auth Credentials",
bearerFormat="Bearer",
description="O-Auth2 Credentials obtained on frontend, used to authenticate with Google services",
)
def get_classroom_token(
token: str = Depends(classroom_auth_scheme),
) -> requests.ClassroomAuthCredentials:
"""Converts a json string of Authorization Bearer token into ClassroomAuthCredentials class
Args:
token (str, optional): Autorization Header Bearer Token. Defaults to Depends(auth_scheme).
Raises:
HTTPException: 400 level response meaning the token was not in the correct format
Returns:
requests.ClassroomAuthCredentials
"""
try:
# token.credentials is a JSON String -> want: pydantic Basemodel
token_dict = json.loads(token.credentials)
token = requests.ClassroomAuthCredentials.parse_obj(token_dict)
return token
except Exception as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"{e}",
)
And this is how I receive the token in my routes:
#router.post("/test-auth", summary="Validate authentication with Google Classroom API")
async def test_auth(token=Depends(get_classroom_token)):
try:
gc_service_test = get_service(token)
gc_api_test = ClassroomApi(service=gc_service_test)
user_profile = gc_api_test.get_user_profile("me")
response: responses.ListGoogleClassroomCourses = {
"message": f"Auth Credentials Are Valid",
"userProfile": user_profile,
}
return JSONResponse(response)
except errors.HttpError as error:
# handle exceptions...
Is there a way to specify the data structure of token.credentials like there is for request body?
This would make it easier to access properties as well as provide a format in the authorize modal on the swagger docs for other developers on the team, so they don't have to guess what the required properties of the Authorization token are.
This is my data model of the token.credentials as a BaseModel
class ClassroomAuthCredentials(BaseModel):
token: str = Field(..., example="MyJWT")
clientId: str = Field(..., example="myClientId")
clientSecret: str = Field(..., example="myClientSecret")
refreshToken: str = Field(..., example="myRefreshToken")
scopes: list[str] = Field(
...,
example=[
"https://www.googleapis.com/auth/classroom.courses.readonly",
"https://www.googleapis.com/auth/classroom.coursework.students",
],
)

Is there a way to authenticate OAUTH2.0 of google API through terminal?

I am fetching google photos from my account using Google Photo API. Now there is a need for me to execute that php file via terminal, but the problem is that I can't authenticate with Google API in doing so. Is there a way to do this, and if yes, then how shall it be done?
Yes, it is possible, you need an interactive login for the first authentication but then you can save the token and refresh it automatically as required.
I have implemented this class in Python to do just that.
from requests.adapters import HTTPAdapter
from requests_oauthlib import OAuth2Session
from pathlib import Path
from urllib3.util.retry import Retry
from typing import List, Optional
from json import load, dump, JSONDecodeError
import logging
log = logging.getLogger(__name__)
# OAuth endpoints given in the Google API documentation
authorization_base_url = "https://accounts.google.com/o/oauth2/v2/auth"
token_uri = "https://www.googleapis.com/oauth2/v4/token"
class Authorize:
def __init__(
self, scope: List[str], token_file: Path,
secrets_file: Path, max_retries: int = 5
):
""" A very simple class to handle Google API authorization flow
for the requests library. Includes saving the token and automatic
token refresh.
Args:
scope: list of the scopes for which permission will be granted
token_file: full path of a file in which the user token will be
placed. After first use the previous token will also be read in from
this file
secrets_file: full path of the client secrets file obtained from
Google Api Console
"""
self.max_retries = max_retries
self.scope: List[str] = scope
self.token_file: Path = token_file
self.session = None
self.token = None
try:
with secrets_file.open('r') as stream:
all_json = load(stream)
secrets = all_json['installed']
self.client_id = secrets['client_id']
self.client_secret = secrets['client_secret']
self.redirect_uri = secrets['redirect_uris'][0]
self.token_uri = secrets['token_uri']
self.extra = {
'client_id': self.client_id,
'client_secret': self.client_secret}
except (JSONDecodeError, IOError):
print('missing or bad secrets file: {}'.format(secrets_file))
exit(1)
def load_token(self) -> Optional[str]:
try:
with self.token_file.open('r') as stream:
token = load(stream)
except (JSONDecodeError, IOError):
return None
return token
def save_token(self, token: str):
with self.token_file.open('w') as stream:
dump(token, stream)
self.token_file.chmod(0o600)
def authorize(self):
""" Initiates OAuth2 authentication and authorization flow
"""
token = self.load_token()
if token:
self.session = OAuth2Session(self.client_id, token=token,
auto_refresh_url=self.token_uri,
auto_refresh_kwargs=self.extra,
token_updater=self.save_token)
else:
self.session = OAuth2Session(self.client_id, scope=self.scope,
redirect_uri=self.redirect_uri,
auto_refresh_url=self.token_uri,
auto_refresh_kwargs=self.extra,
token_updater=self.save_token)
# Redirect user to Google for authorization
authorization_url, _ = self.session.authorization_url(
authorization_base_url,
access_type="offline",
prompt="select_account")
print('Please go here and authorize,', authorization_url)
# Get the authorization verifier code from the callback url
response_code = input('Paste the response token here:')
# Fetch the access token
self.token = self.session.fetch_token(
self.token_uri, client_secret=self.client_secret,
code=response_code)
self.save_token(self.token)
# note we want retries on POST as well, need to review this once we
# start to do methods that write to Google Photos
retries = Retry(total=self.max_retries,
backoff_factor=0.1,
status_forcelist=[500, 502, 503, 504],
method_whitelist=frozenset(['GET', 'POST']),
raise_on_status=False)
self.session.mount('https://', HTTPAdapter(max_retries=retries))

flask-jwt-extended current_user identity = None when creating non-fresh access token from refresh token

In my flask app (python 2.7), I am trying to trigger the access token to refresh via the refresh token whenever it expires with the #jwt.expired_token_loader decorator. Both the access token and refresh token are stored in a cookie (JWT_TOKEN_LOCATION = 'cookies').
I am utilizing the same code provided in the documentation (https://flask-jwt-extended.readthedocs.io/en/latest/tokens_in_cookies.html) to do this and I am able to successfully generate a new access token. However, upon generation of a new access token the identity claim of the access token is equal to None (get_raw_jwt()). No matter what I do to the refresh token or the access token whenever I print the jwt_claims or attempt to grab the current user with current_user = get_jwt_identity() it returns the identity as None. It is important for me to know which user is submitting queries to the Neural Net so I can properly keep track of which queries were submitted by what users (one-to-many relationship).
I have tried troubleshooting (decoding) the refresh_token and I ran into a separate issue: When I try to decode the refresh_token, with decode_token(), I get a long traceback that ends in InvalidSignatureError: Signature verification failed. I took the refresh_token and ran it through https://jwt.io and it decodes the token. I can see in the decoded token that the "identity" claim is providing me the user's identity, but it tells me that the token is not verified. However, once I check the secret base64 encoded box on the screen the signature becomes verified and the signature portion of the jwt changed along with it. I attempt to decode this modified jwt that https://jwt.io provided me with the decode_token function and it still provides me the same error: InvalidSignatureError: Signature verification failed.
I have spent hours reading everything google provides me on flask-jet-extended and PyJWT and I cannot figure out how to fix it. I have tried modifying my JWT_SECRET_KEY configuration to different strings and even encoding it with base64 and none of this solves the issues. I have turned on and off the JWT_COOKIE_CSRF_PROTECT configuration. I have turned on and off JWT_ACCESS_COOKIE_PATH and JWT_REFRESH_COOKIE_PATH.I have tried to decode both the refresh_token_cookie and CSRF_refresh_token cookie. I have attempted to run decode_token() with the refresh_token_cookie and CSRF_refresh_token cookie while providing the csrf_value=request.cookies.get('csrftoken') argument. I have tried using the decode() function from jwt directly (
from jwt import decode).
I just don't know what else to do and cannot find any additional online resources. Any help is much appreciated!!
My next step is either to move my authentication system to flask-jet-simple or PyJWT. I really want to use JWT to authenticate my users. I do not know how to combine JWT with flask-login or if this is even possible. I can't manage to find any resources online where someone has utilized flask-login with JWT. I did find a fairly recent repo called flask-jwt-login that I might try to use if I can't figure this out. Ultimately I would like to stay with flask-jwt-extended. I have other parts of this web app I need to focus on and want to get his part squared away.
Anyways, here is my code, the workflow starts on the /login page. This will redirect you to the /NN page. Once the access token expires, if you try to reload the /NN page it will reroute itself to the /token/refresh page. Once it refreshes the token, it will return back to the /NN page.
Please let me know if I need to upload any additional files.
P.S. This is my first post on stack overflow so forgive me for any formatting issues.
application.py
from flask import url_for,render_template, redirect,request, jsonify,flash,\
make_response, session
from flask_jwt_extended import (create_access_token, create_refresh_token,
jwt_required, get_jwt_identity, get_jwt_claims,get_current_user,
set_access_cookies,set_refresh_cookies,
unset_jwt_cookies, get_raw_jwt, jwt_refresh_token_required,decode_token)
from jwt import decode
from forms import RegisterForm, LoginForm, NNForm
from models import Users
from website import app,db,jwt
#ToDo When the token expires I get an HTTP status code of 401 I can use expired_token_loader refresh token.
#app.route('/token/refresh', methods=['GET','POST'])
#jwt_refresh_token_required
#jwt.expired_token_loader
def refresh():
#Create the new access token
ref_token = request.cookies.get('refresh_token_cookie')
csrftoken = request.cookies.get('csrftoken')
decode_ref_token = decode_token(ref_token)
current_user = get_jwt_identity()
print('ref_token:', ref_token)
print('current_user:', current_user, get_raw_jwt())
access_token = create_access_token(identity=current_user)
#Set the JWT access cookie in the response
print('from refresh():', request.url)
response = make_response(redirect(request.url))
set_access_cookies(response,access_token)
#set_refresh_cookies()
return response
#app.route('/token/remove', methods=['POST'])
def logout():
#ToDo Still need to build the logout page.
response = make_response(redirect(url_for('logout_page')))
unset_jwt_cookies(response)
return response
#app.route('/register/', methods=['GET','POST'])
def register_page():
form = RegisterForm(request.form)
print( request.method, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
user = Users(form.first_name.data, form.last_name.data, \
form.email.data, form.password.data, form.organization.data)
user.save_to_db()
flash("Thanks for Registering. Please login")
return redirect(url_for("NN_page"))
return render_template('register.html',form=form)
#app.route('/login/', methods=['GET','POST'])
def login_page():
form = LoginForm(request.form)
print(request.method, request.form)
if request.method == "POST":
#This checks if the user is in the db and returns the user obj.
user = form.validate_on_submit()
if user:
access_token = create_access_token(identity=user.email, fresh=True)
refresh_token = create_refresh_token(identity=user.email)
response = make_response(redirect(url_for('NN_page')))
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
#response.headers['Authorization'] = 'Bearer {}'.format(access_token)
print(response)
return response
#return jsonify({'access_token':access_token})
#return redirect((url_for("NN_page")))
return render_template('login_page.html', form=form)
#jwt.invalid_token_loader #This allows me to stop people who have not logged in yet.
def missing_JWT_token(msg):
print('from missing_JWT_token:', msg)
return redirect(url_for('login_page'))
# return "The site being accessed requires a valid JWT to view." \
# "Error: {}".format(msg)
#app.route('/NN/', methods=['GET','POST'])
#jwt_required
def NN_page():
jwt_claims = get_raw_jwt()
print(jwt_claims)
print('cookie keys:', request.cookies.get('refresh_token_cookie'))
user = get_jwt_identity()
print('User:',user)
form = NNForm(request.form, headers=request.headers)
print(request.form, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
return redirect((url_for("success_NN_submission")))
return render_template('NN_page.html', form=form)
config.py
import os
from datetime import timedelta
from base64 import b64encode
secret_key = os.urandom(24)
jwt_secret_key = b64encode('I_love_my_smokes!')
class BaseConfig(object):
SECRET_KEY = secret_key
SQLALCHEMY_DATABASE_URI = 'sqlite:///Protein_NN.db'
SQLALCHEMY_TRACK_MODIFICATION = False
#JWT_SECRET_KEY = jwt_secret_key
JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=10)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(minutes=1)
JWT_TOKEN_LOCATION = 'cookies'
#JWT_ACCESS_COOKIE_PATH = '/NN/'
#JWT_REFRESH_COOKIE_PATH ='/token/refresh'
JWT_COOKIE_CSRF_PROTECT = False
SESSION_COOKIE_SECURE = True
class DevelopmentConfig(BaseConfig):
DEBUG = True
JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=5)
SESSION_COOKIE_SECURE = False
#PROPOGATE_EXCEPTION = True
#EMAIL SETTINGS
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
#MAIL_PORT = 587 # This is for TLS
MAIL_USE_TLS = False
MAIL_USE_SSL = True
#MAIL_USERNAME = os.environ['EMAIL_USER']
#MAIL_PASSWORD = os.environ['EMAIL_PASSWORD']
#BOOTSTRAP_SERVE_LOCAL = True
This is what get_raw_jwt() returns after the access token has been refreshed by the refresh token.
{'user_claims': {}, u'jti': u'9fb01b6c-619b-4fe6-91d3-73f8609f2f61',
u'exp': 1547022397, u'iat': 1547022392, u'fresh': False,
u'type': u'access', u'nbf': 1547022392, u'identity': None}
As you can see the identity claim is equal to None.
Here is the traceback I see:
Traceback (most recent call last):
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1719, in handle_user_exception
return handler(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask_jwt_extended/jwt_manager.py", line 93, in handle_expired_error
return self._expired_token_callback()
File "/Users/Danny/Documents/Codes/Ellington/NN App/website/application.py", line 43, in refresh
print('current_user:', current_user, decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNzVlNWExMy1mNjRiLTQxNmItOTY0ZC0wMDg5ODI4NGY2NGQiLCJleHAiOjE1NDcwMTk5ODUsImlhdCI6MTU0NzAxOTkyNSwidHlwZSI6InJlZnJlc2giLCJuYmYiOjE1NDcwMTk5MjUsImlkZW50aXR5IjoiZGFubnlAbWUuY29tIn0.LVEj6As2Uh_xgTbjm94b0M6mJeD0YLkf9KpgNKTZJOw'))
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jwt.py", line 92, in decode
jwt, key=key, algorithms=algorithms, options=options, **kwargs
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 156, in decode
key, algorithms)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 223, in _verify_signature
raise InvalidSignatureError('Signature verification failed')
InvalidSignatureError: Signature verification failed
Having those two separate decorators on your refresh function won’t work the way you want it to. The expired loader decorator isn’t going to have a current user set because the jwt isn’t valid when that callback function is called.
Instead try breaking out the refresh code into a helper function used by both decorators independently:
def refresh_token(username):
# return flask response from here
#jwt.expired_token_loader
def handle_expired_token():
# get username here from raw jwt
username = 'todo'
return refresh_token(username)
#app.route(‘/refresh)
#jwt_refresh_token_required
def refresh_endpoint():
username = get_current_identity()
return refresh_token(username)
You could also use a custom decorator instead of the jwt_required decorator and achieve a similar thing. Some examples of that are discussed here: https://gitter.im/flask-jwt-extended/Lobby?at=5c1a9b37c35a3002474ddf3d

How to make Flask Requests module authenticate?

I built my own API with flask, but now I'm trying to write a script to consume the API and the Requests module won't make it past the log in.
The Form
The form has jut a 'username' input, 'password', and I'm also using flask-wtf to implement csrf protection, so there is a 'csrf_token' hidden input.
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=1, max=20)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=1, max=25)])
The Route
#auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'GET':
return render_template('login.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
credentials = {'username': request.form['username'],
'password': request.form['password']}
try:
r = requests.post('http://api.domain.com:80/authenticate', data=credentials)
response = r.json()
except Exception as e:
print('JSON Auth Error: {}'.format(e))
else:
if r.status_code is 200:
session['access_token'] = response['access_token']
session['refresh_token'] = response['refresh_token']
return redirect('/')
return render_template('login.html', form=form, error='invalid')
The API Endpoint
#api.route('/authenticate', methods=['POST'])
def authenticate():
# form data
username = request.form['username']
password = request.form['password']
# check if user exists
try:
user = User.query.filter_by(username=username).first()
except exc.SQLAlchemyError as e:
print(e)
if user and password == user.password:
tokens = generate_tokens()
return jsonify({'access_token': tokens['access'], 'refresh_token': tokens['refresh']}), 200
else:
return jsonify({'message': 'invalid login'}), 401
The API Resource Endpoint I am trying to access
#api.route('/pending', methods=['GET'])
#login_required
def pending():
The #login_required verification method
def tokens_valid():
if 'access_token' and 'refresh_token' in session:
# retrieve access and refresh tokens from session cookie
tokens = {'access_token': session['access_token'],
'refresh_token': session['refresh_token']}
# validate token endpoint
try:
r = requests.post('http://api.domain.com/validate', data=tokens)
response = r.json()
except Exception as e:
print('JSON Token Error: {}'.format(e))
else:
if r.status_code is 200:
if response['message'] == 'valid access token':
return True
elif response['message'] == 'new access token':
#set new tokens
session['access_token'] = response['access_token']
session['refresh_token'] = response['refresh_token']
return True
return False
What I have tried
v.1
def login():
credentials = {'username': 'username',
'password': 'password'}
s = requests.Session()
r = s.post('http://sub.domain.com/auth/login', data=credentials)
return s
v.2
def login():
login_form = requests.get('http://sub.domain.com/auth/login')
soup = BeautifulSoup(login_form.text, "html.parser")
csrf = soup.find('input', id='csrf_token').get('value')
credentials = {'username': 'username',
'password': 'password',
'csrf_token': csrf}
s = requests.Session()
r = s.post('http://sub.domain.com/auth/login', data=credentials)
return s
v.3
from flask import Flask, session
def login():
credentials = {'username': 'username',
'password': 'password'}
r = requests.post('http://api.domain.com/authenticate', data=credentials)
tokens = r.json()
session['access_token'] = tokens['access_token']
session['refresh_token'] = tokens['refresh_token']
So far I can get the '/authenticate' api endpoint to return my session tokens, but i can't access the protected resource because i can't seem to save them to my session any how.
Can anyone help me figure this out?

Why does this Python Reddit API call fail?

If someone could point me in the right direction I would be very grateful. I am trying to use the requests module to interact with Reddit's APIs, but for some reason I am getting HTTP status 403 codes. I am able to successfully log in I believe (since I get HTTP 200), but I cannot perform a successful API request. I am very new to this so please use small words. Thanks.
import requests
import json
from pprint import pprint
# URLs
url_base = r'https://www.reddit.com'
url_login = '/api/login'
url_me = '/api/v1/me'
# Credentials
user = '__kitten_mittens__'
passwd = 'password'
params = {'user': user,
'passwd': passwd,
'api_type': 'json',}
headers = {'user-agent': '/u/__kitten_mittens__ practice api bot'}
# Set up the session/headers
client = requests.session()
client.headers = headers
response = client.post(url_base + url_login, data = params)
j = json.loads(response.content.decode('utf-8'))
mh = j['json']['data']['modhash']
print("The modhash for {USER} is {mh}".format(USER=user, mh=mh))
# THIS CODE GIVES HTTP STATUS CODE 403
response = client.get(url_base + url_me)
me_json = json.loads(r.content.decode('utf-8'))
pprint(me_json)
EDIT: I fixed the missing single quote from the password field. That was not part of the problem.