OAuth2 Grant Type - authorization code (python) - beautifulsoup

I am try to figure out how to get Oauth 2 working in my python code.
import requests, json
import webbrowser
authorize_url = "https://tcfhirsandbox.com.au/oauth2/authorize"
token_url = "https://tcfhirsandbox.com.au/oauth2/token"
state = 'asdasdasdasdasdas'
scope = 'noscope'
callback_uri = "x-argonaut-app://HealthProviderLogin/"
test_api_url = "https://tcfhirsandbox.com.au/fhir/dstu2/Patient?identifier=RN000000200"
client_id = '6A605kYem9GmG38Vo6TTzh8IFnjWHZWtRn46K1hoxQ'
client_secret = 'POrisHrcdMvUKmaR6Cea0b8jtx-z4ewVWrnaIXASO-H3tB3g5MgPV7Vqty7OP8aEbSGENWRMkeVKZDdG7Pw'
authorization_redirect_url = authorize_url + '?response_type=code&state=' + state + '&client_id=' + client_id + '&scope='+scope+'&redirect_uri=' + callback_uri
webbrowser.open(authorization_redirect_url)
authorization_code = input("Code:")
data = {'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': callback_uri}
access_token_response = requests.post(token_url, data=data, verify=True, allow_redirects=True, auth=(client_id, client_secret))
tokens = json.loads(access_token_response.text)
access_token = tokens['access_token']
api_call_headers = {'Authorization': 'Bearer ' + access_token}
api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=True)
print(api_call_response.status_code)
print (api_call_response.text)
The issue here is I have to manually input the code from the authorization URL. I want to automate it !
Thanks,
22/01/2021|09:54AM
Tried this
import requests, json
rom bs4 import BeautifulSoup
import mechanize
authorize_url = "https://tcfhirsandbox.com.au/oauth2/authorize"
token_url = "https://tcfhirsandbox.com.au/oauth2/token"
state = 'asdasdasdasdasdas'
scope = 'noscope'
callback_uri = "x-argonaut-app://HealthProviderLogin/"
test_api_url = "https://tcfhirsandbox.com.au/fhir/dstu2/Patient?identifier=RN000000200"
client_id = '6A605kYem9GmG38Vo6TTzh8IFnjWHZWtRn46K1hoxQ'
client_secret = 'POrisHrcdMvUKmaR6Cea0b8jtx-z4ewVWrnaIXASO-H3tB3g5MgPV7Vqty7OP8aEbSGENWRMkeVKZDdG7Pw'
OAuth_url = authorize_url + '?response_type=code&state=' + state + '&client_id=' + client_id + '&scope='+scope+'&redirect_uri=' + callback_uri
br = mechanize.Browser()
br.open(OAuth_url)
br.select_form(nr=0)
br.form['Username'] = 'my_username'
br.form['Password'] = 'my_password'
r = br.submit()
#print(r.read())
resp = r.read()
br.select_form(nr=0)
ac = br.form.click(name = 'Accept')
soup = BeautifulSoup(resp)
print(soup)
print(ac)
auth_code = str(ac)
code_list = auth_code.split("=")
cd_lst = code_list[1].split("&")
authorization_code = str(cd_lst[0])
print(authorization_code)
data = {'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': callback_uri}
access_token_response = requests.post(token_url, data=data, verify=True, allow_redirects=True, auth=(client_id, client_secret))
print(access_token_response.status_code)
tokens = json.loads(access_token_response.text)
access_token = tokens['access_token']
print(access_token)
I think I am close but still couldn't get it working.
It is giving bad request (error code:400) as response.
If anyone can help with this would be awesome. Thanks

TL;DR You're getting a 400 BAD_REQUEST because the OAuth_url isn't properly constructed. Try:
https://tcfhirsandbox.com.au/oauth2/authorize?
response_type=code&
state=asdasdasdasdasdas&client_id=6A605kYem9GmG38Vo6TTzh8IFnjWHZWtRn46K1hoxQ&
redirect_uri=x-argonaut-app://HealthProviderLogin/
scope=launch%2Fpatient+openid+fhirUser+patient%2F%2A.read&
aud=https://tcfhirsandbox.com.au/
The base url for the reference server you gave (https://tcfhirsandbox.com.au) doesn't resolve.
So I'll demonstrate using another reference server.
CapabilityStatement: https://inferno.healthit.gov/reference-server/r4/metadata?_format=json
SMART Configuration: https://inferno.healthit.gov/reference-server/r4/.well-known/smart-configuration.json
The OAuth_url you've constructed:
https://tcfhirsandbox.com.au/oauth2/authorize?
response_type=code&
state=asdasdasdasdasdas&client_id=6A605kYem9GmG38Vo6TTzh8IFnjWHZWtRn46K1hoxQ&
redirect_uri=x-argonaut-app://HealthProviderLogin/
scope=noscope&
A working OAuth_url with the reference server [^1]:
https://inferno.healthit.gov/reference-server/oauth/authorization?
response_type=code&
state=ad6458f9-240a-42b7-b314-05d0c3b2c7c9&
client_id=SAMPLE_CONFIDENTIAL_CLIENT_ID&
redirect_uri=https%3A%2F%2Finferno.healthit.gov%2Finferno%2Foauth2%2Fstatic%2F
redirect&
scope=launch%2Fpatient+openid+fhirUser+patient%2F%2A.read&
aud=https%3A%2F%2Finferno.healthit.gov%2Freference-server%2Fr4
You'll see two differences between the request you've constructed and reference, one of which is likely causing the 400 BAD_REQUEST:
scope (noscope isn't a valid SMART on FHIR scope [^2], which will cause most servers to error)
aud (you didn't include an aud query param, which is used as a claim in the access_token jwt you'll be issued)
[^1] Constructed using the Inferno test: https://inferno.healthit.gov/inferno/5g6OuEGN4hM/test_sets/test_procedure/
[^2] Direct link to supported SMART on FHIR scopes: http://hl7.org/fhir/smart-app-launch/scopes-and-launch-context/index.html#quick-start

Related

Encrypted access token request to google api failed with 400 code

Recently I come up a scenario where I need to encrypt a WEB API request and response using PyCryptodome inside Synapse notebook activity. I am trying to make a call to Google API, but the request should be encrypted and similarly response should be encrypted. After making the call with encrypted data, I am getting below error.
Error:
error code: 400, message: Invalid JSON Payload received. Unexpected Token, Status: Invalid argument.
I have written below code:-
import os
import requests
import json
import base64
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.padding import pad,unpad
import secrets
key= os.urandom(16)
iv = Random.new().read(AES.block_size)
def encrypt_data(key, data):
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_data = base64.b64encode(cipher.encrypt(pad(data)))
return encrypted_data
url = "https://accounts.google.com/o/oauth2/token"
client_Id = "XXXXX"
client_secret = "YYYYY"
grant_type = "refresh_token"
refresh_token = "ZZZZZZ"
access_type="offline"
data = {"grant_type":grant_type,
"client_id":client_Id,
"client_secret":client_secret,
"refresh_token":refresh_token,
"access_type":access_type
}
encode_data = json.dumps(data).encode("utf-8")
encrypt_data = encrypt_data(key,encode_data)
response = requests.post(url, data = encrypt_data)
print(response.content)
It would be really helpful if someone can give me idea or guide me on how I can achieve this.
Thank You!

Retrieve a JWT from a feature A in a feature B

I’m trying to retrieve a JWT from a feature A in a feature B.
For this I have in the feature A:
# create API access for the client
Given url baseUrl
And path 'admin', 'clients', clientApiId, 'accesses', 'api', 'api-key', 'renew'
And header Authorization = 'Bearer ' + authenticationJWT
When method put
Then status 200
* def clientApiJWT = response
And in feature B:
# Create a process with API access
* def clientApiAccess = call read('classpath:karate/common/create-client-api-access.feature')
* clientApiJWT = clientApiAccess.clientApiJWT
With this code I recover the following error:
Thank you for your help
Shouldn't it be:
* def clientApiJWT = clientApiAccess.clientApiJWT

Replacing Requests with urllib2

I have the following code using requests to generate CSRFToken import requests:
import requests
class TokenGenerator:
def GenerateToken(self, uid, pwd):
TokenGenerator.session = requests.Session()
TokenGenerator.resp = TokenGenerator.session.get("http://chp1766.neilsoft.in:8000/", cookies={'contact.language': 'en'},auth=(uid,pwd))
if TokenGenerator.resp.status_code == 200:
token = TokenGenerator.session.cookies.get('CSRFToken')
print("TOKEN")
print(token)
obj = TokenGenerator()
obj.GenerateToken('caddok', '')
How can I replace the requests module with urllib2?
With urllib you can get the same result with the following code:
req1 = urllib2.Request("YOUR URL")
response = urllib2.urlopen(req1)
cookie = response.headers.get('CSRFToken')
print(cookie)

Sending form data with an HTTP PUT request using Grinder API

I'm trying to replicate the following successful cURL operation with Grinder.
curl -X PUT -d "title=Here%27s+the+title&content=Here%27s+the+content&signature=myusername%3A3ad1117dab0ade17bdbd47cc8efd5b08" http://www.mysite.com/api
Here's my script:
from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPRequest
from HTTPClient import NVPair
import hashlib
test1 = Test(1, "Request resource")
request1 = HTTPRequest(url="http://www.mysite.com/api")
test1.record(request1)
log = grinder.logger.info
test1.record(log)
m = hashlib.md5()
class TestRunner:
def __call__(self):
params = [NVPair("title","Here's the title"),NVPair("content", "Here's the content")]
params.sort(key=lambda param: param.getName())
ps = ""
for param in params:
ps = ps + param.getValue() + ":"
ps = ps + "myapikey"
m.update(ps)
params.append(NVPair("signature", ("myusername:" + m.hexdigest())))
request1.setFormData(tuple(params))
result = request1.PUT()
The test runs okay, but it seems that my script doesn't actually send any of the params data to the API, and I can't work out why. There are no errors generated, but I get a 401 Unauthorized response from the API, indicating that a successful PUT request reached it, but obviously without a signature the request was rejected.
This isn't exactly an answer, more of a workaround that I came up with, that I've decided to post since this question hasn't yet received any responses, and it may help anyone else trying to achieve the same thing.
The workaround is basically to use the httplib and urllib modules to build and make the PUT request instead of the HTTPClient module.
import hashlib
import httplib, urllib
....
params = [("title", "Here's the title"),("content", "Here's the content")]
params.sort(key=lambda param: param[0])
ps = ""
for param in params:
ps = ps + param[1] + ":"
ps = ps + "myapikey"
m = hashlib.md5()
m.update(ps)
params.append(("signature", "myusername:" + m.hexdigest()))
params = urllib.urlencode(params)
print params
headers = {"Content-type": "application/x-www-form-urlencoded"}
conn = httplib.HTTPConnection("www.mysite.com:80")
conn.request("PUT", "/api", params, headers)
response = conn.getresponse()
print response.status, response.reason
print response.read()
conn.close()
(Based on the example at the bottom of this documentation page.)
You have to refer to the multi-form posting example in Grinder script gallery, but changing the Post to Put. It works for me.
files = ( NVPair("self", "form.py"), )
parameters = ( NVPair("run number", str(grinder.runNumber)), )
# This is the Jython way of creating an NVPair[] Java array
# with one element.
headers = zeros(1, NVPair)
# Create a multi-part form encoded byte array.
data = Codecs.mpFormDataEncode(parameters, files, headers)
grinder.logger.output("Content type set to %s" % headers[0].value)
# Call the version of POST that takes a byte array.
result = request1.PUT("/upload", data, headers)

Google Reader API Unread Count

Does Google Reader have an API and if so, how can I get the count of the number of unread posts for a specific user knowing their username and password?
This URL will give you a count of unread posts per feed. You can then iterate over the feeds and sum up the counts.
http://www.google.com/reader/api/0/unread-count?all=true
Here is a minimalist example in Python...parsing the xml/json and summing the counts is left as an exercise for the reader:
import urllib
import urllib2
username = 'username#gmail.com'
password = '******'
# Authenticate to obtain SID
auth_url = 'https://www.google.com/accounts/ClientLogin'
auth_req_data = urllib.urlencode({'Email': username,
'Passwd': password,
'service': 'reader'})
auth_req = urllib2.Request(auth_url, data=auth_req_data)
auth_resp = urllib2.urlopen(auth_req)
auth_resp_content = auth_resp.read()
auth_resp_dict = dict(x.split('=') for x in auth_resp_content.split('\n') if x)
auth_token = auth_resp_dict["Auth"]
# Create a cookie in the header using the SID
header = {}
header['Authorization'] = 'GoogleLogin auth=%s' % auth_token
reader_base_url = 'http://www.google.com/reader/api/0/unread-count?%s'
reader_req_data = urllib.urlencode({'all': 'true',
'output': 'xml'})
reader_url = reader_base_url % (reader_req_data)
reader_req = urllib2.Request(reader_url, None, header)
reader_resp = urllib2.urlopen(reader_req)
reader_resp_content = reader_resp.read()
print reader_resp_content
And some additional links on the topic:
http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
How do you access an authenticated Google App Engine service from a (non-web) python client?
http://blog.gpowered.net/2007/08/google-reader-api-functions.html
It is there. Still in Beta though.
Here is an update to this answer
import urllib
import urllib2
username = 'username#gmail.com'
password = '******'
# Authenticate to obtain Auth
auth_url = 'https://www.google.com/accounts/ClientLogin'
#auth_req_data = urllib.urlencode({'Email': username,
# 'Passwd': password})
auth_req_data = urllib.urlencode({'Email': username,
'Passwd': password,
'service': 'reader'})
auth_req = urllib2.Request(auth_url, data=auth_req_data)
auth_resp = urllib2.urlopen(auth_req)
auth_resp_content = auth_resp.read()
auth_resp_dict = dict(x.split('=') for x in auth_resp_content.split('\n') if x)
# SID = auth_resp_dict["SID"]
AUTH = auth_resp_dict["Auth"]
# Create a cookie in the header using the Auth
header = {}
#header['Cookie'] = 'Name=SID;SID=%s;Domain=.google.com;Path=/;Expires=160000000000' % SID
header['Authorization'] = 'GoogleLogin auth=%s' % AUTH
reader_base_url = 'http://www.google.com/reader/api/0/unread-count?%s'
reader_req_data = urllib.urlencode({'all': 'true',
'output': 'xml'})
reader_url = reader_base_url % (reader_req_data)
reader_req = urllib2.Request(reader_url, None, header)
reader_resp = urllib2.urlopen(reader_req)
reader_resp_content = reader_resp.read()
print reader_resp_content
Google Reader removed SID auth around June, 2010 (I think), using new Auth from ClientLogin is the new way and it's a bit simpler (header is shorter). You will have to add service in data for requesting Auth, I noticed no Auth returned if you don't send the service=reader.
You can read more about the change of authentication method in this thread.
In the API posted in [1], the "token" field should be "T"
[1] http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI