Python machine-to-machine user level jwt authentication with box - python

My Python script needs to push files to Box every night, unattended. I've been reading as much as I can find about how machine-to-machine user-level authentication with Box in Python works, but I can't find any examples of performing the actual authentication in a Python script.
I found out how to encode what Box requires as a JWT payload here: JWT Claims. But, how do I use this JWT once I have it?
There is a very suggestive paragraph in the Box documentation about getting a user access token (JWT Authentication):
Your service will authenticate to Box by sending a JSON Web Token to the /token endpoint containing a JSON payload that includes the Client ID, enterprise_id or user_id, and additional claims, and sign it using the private key of an RSA keypair. Box then verifies the identity of your application using the public key specific to your application.
The problem is, I can't find any reference that tells me how to use the JWT once I have it. I'm pretty sure I make some call to https://api.box.com/oauth2/token, but how do I sign it with the private key, and what is the exact way of sending it in Python? E.g. do I use pycurl, or something else?
Once I have an access token I am able to authenticate using OAuth2, so that part is all right. There's just that piece in the middle I'm missing.
Please note I need to get a user token, not an enterprise-level token, so JWTAuth doesn't work for me.

You can do user-based authentication with JWTAUth. Instead of calling authenticate_instance, you use authenticate_app_user. Here is a code snippet:
from boxsdk import JWTAuth, Client
auth = JWTAuth(
client_id='CLIENT ID HERE',
client_secret='CLIENT SECRET HERE',
enterprise_id='USER_ID HERE',
jwt_key_id='JWT KEY HERE',
rsa_private_key_file_sys_path='PATH/TO/FILE',
rsa_private_key_passphrase=b'PASSPHRASE HERE'
)
access_token = auth.authenticate_app_user(type('',(object,),{"object_id": "USER_ID HERE"})())
client = Client(auth)
# ... etc
However, you still need to get your app authorized by a box admin for your enterprise.

Related

Django doesn't validate or see the JWT token from Azure

I used azure-ad-verify-token 0.2.1 on Django-rest backend to validate a jwt token from Microsoft Azure, where the user is authenticated on the frontend with React.
According to the documentation, this library should do everything on its own.
from azure_ad_verify_token import verify_jwt
azure_ad_app_id = 'my app id'
azure_ad_issuer = 'https://exampletenant.b2clogin.com/0867afa-24e7-40e9-9d27-74bb598zzzzc/v2.0/'
azure_ad_jwks_uri = 'https://exampletenant.b2clogin.com/exampletenant.onmicrosoft.com/B2C_1_app_sign_in/discovery/v2.0/keys'
payload = verify_jwt(
token='<AZURE_JWT_TO_VERIFY_HERE>',
valid_audiences=[azure_ad_app_id],
issuer=azure_ad_issuer,
jwks_uri=azure_ad_jwks_uri,
verify=True,
)
print(payload)
I don't understand the line token='<AZURE_JWT_TO_VERIFY_HERE>', how can I put the token there?
Authorization from Azure on React is successful, and I get a access jwt-token that I can extract:
token = request.headers['Authorization']
But I need to validate it and somehow insert it into a string token='<AZURE_JWT_TO_VERIFY_HERE>', but it doesn't recognize the request here.
How can I put a token= from the header?
And in general, is this the right way? Or am I missing something? Any help and hints would be very helpful and would be greatly appreciated. Or advise another library for token validation in Python.
azure-ad-verify-token This is used to verify the tokens received from azure ad.
You have to get auth tokens from azure using MSAL python library and the azure-ad-verify-token will then verify the token.
To retrieve the tokens, you will need MSAL python library, and it
will also take clientid and tenentd as arguments.
test_app=PublicClientApplication(client_id=client_id,authority="https://login.microsoftonline.com/"+tenant_id)
test_tokens=test_app.acquire_token_interactive(scopes=scopes)
Now you can take the token you just received and use it in theazure-ad-verify-token.
token=test_tokens['access_token']
Reference:
MSAL Python
Authenticate Python apps by using the Azure SDK for Python

Google Admin Directory API - Send a query via apiclient

I am retrieving a ChromeOS device MAC address via the Google Admin Directory API using the device's Serial Number as reference, and am making my calls through
apiclient.
service = discovery.build('admin', 'directory_v1', developerKey=settings.API_KEY)
Here are the calls available for ChromeOS devices; my issue is that I require a Device ID in order to execute the following:
service.chromeosdevices().get(customerId=settings.CID, deviceId=obtained_id, projection=None).execute()
I can send a GET query via the following format:
https://www.googleapis.com/admin/directory/v1/customer/my_customer/devices/chromeos?projection=full&query=id:" + serial + "&orderBy=status&sortOrder=ascending&maxResults=10", "GET")
... but I'm trying to avoid using OAuth2 and just use my API key. Passing the key in a GET request doesn't work either, as it still returns a "Login Required" notice.
How do I squeeze the above query into an apiclient-friendly format? The only option I found via the above calls was to request every device we have (via list), then sift through the mountain of data for the matching Serial number, which seems silly and excessive.
I did notice I could call apiclient.http.HttpRequests, but I couldn't find a way to pass the API key through it either. There's new_batch_http_request, but I can't discern from the docs how to simply pass a URL to it.
Thank you!
Got it!
You can't use just a key for Directory API queries, you need a Service account.
I'm using google-auth (see here) since oauth2client is deprecated.
You also need to:
Delegate the necessary permissions for your service account (mine has the role of Viewer and has scope access to https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly)
Delegate API access to it separately in the Admin Console (Security -> Advanced Settings -> Authentication)
Get your json client secret key and place it with your app (don't include it in your VCS)
Obtain your credentials like this:
credentials = service_account.Credentials.from_service_account_file(
settings.CLIENT_KEY,
scopes=settings.SCOPES,
subject=settings.ADMIN_USER)
where ADMIN_USER is the email address of an authorized Domain admin.
Then you send a GET request like so:
authed_session = AuthorizedSession(credentials)
response = authed_session.get(request_id_url)
This returns a Requests object you can read via response.content.
Hope it helps someone else!

python3 upload files to ondrive or sharepoint?

Anyone know if this is possible?
I just want to automate dropping some documents into my onedrive for business account.
I tried
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
from onedrivesdk.helpers.resource_discovery import ResourceDiscoveryRequest
redirect_uri = 'http://localhost:8080'
client_id = 'appid'
client_secret = 'mysecret'
discovery_uri = 'https://api.office.com/discovery/'
auth_server_url='https://login.live.com/oauth20_authorize.srf?scope=wl.skydrive_update'
#auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize',
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'
http = onedrivesdk.HttpProvider()
auth = onedrivesdk.AuthProvider(http,
client_id,
auth_server_url=auth_server_url,
auth_token_url=auth_token_url)
auth_url = auth.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
auth.authenticate(code, redirect_uri, client_secret, resource=resource)
# If you have access to more than one service, you'll need to decide
# which ServiceInfo to use instead of just using the first one, as below.
service_info = ResourceDiscoveryRequest().get_service_info(auth.access_token)[0]
auth.redeem_refresh_token(service_info.service_resource_id)
client = onedrivesdk.OneDriveClient(service_info.service_resource_id + '/_api/v2.0/', auth, http)
I registered an APP and got a secret and id. But when I ran this I got scope is invalid errors. Plus it tries to launch a webpage which isn't great for a command line kinda environment. I think this SDK might be outdated as well because originally this script had login.microsoftonline, but that wasn't reachable so I changed it to login.live.com.
I wrote this sample code you posted. You replaced the auth_server_URLwith the authentication URL for Microsoft Account authentication, which can only be used to access OneDrive (the consumer product). You need to continue using the login.microsoftonline.com URL to log into your OneDrive for Business account.
You are correct that this pops up a dialog. However, you can write a little supporting code so that only happens the first time you log into a particular app. Follow these steps (assuming you are using the default implementation of AuthProvider:
Use the sample code above up through the line auth.redeem_refresh_token()
The AuthProvider will now have a Session object, which caches the credentials of the current user and session. Use AuthProvider.save_session() to save the credentials for later.
Next time you start your app, use AuthProvider.load_session() and AuthProvider.refresh_token() to retrieve the previous session and refresh the auth token. This will all be headless.
Take note that the default implementation of SessionBase (found here) uses Pickle and is not safe for product use. Make sure to create a new implementation of Session if you intend to deploy this app to other users.
Onerive's website shows "Not Yet" on "OneDrive SDK for Python" to "OneDrive for Business"
https://dev.onedrive.com/SDKs.htm
The github sample codes did not work for me either, it tried to popup a window of authentication, but IE can not find the address:
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx9-9xx8-4xxf-bexx-1bc5xxxxbd0c&response_type=code
or removed all the "-" in client id
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx99xx84xxfbexx1bc5xxxxbd0c&response_type=code
Either way, I got the same result, IE did not show the popup with a line "This page can’t be displayed"

Migrating Python backend from Gitkit to to Firebase-Auth with python-jose for token verification

Over on GitHub a helpful Google dev told me that
to create a user session, your python backend server only needs a JWT
library to verify the Firebase Auth token (signature and audience) in
the request and extract the user info from the token payload.
I am having trouble with verifying the token.
This is where I'm at; In order to start the migration I proceeded as follows:
I added Firebase-Auth to the Android App, while still having Gitkit in the App until Firebase-Auth works. Now I have two sign-in buttons, one which signs in into Firebase and one for the "almost deprecated" Gitkit.
On firebase.com I imported the Google project into a new Firebase Project, so the user database is the same. I've already managed to use Firebase-Auth in the Android App, am able to log-in as a known user and I can successfully retrieve the token which I will need for my backend server by calling mFirebaseAuth.getCurrentUser().getToken(false).getResult().getToken(). It contains the same user_id as the GitKit token.
Now I'm attempting to replace the identity-toolkit-python-client library with python-jose. Since I'm currently not sending the Firebase token to the backend, but only the Gitkit token, I want to test this python-jose library on the Gitkit token.
On the backend, before calling GitKit.VerifyGitkitToken() i'm now printing out the results of jose.jwt.get_unverified_header() and jose.jwt.get_unverified_claims() in order to check if I get to see what I expect. The results are good, I am able to view the content of the Gitkit token just as expected.
My problem comes with the verification. I'm unable to use jose.jwt.decode() for verification because I don't know which key I need to use.
jose.jwt.decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None)
I know the algorithm from the header and an 'aud' field is also stored in the claims, if that is of any help.
Getting back to the engineers comment
verify the Firebase Auth token (signature and audience)
How do I do that with the info I have avaliable? I guess audience is the 'aud' field in the claims, but how do I check the signature?
Once I've removed the Gitkit dependency on the server, I will continue with the migration.
From what I've seen, the GitKit library apparently makes an "RPC" call to a Google server for verification, but I may be wrong.
So, which will be the key for Gitkit token verification as well as the one for the Firebase token verification?
The keys can be obtained
for Firebase at
https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com
and for Gitkit at
https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys
Using Googles oauth2client library makes the verification very easy.
But if you want to use python-jose instead of oauth2client, then you first need to convert the PEM certificate into an RSA public key (update: this issue got fixed, for Firebase this is now handled by the library, scroll down to the end in the GitHub link preceding this comment. Not sure about Gitkit). This public key is then the key that needs to be used. And the audience should not be extracted from the JWT header, but hard coded into the source code, where in Firebase the audience is the project id and in Gitkit it is one of the OAuth 2.0 client IDs which can be found in the Google Developer Console Credentials section.
The kid in the JWT header is used to select the appropriate certificate which will be used to obtain the key used to perform the verification.
# firebase
# target_audience = "firebase-project-id"
# certificate_url = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com'
# gitkit
target_audience = "123456789-abcdef.apps.googleusercontent.com" # (from developer console, OAuth 2.0 client IDs)
certificate_url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys'
response = urllib.urlopen(certificate_url)
certs = response.read()
certs = json.loads(certs)
print "CERTS", certs
print ''
print ''
# -------------- verify via oauth2client
from oauth2client import crypt
crypt.MAX_TOKEN_LIFETIME_SECS = 30 * 86400 # according to https://github.com/google/identity-toolkit-python-client/blob/master/identitytoolkit/gitkitclient.py
print "VALID TOKEN", crypt.verify_signed_jwt_with_certs(idtoken, certs, target_audience)
print ''
print ''
# -------------- verify via python-jose
from jose import jwt
unverified_header = jwt.get_unverified_header(idtoken)
print "UNVERIFIED HEADER", unverified_header
print ''
print ''
unverified_claims = jwt.get_unverified_claims(idtoken)
print "UNVERIFIED CLAIMS", unverified_claims
print ''
print ''
from ssl import PEM_cert_to_DER_cert
from Crypto.Util.asn1 import DerSequence
pem = certs[unverified_header['kid']]
der = PEM_cert_to_DER_cert(pem)
cert = DerSequence()
cert.decode(der)
tbsCertificate = DerSequence()
tbsCertificate.decode(cert[0])
rsa_public_key = tbsCertificate[6]
print "VALID TOKEN", jwt.decode(idtoken, rsa_public_key, algorithms=unverified_header['alg'], audience=target_audience)

Get access token for Soundcloud desktop application?

Toying around with Soundclouds SDK for Python with an impact of TKinter as GUI. Now I want to generate a access token for each user so that I could access more API-endpoints.
I have created an applicaton in Soundclouds Developer portal with a link to my callback.
There is nothing corresponding to generating a access token for an desktop application. Only for server-side application. I tried this code below:
import soundcloud
# create client object with app credentials
client = soundcloud.Client(client_id='YOUR_CLIENT_ID',
client_secret='YOUR_CLIENT_SECRET',
redirect_uri='REDIRECT_URL')
# redirect user to authorize URL
redirect client.authorize_url()
I have set my keys, and redirect_uri as the callback on my webserver. When I run my python file from the terminal, I get this:
File "token.py", line 9
redirect client.authorize_url()
^
SyntaxError: invalid syntax
Using Python 2.7.5+
What is causing this? I want to generete my access token and print in later on.
The solution might be that I need to create an instance of an web browser window, make the user accept the app using Soundcloud connect. The I grab the url and sort out the "code" in the url. Exchanges the code against an access-token and stores it inside a text-file. So that I could grab it later on.
A simple way of obtaining an access token is by first authenticating via the User Credentials flow, which exchanges your username and password for an access token:
client = soundcloud.Client(client_id = 'CLIENT_ID',
client_secret = 'CLIENT_SECRET',
username = 'USERNAME',
password = 'PASSWORD')
print client.access_token
try:
redirect(client.authorize_url())

Categories