I'm trying to build a (what I thought would be) a simple script in Python to download Microsoft's Azure's rate card using the Billing API.
token_response = adal.acquire_token_with_client_credentials(
'https://login.microsoftonline.com/' + TENANT_ID,
CLIENT-ID,
CLIENT-KEY)
access_token = token_response.get('accessToken')
endpoint = "https://management.azure.com/subscriptions/[SUBSCRIPTION-ID]/providers/Microsoft.Commerce/RateCard?api-version=2015-06-01-preview&$filter=OfferDurableId eq 'MS-AZR-0003P' and Currency eq 'GBP' and Locale eq 'en-GB' and RegionInfo eq 'GB'"
headers = {"Authorization": 'Bearer ' + access_token}
out = requests.get(endpoint,headers=headers)
json_output = out.json()
print son_output
The query seems to be executing correctly. Authorisation seems to go ok, and I'm getting a 200 OK message response. But the output is empty: {u'value': []}. I've tried different OfferIDs, different $filter strings and now it's driving me mad...
I wonder if perhaps I haven't delegated suitable permissions, but I've created an Application attached to Active Directory, generated a key, and delegated permissions to Windows Azure Service Management? And if that was the problem, wouldn't I receive an error?
Happy to hear alternative ways of doing the same thing, but Python is all I really know...
Please make sure that the user account you're using to interact with the billing and usage API has access to the Azure Subscription. In your case, you're using a Service Principal so please make sure that this user is assigned a role in the Subscription.
To assign a Service Principal a role, you may find this link helpful: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.
When it comes to assigning role, since this user only needs to read the data, its better to go with Reader role so that inadvertent changes can't be made to your Azure Subscription.
Related
For over a year, I have connected to Salesforce using the simple_salesforce package in order to pull some data from various objects and load it to a data lake.
I have used the authentication method using username / password / security token.
client = Salesforce(
username="****************",
password="*************",
security_token="****************"
)
On the 1st of February came the enforcement of multi factor auth. Starting on that day, I consistently hit the same error over and over.
[{'message': 'This session is not valid for use with the REST API', 'errorCode': 'INVALID_SESSION_ID'}]
After some research, I tried to add a permission set with API Enabled and then API Only user. Result: still the same error, but now I am locked out of the UI.
Has anyone else encountered similar issues and could point me towards the right resources, please? Thanks!
MFA shouldn't matter for API access according to https://help.salesforce.com/s/articleView?id=000352937&type=1 (Ctrl+F "API"), it's probably something else your admin did.
Username, password+token sounds like you're use SOAP login method.
See if you can create a "connected app" in SF to use the OAuth2 login method, more natural for REST API. I wrote a bit about it in https://stackoverflow.com/a/62694002/313628. In the connected app you should be able to allow API access, even full if needed. No idea if Simple has natural place for the keys though, it's bit rubbish if you'll have to craft raw http requests yourself.
Simple's documentation also mentions using JWT to log in (and that requires connected app anyway), basically instead of username + pass you go username + certificate + the fact admin preauthorised this user... You'll be fine until certificate expires.
The text part of https://gist.github.com/booleangate/30d345ecf0617db0ea19c54c7a44d06f can help you with the connected app creation; sample code's probably not needed if you're going with Simple
I only want to pull data from a list called dataacq within a group (or site?) called prod within the domain (or root site?) tenant.sharepoint.com (or tenant-my.sharepoint.com ?) and put it into a DataFrame.
I have an issue with the token gotten through app.acquire_token_silent.
Microsoft documentation is not comprehensible because it's too heavy and has little workable cookbooks/working examples (as can be seen by my numerous question marks). Also it seems they want to centralize all their APIs into graph.microsoft.com, yet there is no warning that tenant.sharepoint.com/sites/prod/_api/ is going to be discontinued.
I have gotten the following permissions from the azure portal for my app.
I don't believe I need all of them, but I am not sure. I just want to read a list. So is only Microsoft Graph > Sites.read.All necessary? Or is it Sharepoint > Allsites.Read ?
I know I both have an "app only" permission and a "signed in user" permission.
I did download the "quickstart" examples and I did read https://msal-python.readthedocs.io/en/latest/ . Although a token was successfully pulled using app.acquire_token_silent, using the returned token always throws some error whatever scope ('https://microsoft.sharepoint-df.com/.default' or 'https://graph.microsoft.com/.default') or API domain (graph.microsoft.com or tenant.sharepoint.com) I am using into a request:
{'error_description':
"Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown."}
{'error': {'code': 'AccessDenied',
'message': 'Either scp or roles claim need to be present in the token.',
'innerError': {'date': '2021-02-19T08:05:16',
'request-id': '01efc071-18e6-4006-8780-f771419ebe3e',
'client-request-id': '01efc071-18e6-4006-8780-f771419ebe3e'}}}
On the other hand, there is an API developer testing portal. When I am copying the token given in this portal into my python code, both scope/API domains work.
This is e.g. an example that works with copying & pasting the token from the portal, but not working with the token issued by the app.acquire_token_silent method:
r = requests.get( # Use token to call downstream service
fr'https://graph.microsoft.com/v1.0/sites/root:/sites/prod:/lists/{list_id}/items?expand=fields(select=Created))',
headers={'Authorization': 'Bearer ' + result['access_token'],},)
So the issue is with this app.acquire_token_silent method or the configuration file. But the returned response seems alright:
{'token_type': 'Bearer',
'expires_in': 3599,
'ext_expires_in': 3599,
'access_token': '...'}
What am I missing?
According to the code r = requests.get..... you provided in your description, it seems you use the microsoft graph api to implement it. If you use this api, you should use https://graph.microsoft.com/.default as scope to get the access token. And you can copy the access token to this page, decode the token and check if the permissions are included in it.
And according to the screenshot of "API permissions" tab of your registered app, please also do grant admin consent operation for the permission Sites.Read.All although it shows not required admin consent.
================================Update===============================
It seems the method acquire_token_silent() acquire the access token by client credential flow. So we should add the "Application" type permission but not "Delegated" permission in registered app.
In my company, I need to upload Excel files on OneDrive.
We have a 365 Business Plan and every employee has an own 365 account, but I want to maintain just one repository for merged files and avoid to share the same repos account among all, so I prefer to implement a "access without user" through client credentials flow.
The first problem that I've met is the authorization: when I try to authorize the app by /adminconsent endpoint, it fails because my client account is not an administrator :-( So I've tried to use another account, a simple Microsoft Account (for that I've made a new registration of the app in the Application Portal) but when I try to authorize the app I receive this error:
"AADSTS50020: We are unable to issue tokens from this API version for a Microsoft account. Please contact the application vendor as they need to use version 2.0 of the protocol to support this."
What's wrong?
As an alternative, I've thought to continue with 365 Business employee accounts, create a folder with a tech account and share it, but when using Graph Explorer with an employee account and make the request
/me/drive/sharedWithMe
I receive just the shared folder but without the content
Here the code (I'm using the requests_oauthlib Python module):
In the beginning, I initialize the class object
client = BackendApplicationClient(client_id=config.CLIENT_ID)
self.oauth = OAuth2Session(
client.client_id,
scope=config.SCOPES,
redirect_uri='https://me.local/allowed')
then I make a request for authorization_url
auth_base = 'https://login.microsoftonline.com/common/adminconsent'
self.authorization_url, state = self.oauth.authorization_url(
auth_base,
state="12345")
return self.authorization_url
and the request for the token
return self.oauth.fetch_token(
token_url=https://login.microsoftonline.com/common/oauth2/v2.0/token',
client_id=config.CLIENT_ID,
scope="https://graph.microsoft.com/.default",
client_secret=config.CLIENT_SECRET,
authorization_response='https://me.local/authorized'
)
You need to be a tenant administrator in order to consent application only access (where you only use client id and secret). However, you can use alternative flows such as Resource Owner Credentials Grant and On-Behalf-Of Grant which requires you to have the credentials of a user with relevant permissions.
You can also read about those flows in my post:
Getting Access Token for Microsoft Graph Using OAuth REST API.
Regarding the message with "version 2.0" - it may be caused by a mixup between version 1 and version 2 of the Microsoft OAuth API. Version 1 is only meant for organization users (users which sit inside azure active directory) and version 2 support Microsoft accounts as well. You can read more about the difference between the two versions in here. Make sure you use one of those versions for the entire process (creating the app, assigning and consenting permissions, and requesting an access token). Mixing between the two versions may not work.
I'm trying to figure out how to authenticate and create an entry on quickbooks online through Python. Currently, when I try to click auth link in their API Explorer, I get 404 page.
What I'm trying to do is creating invoice through Python. However, it seems like their documentation is not complete. I contacted their support, and I haven't heard from them yet.
The python-quickbooks library is probably the correct choice now, as it is a "complete rework of quickbooks-python". It has pretty comprehensive instructions on getting and using the auth keys, though I wouldn't call it "simple", since the process is by definition somewhat complex. The instructions are "for Django", but the Django-specific code simply gets parameters out of a URL string.
We're using it to great effect, because the syntax is as easy as:
auth_client = AuthClient(
client_id = CLIENT_ID # from QB website
,client_secret = CLIENT_SECRET # from QB website
,environment = 'sandbox' # or 'production'
,redirect_uri = REDIRECT_URI
)
client = QuickBooks(
auth_client = auth_client
,refresh_token = REFRESH_TOKEN
,company_id = COMPANY_ID
)
account = Account.get(qbid, qb=client) # qbid can be retrieved from the AccountList
return account.CurrentBalance
This library will get the job done https://github.com/HaPsantran/quickbooks-python
It works in JSON so you would construct the Invoice based off of docs at https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/030_entity_services_reference/invoice using the JSON examples.
The library doesn't support sandbox mode** so if you are going to use the development consumer key and secret than you would change this code.
base_url_v3 = "https://quickbooks.api.intuit.com/v3"
to
base_url_v3 = "https://sandbox-quickbooks.api.intuit.com/v3"
while in that mode.
** Sandbox mode only applies currently to U.S. QBO
Having written a lot of the module #Minimul mentions — with a very helpful start by simonv3, who figured out how to get it working first and then I just built on it — I am fairly confident that this will not support the oauth workflow of getting the request token, prompting the user to authenticate out of band, and then getting and storing the access token. It presumes you already have an access token.
Simon (or another Python developer) may be able to comment on how he gets the access token with Python, and if so, it'd be great if he (or they) could add it to the module for all to enjoy.
I had this same problem. I just figured it out and posed the step-by-step process here:
python with Quickbooks Online API v3
Hope this helps.
I looked at the existing python clients for quickbooks and found them to be either outdated or not having all the features. So i created a new python client for quickbooks which can be found at https://pypi.python.org/pypi/quickbooks-py
I am trying to use simple_salesforce to query salesforce data with Python. I am using my username and password, which I am 100% sure is correct. I got the org ID from logging into Salesforce and looking at my company profile. It's only a 15-digit ID. I am specifically using an orgID to avoid using a security token as I don't know what it is. What am I doing wrong?
Code:
from simple_salesforce import Salesforce
sf = Salesforce(instance_url='https://na1.salesforce.com', session_id='')
sf = Salesforce(password='password', username='email', organizationId='15 digit org id')
Output:
File "C:\Python27\lib\site-packages\simple_salesforce\api.py", line 100, in __init__
proxies=self.proxies)
File "C:\Python27\lib\site-packages\simple_salesforce\login.py", line 124, in SalesforceLogin
code=except_code, message=except_msg))
simple_salesforce.login.SalesforceAuthenticationFailed: INVALID_LOGIN: Invalid username, password, security token; or user locked out.
I wrote most of simple-salesforce (although not the organizationId part, as I don't have an IP-whitelisted account to test against)
The standard/vanilla/regular/99% of users should use version is the simple username, password, security_token method.
So something like this
from simple_salesforce import Salesforce
sf = Salesforce(username='nick#nickcatalano.com', password='nickspassword', security_token='tokenemailedtonick')
By far the most confusing part is the security_token part (and was the part I got snagged with.) It turns out the Security Token is emailed to you after a successful password reset. So if you go into your salesforce account and reset your password, I believe you'll end up with an email with the subject salesforce.com security token confirmation which will contain a Security Token in the email. That's your security_token.
To be honest, the security_token kwarg is more a convenience than anything. In the normal email/password/token flow that most users rely on what is actually being sent is email as the login and {password}{security_token} as the password. I believe you could concat that yourself and just pass in a email and password kwarg if you want, but I figured forcing people to concat the password and token themselves would get go against the simple part of simple-salesforce
There is a way to log in with simple-salesforce with only a username and password. No security token required:
from simple_salesforce import Salesforce, SalesforceLogin
session_id, instance = SalesforceLogin(username='<user>', password='<pass>')
sf = Salesforce(instance=instance, session_id=session_id)
# Logged in! Now perform API actions, SOQL queries, etc.
sf.query_all('<soql>')
Explanation
All examples using simple-salesforce begin with a call to the Salesforce constructor to log in. This constructor accepts either an existing session ID, or authentication credentials to log in and make a new session. When logging in, it calls the lower-level SalesforceLogin function to do the real work, but interestingly SalesforceLogin does not enforce the same constraints on its arguments—it issues the correct SOAP call to log in with just a username and password, without requiring a token or organization ID.
Using this trick, we call SalesforceLogin directly, obtain the new session ID, then pass it directly into the Salesforce constructor. From that point on, we are able to make authenticated API requests.
Note
The version of simple-salesforce on PyPI (i.e. pip install simple-salesforce) is very outdated with the simple-salesforce GitHub repository. The latest version supports additional login parameters like domain for login with custom domains. To get the latest version, use
pip install --upgrade https://github.com/simple-salesforce/simple-salesforce/archive/master.zip
(Pip-installing from zip is faster than using git+ssh:// or git+https://, as noted in this answer.)
Edit
How will resetting my password show me what the token is?
It just will. If user has ever before requested the security token (which is sent to you via email - so you need to have access to the email address associated with your user) - every subsequent password reset will result with new token being generated and emailed to you. On top of that, once you're logged in to the system (to the web version, not via API) you will have an option to reset your token (and again, this will send you an email).
It's like you haven't read or tried anything we have written!
Looking for an answer drawing from credible and/or official sources.
https://help.salesforce.com/htviewhelpdoc?id=user_security_token.htm
https://help.salesforce.com/HTViewSolution?id=000004502
https://help.salesforce.com/HTViewSolution?id=000003783
And from the library's documentation:
https://github.com/neworganizing/simple-salesforce
To login using IP-whitelist Organization ID method, simply use your
Salesforce username, password and organizationId
This. If your IP address is whitelisted - you don't need the token. If it isn't - you NEED to generate the token. Period.
Original answer
I'm not familiar with that Python library but... Go to Salesforce -> Setup -> My personal infromation and check login history. if it contains stuff like "failed: security token required" then you're screwed and you will have to use the security token.
I'm not aware of any bypass that uses org id (I've connected via API from PHP, Java, C#... so I'd be very surprised if that Python library had some magical way to bypass it. You probably are used to passing a session id that assumes you're already authenticated and have a valid session.
Another option would be to check your IP and add it to trusted IP ranges (it's an option in the setup). It's useful when for example whole office has same static IP; less useful if you're working from home.
If that's also a no-go - you might want to look for libraries that use OAuth2 instead of regular SOAP API to authenticate.
Although this is kinda late, somebody searching for this very same issue may be helped as to what I did.
I struggled by adding the company ID as well, but the problem here is, unless you're a self-service user, the company ID can be blank.
sf = Salesforce(password='password', username='email', organizationId='')
As other users mentioned, make sure you're using IP-White listing or it will not work.
A security token is an automatically generated key that you must add to the end of your password in order to log into Salesforce from an untrusted network. For example, if your password is mypassword, and your security token is XXXXXXXXXX, then you must enter mypasswordXXXXXXXXXX to log in. Security tokens are required whether you log in via the API or a desktop client such as Connect for Outlook, Connect Offline, Connect for Office, Connect for Lotus Notes, or the Data Loader.
To reset your security token:
At the top of any Salesforce page, click the down arrow next to your name. From the menu under your name, select Setup or My Settings—whichever one appears.
From the left pane, select one of the following:
If you clicked Setup, select My Personal Information | Reset My Security Token.
If you clicked My Settings, select Personal | Reset My Security Token.
Click the Reset Security Token button. The new security token is sent via email to the email address on your Salesforce user record.
If you ip is whitelisted / trusted and you still get invalid login not using the token, You MUST include the security_token='' in the connection string for it to work.
sf = Salesforce(username='USERNAME', password='PASSWORD', security_token='')
A security token is required to login.
Whenever your password is reset, your security token is also reset.
If you do not have a token and cannot reset it.
Try changing your password.
Thanks.
I was able to test that this was working with my security token against a developer org with no issues. This was all done as a standard user with no administrator privileges. Using the OrgId just failed out.
By resetting my password I received a new security token.
username = login for your instance.
password = your password
The code below should get you logged in.
from simple_salesforce import Salesforce
sf = Salesforce(username='username',password='password', security_token='whatever came in reset password')