MS Graph authentication using python - python

Trying to write a Python code where I would like to access my calendar and retrieve my schedule.
Not able to get through the authentication phase.
Seen and tested many examples, but all require running a local server where I browse locally and need to click a button and then enter my credentials.
Aiming to perform all of this inside my Python code.

You can achieve this one of two ways:
Using Resource Owner Password Credential flow - This allows you to pass the username and password to Azure AD. Gotcha's here are if there's any extra thing on the auth flow (consent, MFA, password reset) you'll just get a failure.
Using Client Credentials flow - This one requires admin consent. Also, you have to be really careful about this one as this client will have access to ALL info about all users. This should only be used with secure clients, not clients that other users have access to.
Here's a code snippet that showcases both of these:
import adal
import requests
tenant = "contoso.com"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
username = "foo#contoso.com"
password = "mypassword"
authority = "https://login.microsoftonline.com/" + tenant
RESOURCE = "https://graph.microsoft.com"
context = adal.AuthenticationContext(authority)
# Use this for Client Credentials
#token = context.acquire_token_with_client_credentials(
# RESOURCE,
# client_id,
# client_secret
# )
# Use this for Resource Owner Password Credentials (ROPC)
token = context.acquire_token_with_username_password(RESOURCE, username, password, client_id);
graph_api_endpoint = 'https://graph.microsoft.com/v1.0{0}'
# /me only works with ROPC, for Client Credentials you'll need /<UsersObjectId/
request_url = graph_api_endpoint.format('/me')
headers = {
'User-Agent' : 'python_tutorial/1.0',
'Authorization' : 'Bearer {0}'.format(token["accessToken"]),
'Accept' : 'application/json',
'Content-Type' : 'application/json'
}
response = requests.get(url = request_url, headers = headers)
print (response.content)

Will try the above...
What I did to solve this was using this example - https://developer.microsoft.com/en-us/graph/docs/authorization/app_only
The problem was asking for the right permissions for the app in Azure.

Related

Microsoft Graph API Python SDK "Insufficient privileges to complete the operation."

i'm trying to get user data from AAD using Microsoft Graph API Python SDK.
App registration that i have in company tenant has the followiing API permissions:
I'm using the following piece of code to get user's details from AAD:
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
credentials = ServicePrincipalCredentials(
client_id="appClientId",
secret="appClientSecret",
resource="https://graph.windows.net",
tenant = 'companyTenant'
)
tenant_id = 'companyTenantId'
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
user = graphrbac_client.users.get("myUserObjectId")
And get "azure.graphrbac.models.graph_error_py3.GraphErrorException: Insufficient privileges to complete the operation."
I'm using Python 3.10.5 and my app service should be able to get data of any user from AAD.
What am i doing wrong here?
Looks like the resource are trying to reach out is incorrect , https://graph.windows.net is used when you want to connect to AAD graph , please check the docs for more info - https://learn.microsoft.com/en-us/previous-versions/azure/ad/graph/howto/azure-ad-graph-api-operations-overview.
Could you please try by using the resource = graph.microsoft.com .
graph.microsoft.com is correct endpoint for graph .
please see the doc - https://learn.microsoft.com/en-us/graph/use-the-api
Hope this helps
Thanks
I tried to reproduce the same in my environment and got below results:
I created one Azure AD application and granted API permissions like below:
When I ran the same code as you, I got same error as below:
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
credentials = ServicePrincipalCredentials(
client_id="appClientId",
secret="appClientSecret",
resource="https://graph.windows.net",
tenant = 'companyTenantId'
)
tenant_id = 'companyTenantId'
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
user = graphrbac_client.users.get("myUserObjectId")
Response:
I agree with Vicky kumar that graph.windows.net is deprecated and you need to change/migrate to https://graph.microsoft.com.
But the libraries that you are using won't support this resource that results error as below:
Your current library azure.graphrbac only supports resource as graph.windows.net that needs AAD graph permissions:
To resolve the error, you can make use of below code by installing urllib3 library beforehand:
import urllib3
uri = "https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token"
payload= {
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'login.microsoftonline.com',
'client_id': '3de439c4-570d-4534-bxxb-e3axxx5d', #Your AppID
'scope': 'https://graph.microsoft.com/.default',
'client_secret': 'T2Y8Q~wYQxxxxxxxxxxOODUtFxajo', #Your client secret
'grant_type': 'client_credentials'
 }
http = urllib3.PoolManager()
response = http.request('POST', uri, payload)
my_dict = eval(response.data)
token = f"{my_dict['token_type']} {my_dict['access_token']}"
#print(token)
uri5 = 'https://graph.microsoft.com/v1.0/users/myUserID'
payload5 = {'Authorization':token,'Host':'graph.microsoft.com','ConsistencyLevel':'eventual'}
https = urllib3.PoolManager()
response5 = http.request('GET', uri5, headers=payload5)
print(response5.data)
When I ran the above code, I got the user details successfully as below:
Okay, so it came out that the issue was that i was using wrong SDK, the one that i've used was working with the AAD graph but i need Microsoft.Graph (if the permission that i've granted to the app registration would be of the AAD Graph type - then it would work, but since AAD Graph cannot be assigned anymore to the app registration since it is deprecated i've assigned Microsoft.Graph permission).
So the fix was to use another SDK from MS (that is currenty in preview) and it worked for me, here is the code:
from azure.identity import ClientSecretCredential
from msgraph.core import GraphClient
credential = ClientSecretCredential(tenant_id='tenantId',client_secret='appRegClientId',client_id='appRegClientSecret')
client = GraphClient(credential=credential)
result = client.get('/users') # gets all users
# result = client.get('/users/userObjectId') # gets a certain user by it's objectId
# result = client.get('/users/email') # gets a certain user by it's email address
print(result.json())

Azure Application connect to OneDrive Authentication Issue

I need to programmatically connect to onedrive and download some files. I am using the following code which works fine. However, when another user of my Tenant tries it, he gets a message that he must use MFA. Could this be because I am a user administrator and he is not? or is this an issue with the permissions of the application I have registered? MFA cannot be touched.
import logging
import requests
import json
import msal
import requests
CLIENT_ID = ''
TENANT_ID = ''
AUTHORITY_URL = 'https://login.microsoftonline.com/{}'.format(TENANT_ID)
RESOURCE_URL = 'https://graph.microsoft.com/'
API_VERSION = 'v1.0'
USERNAME = '' #Office365 user's account username
PASSWORD = ''
SCOPES = ['Sites.ReadWrite.All','Files.ReadWrite.All'] # Add other scopes/permissions as needed.
#Creating a public client app, Aquire a access token for the user and set the header for API calls
cognos_to_onedrive = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY_URL)
token = cognos_to_onedrive.acquire_token_by_username_password(USERNAME,PASSWORD,SCOPES)
headers = {'Authorization': 'Bearer {}'.format(token['access_token'])}
r = requests.get('https://graph.microsoft.com/v1.0/me/drives/{drive_id}/root:/DropFileOut', headers=headers).json()
print(r)
So, somehow the MFA was disabled for my ID. The solution would be to add the user to the MFA Policy exceptions, in this case.

Python Simple OAuth access

I am trying to access a proprietary web service that has Oauth access. I have no experience with that and I am trying to use ;'requests' and 'requests_oauthlib'.
I have the following access data:
auth_url = 'https://foobar.url.com/oauth/token'
data_url = 'https://foobar.url.com/rest/v1/aname/overview'
API_Key = 'Basic Y2xpY2tleS1hcHA6YXBwLWFjY2Vzcw=='
username ='ausername' password = 'apassword' grant_type = 'password'
The API key should be put in the Header in the Field 'Authorization'
Any examples that fit the above would be appreciated. I cannot find any match with the requests_oauth docs.
Well, requests_oauthlib hasn't provided a way to issue password grant type token. You can try Authlib, the client of Authlib has a similar API, check how to authenticate with password grant type:
https://docs.authlib.org/en/latest/client/oauth2.html#oauth2session-for-password

How do I do OAuth2 style authentication to google w/o user interaction (to replace the abandoned gdata.docs.client.DocsClient API)

Can anyone provide sample code that would be a working replacement for the code that used the now defunct gdata.docs.client API:
import gdata.docs, gdata.docs.client, gdata
pw = file('pw','r').read()
client = gdata.docs.client.DocsClient()
client.ClientLogin('username#domain.org',pw,None)
Until yesterday, that code worked, but google has finally axed the depreceated ClientLogin API (https://developers.google.com/identity/protocols/AuthForInstalledApps).
In reading over the documentation for the OAuth 2.0 library, it looks like the process is supposed to involve user interaction to finish the authentication process, but I need a script to run in a cronjob on a regular basis without user involvement (we update various parts of our google site using a script on a cronjob).
Current Answer:
Hard-coding authentication for the docs API was possible, but that API is also discontinued, so here's the way to do it for the new, preferred DRIVE API.
credentials = OAuth2WebServerFlow(
client_id = "CLIENT ID",
client_secret = "CLIENT SECRET",
scope = 'https://www.googleapis.com/auth/drive',
user_agent = "HTTP",
access_token = "ACCESS TOKEN",
refresh_token = "REFRESH TOKEN",
)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
# Now it gets ugly
# The authorize method below changes the "request" method of http_client
# Not at all sure why I had to fake this to make it work, but I did --
# I believe this must get set somewhere in the normal gdrive flow which
# we're circumventing with this method. You can see the code that
# requires this fake client here:
# https://code.google.com/p/gdata-python client/source/browse/src/gdata/gauth.py
# at ~line 1324
Class FakeClient:
request = 'Fake'
http.http_client = FakeClient()
http = credentials.authorize(http)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
Class FakeClient:
request = 'Fake'
http.http_client = FakeClient()
http = credentials.authorize(http)
In order to get those credentials, you can use the standard OAuth method described in the google documentation and then just dig into the variables to find all the right information. Here's some code I wrote myself to print it all out:
if NEED_NEW_CREDENTIALS:
CLIENT_ID = 'ID'
CLIENT_SECRET = 'SECRET'
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE,
redirect_uri=REDIRECT_URI)
authorize_url = flow.step1_get_authorize_url()
print 'go to the following link in your browser: ' + authorize_url
code = raw_input('Enter verification code: ').strip()
credentials = flow.step2_exchange(code)
print 'Hey buddy -- you better hardcode these new credentials in.'
print 'client_id = "%s"'%credentials.client_id
print 'client_secret = "%s"'%credentials.client_secret
print 'scope = "%s"'%OAUTH_SCOPE
print 'user_agent = "%s"'%credentials.user_agent
print 'access_token = "%s"'%credentials.token_response['access_token']
print 'refresh_token = "%s"'%credentials.token_response['refresh_token']
Ok, I found a better solution in the PyDrive library, which already wraps all of this nicely up for you:
http://pythonhosted.org/PyDrive/
If you set PyDrive to store credentials, it will only make you go through the browser once, then remember the credentials automatically.
You'll need to set up a settings.yaml file that looks like this to work with it:
save_credentials: true
save_credentials_backend: file
save_credentials_file: credentials.txt
client_config_file: client_secrets.json
Once you've done that, and installed your secret in client_secrets.json, the login process is as simple as:
from pydrive.auth import GoogleAuth
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
Then you're working with the PyDrive API, which is pretty friendly to use and well documented here: http://pythonhosted.org/PyDrive/oauth.html#customizing-authentication-with-settings-yaml

Working with the Box.com SDK for Python

I am trying to get started with the Box.com SDK and I have a few questions.
from boxsdk import OAuth2
oauth = OAuth2(
client_id='YOUR_CLIENT_ID',
client_secret='YOUR_CLIENT_SECRET',
store_tokens=your_store_tokens_callback_method,
)
auth_url, csrf_token = oauth.get_authorization_url('http://YOUR_REDIRECT_URL')
def store_tokens(access_token, refresh_token):
# store the tokens at secure storage (e.g. Keychain)
1)What is the redirect URL and how do I use it? Do I need to have a server running to use this?
2)What sort of code to I need in the store_tokens method?
The redirect URL is only required if you're runng a Web application that needs to respond to user's requests to authenticate. If you're programtically authenticating, you can simply set this as http://localhost. In a scenario where you require the user to manually authenticate, the redirect URL should invoke some function in your web app to store and process the authentication code returned. Do you need a server running? Well, if you want to do something with the authentication code returned, the URL you specify should be under your control and invoke code to do something useful.
Here's an example of what the store_tokens function should look like. It should accept two parameters, access_token and refresh_token. In the example below, the function will commit these to a local store for use when the API needs to re-authenticate:
From here:
"""An example of Box authentication with external store"""
import keyring
from boxsdk import OAuth2
from boxsdk import Client
CLIENT_ID = 'specify your Box client_id here'
CLIENT_SECRET = 'specify your Box client_secret here'
def read_tokens():
"""Reads authorisation tokens from keyring"""
# Use keyring to read the tokens
auth_token = keyring.get_password('Box_Auth', 'mybox#box.com')
refresh_token = keyring.get_password('Box_Refresh', 'mybox#box.com')
return auth_token, refresh_token
def store_tokens(access_token, refresh_token):
"""Callback function when Box SDK refreshes tokens"""
# Use keyring to store the tokens
keyring.set_password('Box_Auth', 'mybox#box.com', access_token)
keyring.set_password('Box_Refresh', 'mybox#box.com', refresh_token)
def main():
"""Authentication against Box Example"""
# Retrieve tokens from secure store
access_token, refresh_token = read_tokens()
# Set up authorisation using the tokens we've retrieved
oauth = OAuth2(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
access_token=access_token,
refresh_token=refresh_token,
store_tokens=store_tokens,
)
# Create the SDK client
client = Client(oauth)
# Get current user details and display
current_user = client.user(user_id='me').get()
print('Box User:', current_user.name)
if __name__ == '__main__':
main()
I suggest taking a look at the OAuth 2 tutorial. It will help give a better understanding of how OAuth works and what the various parameters are used for.
The redirect URL is set in your Box application's settings:
This is the URL where Box will send an auth code that can be used to obtain an access token. For example, if your redirect URL is set to https://myhost.com, then your server will receive a request with a URL that looks something like https://myhost.com?code=123456abcdef.
Note that your redirect URI doesn't need to be a real server. For example, apps that use a WebView will sometimes enter a fake redirect URL and then extract the auth code directly from the URL in the WebView.
The store_tokens callback is optional, but it can be used to save the access and refresh tokens in case your application needs to shutdown. It will be invoked every time the access token and refresh token changes, giving you an opportunity to save them somewhere (to disk, a DB, etc.).
You can then pass in these tokens to your OAuth2 constructor at a later time so that your users don't need to login again.
If you're just testing, you can also pass in a developer token. This tutorial explains how.
This is the most basic example that worked for me:
from boxsdk import Client, OAuth2
CLIENT_ID = ''
CLIENT_SECRET = ''
ACCESS_TOKEN = '' # this is the developer token
oauth2 = OAuth2(CLIENT_ID, CLIENT_SECRET, access_token=ACCESS_TOKEN)
client = Client(oauth2)
my = client.user(user_id='me').get()
print(my.name)
print(my.login)
print(my.avatar_url)

Categories

Resources