access ADFS/OIDC protected webapi with curl or python - python

I would like to access a webapi by a script(bash or python), which is protected by mod_openidc/apache2 and an self-hosted ADFS.
For the authentication, a certificate from a smartcard or locally stored certificate is required.
I already tried several approaches with python or curl, but got no nearly working script.
approach at python:
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session
client_id="abcdef-abcd-abcd-abcd-abcdefghijk"
client = BackendApplicationClient(client_id=client_id)
#client = BackendApplicationClient()
oauth = OAuth2Session(client=client)
protected_url="https://protectedurl/page/"
oauth.fetch_token(token_url='https://sts.myserver.net/adfs/oauth2/token/', include_client_id=True, cert=('/home/user/cert.pem', '/home/user/server.key'))
which lead to: "oauthlib.oauth2.rfc6749.errors.InvalidClientError: (invalid_client) MSIS9627: Received invalid OAuth client credentials request. Client credentials are missing or found empty"
curl:
curl --cert /home/user/cert.pem --key /home/user/server.key
https://sts.example.net/adfs/oauth2/authorize/?response_type=code&scope=openid%20email%20profile%20allatclaims&client_id=XXX&state=XXXredirect_uri=https%3A%2F%2Fexample.net%2Fpage%2Fredirect_uri&nonceXXX
Which gives the sts page in html
So I think I dont have some small bug, but a wrong approach
Since it works in a browser, I dont suggest a issue on server side
Any approaches and examples are warmly welcome

Related

Getting a 401 response while using Requests package

I am trying to access a server over my internal network under https://prodserver.de/info.
I have the code structure as below:
import requests
from requests.auth import *
username = 'User'
password = 'Hello#123'
resp = requests.get('https://prodserver.de/info/', auth=HTTPBasicAuth(username,password))
print(resp.status_code)
While trying to access this server via browser, it works perfectly fine.
What am I doing wrong?
By default, requests library verifies the SSL certificate for HTTPS requests. If the certificate is not verified, it will raise a SSLError. You check this by disabling the certificate verification by passing verify=False as an argument to the get method, if this is the issue.
import requests
from requests.auth import *
username = 'User'
password = 'Hello#123'
resp = requests.get('https://prodserver.de/info/', auth=HTTPBasicAuth(username,password), verify=False)
print(resp.status_code)
try using requests' generic auth, like this:
resp = requests.get('https://prodserver.de/info/', auth=(username,password)
What am I doing wrong?
I can not be sure without investigating your server, but I suggest checking if assumption (you have made) that server is using Basic authorization, there exist various Authentication schemes, it is also possible that your server use cookie-based solution, rather than headers-based one.
While trying to access this server via browser, it works perfectly
fine.
You might then use developer tools to see what is actually send inside and with request which does result in success.

Safe PUT authentication in python requests

I am trying to make a script that uploads files to a nextcloud server via webdav. The documentation says this can be done using a PUT HTTP request, like so:
import requests
url = "https://example.com/path/to/file.txt"
file = open("file/to/upload.txt")
response = requests.put(url, auth=(user, passwd), data=file)
This works just fine, and I get a 201 status code as a response, which means the file has been created, but I feel like its quite unsafe to do it this way.
I can add the parameter verify=True to make sure the file is sent encrypted if the web has a valid SSL certificate, but I don't know if the authentication credentials are encrypted too.
In case they're not, how would I securely make the request without revealing my password?
You're PUTting to a url that starts with https so you're making the request over TLS which is secure if you have a good certificate. If you have a good certificate, your username and password will also be sent over TLS and be secure.
To find out if your certificate is good, try running openssl s_client -showcerts -connect example.com:443 from the commandline and check that it reports Verify return code: 0 (ok) or try opening a browser and type in https://example.com and see if the browser complains.

How to authenticate in Jenkins while remotely accessing its JSON API?

I need to access the Jenkins JSON API from a Python script. The problem is that our Jenkins installation is secured so to log in users have to select a certificate. Sadly, in Jenkins Remote Access Documentation they don't mention a thing about certificates and I tried using the API Token without success.
How can I get to authenticate from a Python script to use their JSON API?
Thanks in advance!
You have to authenticate to the JSON API using HTTP Basic Auth.
To make scripted clients (such as wget) invoke operations that require authorization (such as scheduling a build), use HTTP BASIC authentication to specify the user name and the API token. This is often more convenient than emulating the form-based authentication
https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients
Here is a sample of using Basic Auth with Python.
http://docs.python-requests.org/en/master/user/authentication/
Keep in mind if you are using a Self Signed certificate on an internal Jenkin Server you'll need to turn off certificate validation OR get the certificate from the server and add it to the HTTP request
http://docs.python-requests.org/en/master/user/advanced/
I finally found out how to authenticate to Jenkins using certs and wget. I had to convert my pfx certificates into pem ones with cert and keys in separate files For more info about that come here. In the end this is the command I used.
wget --certificate=/home/B/cert.pem --private-key=/home/B/key.pem --no-check-certificate --output-document=jenkins.json https:<URL>
I'm not completely sure it covers your certificate use case, but since it took me some time to find out, I still want to share this snipped that retrieves the email address for a given user name in Python without special Jenkins libraries. It uses an API token and "supports" (actually ignores) https:
def _get_email_adress(user):
request = urllib.request.Request("https://jenkins_server/user/"+ user +"/api/json")
#according to https://stackoverflow.com/a/28052583/4609258 the following is ugly
context = ssl._create_unverified_context()
base64string = base64.b64encode(bytes('%s:%s' % ('my user name', 'my API token'),'ascii'))
request.add_header("Authorization", "Basic %s" % base64string.decode('utf-8'))
with urllib.request.urlopen(request, context=context) as url:
user_data = json.loads(url.read().decode())
for property in user_data['property']:
if property["_class"]=="hudson.tasks.Mailer$UserProperty":
return property["address"];

Localhost tunneling not working in Python code

I am using pagekite.net for tunneling localhost to run my Django app
Now my tunnel url is something like "myapp.pagekite.me".
Now, when I simply run it in browser, its working. But its not working when I simply perform a GET request in python code.
import requests
requests.get("http://myapp.pagekite.me")
>>>401
Assuming that the 401 in your sample refers to an HTTP Response Code:
401 is Unauthorized
If you sent credentials they are either incorrect, or of insufficient privilege.
If you didn't send credentials, you are required to authenticate. Have a look for the WWW-Authenticate header in the response.

Has anyone combined soap.py or suds with python-ntlm?

I'd like to replace an app's current (badly busted and crufty) cURL-based (cURL command-line based!) SOAP client with suds or soap.py. Trouble is, we have to contact an MS CRM service, and therefore must use NTLM. For a variety of reasons the NTLM proxy is a bit of a pain to use, so I'm looking into python-ntlm to provide that support.
Can suds or soap.py be made to use this authentication method? If so, how? If not, any other suggestions would be fantastic.
Edit
As noted below, suds already supports python-ntlm out of the box.
Suds was fixed to support it since 0.3.8.
Sources of python-suds-0.3.9\suds\transport\https.py says:
class WindowsHttpAuthenticated(HttpAuthenticated):
"""
Provides Windows (NTLM) http authentication.
#ivar pm: The password manager.
#ivar handler: The authentication handler.
"""
def u2handlers(self):
# try to import ntlm support
try:
from ntlm import HTTPNtlmAuthHandler
except ImportError:
raise Exception("Cannot import python-ntlm module")
handlers = HttpTransport.u2handlers(self)
handlers.append(HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(self.pm))
return handlers
Try with the following snippet as described here:
from suds.transport.https import WindowsHttpAuthenticated
ntlm = WindowsHttpAuthenticated(username='xx', password='xx')
client = Client(url, transport=ntlm)
Another approach would be to call your curl command during soap exceptions and then initiate a retry.
something like...
curl -x websenseproxy:8080 --ntlm -U domain\user:password --insecure https://blah.com/prod/webservice.asmx?WSDL
#insecure is used for self signed certs
You can use CNTLM as a local proxy service which will handle all the NTLM authentication calls. Then you just reference the local CNTLM proxy IP and port with no authentication using soapy or urllib2... whatever really.
I've yet to find a python library that deals with complex proxies well.

Categories