Authenticating with the REST web API of MS Dynamics CRM 2016 online - python

I'm trying to access the new REST API for building a server-to-server interface to integrate the CRM with other applications such as the web-shop etc.
I have tried both ways of getting an access-token from Azure AD:
Client credentials
import adal
token_response = adal.acquire_token_with_client_credentials(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
client_id,
secret
)
and user/password
import adal
token_response = adal.acquire_token_with_username_password(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
'interface#crm.my_domain.com',
'my_password'
)
In both cases, token_response gets a token-object, containing accessToken, refreshToken, expiresIn and so on. So I don't think there's an error up to this point.
Then I try to make a simple request to the web API:
headers = {'Authorization': '%s %s' % (token_response.get('tokenType'),
token_response.get('accessToken'))}
r = requests.get('https://domain.api.crm4.dynamics.com/api/data/v8.0/Product',
headers=headers)
This always returns a HTTP 401 - Unauthorized: Access is denied.
('WWW-Authenticate', 'Bearer error=invalid_token,
error_description=Error during token validation!,
authorization_uri=https://login.windows.net/eabcdefgh-1234-5678-a1b1-morerandomstuff/oauth2/authorize,
resource_id=https://domain.api.crm4.dynamics.com/')
The user that tries to make the request has Office-365-Administrator privileges and in the CRM has all manager roles and the administrator role. Which for my taste is even a bit much, but I read somewhere, that the user has to have office-365 admin privileges.
In the Azure AD there is an Application configured which has "delegated rights" to the CRM ("Access CRM Online as organization users").
What am I missing here? Or is my REST-get-request wrong?
Microsoft documentation for the new API is practically nonexistent - whenever you click some link you get docs for the older APIs (organization API etc.)

acquire_token_with_username_password has an optional parameter for specifying which resource you want access to:
resource (str, optional): The resource you are accessing. Defaults to
'https://management.core.windows.net/'.
You should thus be able to specify that you want access to CRM by adding resource as an argument to acquire_token_with_username_password:
token_response = adal.acquire_token_with_username_password(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
'interface#crm.my_domain.com',
'my_password',
resource='https://domain.crm4.dynamics.com'
)
This should give you a proper token for accessing CRM.
After getting the correct token, you also need to modify your Web API call slightly (from Product to products):
r = requests.get('https://domain.api.crm4.dynamics.com/api/data/v8.0/products',
headers=headers)

Related

Authenticate with AWS ALB / Cognito

I am trying to authorize with an ALB from python. As I understand the ALB looks for "AWSELBAuthSessionCookie" cookies before letting you to the website. I also see these cookies when logging into the application myself (using username and password). Question is how do I obtain the values of these cookies if I want to authenticate myself to the website/api from a python program. Has anybody done this before?
I had the exact same problem and could only make it work using an API Gateway since they allow authorization via JWT in the authorization header of the request. This can easily be done in Python, e.g.
import boto3
import requests
client = boto3.client(
"cognito-idp",
region_name="<aws region of the cognito app client>"
)
response = client.initiate_auth(
ClientId="<cognito app client ID>",
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={
"USERNAME": "<username>",
"PASSWORD": "<password>",
"SECRET_HASH": "<secret hash>",
},
)
token = response["AuthenticationResult"]["AccessToken"]
headers = {"Authorization": f"Bearer {token}"}
requests.get("<api gateway url>", headers=headers)
However, I also needed to allow authorization via the Cognito UI. Thus, I had to use both the ALB and API Gateway.
While this solved the issue of making my application available both from the browser (i.e. for humans) as well as from code (i.e. for machines), it introduced a lot of additional AWS components I had to use. And, as a disadvantegous side effect, the API has a request payload limit of 10MB that cannot be increased. This is another issue for me.
I know it's been a year, but if you've solved the issue, feel free to share your solution.

Spotify - get and change user data with Implicit Flow

I'm trying to implement a simple python client for Spotify api. According to the Spotify's Authorization Guide, the app can be authorized in two ways:
App Authorization: Spotify authorizes your app to access the Spotify Platform (APIs, SDKs and Widgets).
User Authorization: Spotify, as well as the user, grant your app permission to access and/or modify the user’s own data. For information about User Authentication, see User Authentication with OAuth 2.0. Calls to the Spotify Web API require authorization by your application user. To get that authorization, your application generates a call to the Spotify Accounts Service /authorize endpoint, passing along a list of the scopes for which access permission is sought.
CLIENT CREDENTIALS
My first attempt used the app authorization using the oauth2 module from Spotipy, because it requires no token passed, but only client id and client secret, which belong to the app developer.
client.py
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
class SpotifyWrapper(spotipy.Spotify):
def category_playlists(self, category, limit=50, offset=0):
return self._get('browse/categories/%s/playlists' % category,
limit=limit,
offset=offset)
def get_api_client():
# create a client authentication request
client_cred = SpotifyClientCredentials(
client_id=DevelopmentConfig.SPOTIFY_CLIENT_ID,
client_secret=DevelopmentConfig.SPOTIFY_CLIENT_SECRET
)
# create a spotify client with a bearer token,
# dynamically re-created if necessary
return SpotifyWrapper(auth=client_cred.get_access_token())
Then I would import and declare it here:
spotify_utilities.py
from app.resources.spotify.client import get_api_client
sp = get_api_client()
And in order to make requests and get user playlists, pass it like so:
def get_user_playlist(username, sp):
ids=[]
playlists = sp.user_playlists(username)
for playlist in playlists['items']:
ids.append(playlist['id'])
print("Name: {}, Number of songs: {}, Playlist ID: {} ".
format(playlist['name'].encode('utf8'),
playlist['tracks']['total'],
playlist['id']))
return ids
This works and will get user content, where the user is the app developer.
IMPLICIT FLOW
Now I want to move on to Implicit Flow, whereby the app asks ANY user who uses for access and scopes, and for that a token will be required.
Once I fetch the token using Javascript, I know I can use it to get user data hitting the API with simple requests:
GET_USER_PROFILE_ENDPOINT = 'https://api.spotify.com/v1/users/{user_id}'
GET_USER_PLAYLISTS_ENDPOINT = 'https://api.spotify.com/v1/users/{user_id}/playlists'
def get_user_profile(token, user_id):
url = GET_USER_PROFILE_ENDPOINT.format(id=user_id)
resp = requests.get(url, headers={"Authorization": "Bearer {}".format(token)})
print (len(resp.json()))
return resp.json()
def get_user_playlists(token, user_id):
url = GET_USER_PLAYLISTS_ENDPOINT..format(id=user_id)
resp = requests.get(url, headers={"Authorization": "Bearer {}".format(token)})
print (len(resp.json()))
return resp.json()
but in order to get (and change) user data first I need to use this token to fetch user ID.
Also, by the following example form Spotipy docs, user must provide his username at terminal:
if __name__ == '__main__':
if len(sys.argv) > 1:
username = sys.argv[1]
else:
print("Whoops, need your username!")
print("usage: python user_playlists.py [username]")
sys.exit()
token = util.prompt_for_user_token(username)
if token:
sp = spotipy.Spotify(auth=token)
playlists = sp.user_playlists(username)
After reading the docs from Spotify and Spotify, some things that are still not clear:
Is it possible to get this USER ID from passing the token only?
Must the app user necessarily provide his Spotify username via a form in a browser, besides authorizing the app when authentication is prompted?
Is it possible to tweak the wrapper above and implement a client which contemplates the parameters required for implicit flow? Would simply spotify = spotipy.Spotify(auth=token) work and get current usr data?
Also, by the following example form Spotipy docs, user must provide
his username at terminal:
That's because Spotipy caches tokens on disk. When no cache path is specified by the user the username simply gets appended to the files file extension as seen here. So the username specified is never being transmitted to any Spotify API endpoint.
1) Is it possible to get this USER ID from passing the token only?
Yes, using /v1/me instead of /v1/users/{user_id} will do exactly that assuming you are using an access token generated by Authorization Code flow or Implicit Grant flow.
2) Must the app user necessarily provide his Spotify username via a
form in a browser, besides authorizing the app when authentication is
prompted?
No, as seen in the first paragraph of my answer.
3) Is it possible to tweak the wrapper above and implement a client
which contemplates the parameters required for implicit flow? Would
simply spotify = spotipy.Spotify(auth=token) work and get current usr
data?
Spotipy seems to only use Authorization Code Flow right now. Due to you said you are
trying to implement a simple python client for Spotify api.
you should just implement Implicit Grant flow in your application. This has examples for all three Spotify authorization flows.

Python get info from API / Oauth Authentication

that is my first try with an API, said API being called OPS.
I would like to get information using the API (OAuth 2) within my python code.
The ressource URL is :
http://ops.epo.org/3.2/rest-services/register/{publication}/{EPODOC}/{EP2814089}/biblio
I also received :
Consumer Key: O220VlTQqAmodifiedsf0YeqgM6c
Consumer Secret Key: swWmodified3edjORU
The documentation states that:
OPS uses the OAuth framework for Authentication and Authorization. At this point in
time, only the “Client Credentials” flow is supported using a Consumer key and
Consumer secret.
The actual steps to follow are:
Step 1: Client converts Consumer key and Consumer secret to
Base64Encode(Consumer key:Consumer secret).
This should be done programmatically using the language you are developing the client
application in. For the purposes of this example, a public website was used to perform
this conversion.
By entering the colon separated Client credentials, an encoded response is generated.
This response is then be used for basic Authentication.
Step 2: Client requests an access token using Basic Authentication, supplying its
Consumer key and Consumer secret with base64Encoding over encrypted HTTPS
connection:
OPS authenticates the client credentials passed in the Authorization header using basic
authentication method.
If credentials are valid, OPS responds with a valid access token.
Step 3: Client accesses OPS resources with access token in authorization header
(bearer tokens) over encrypted HTTPS connection
I tried a few samples of code with requests but, until now, nothing worked.
The client credentials flow is described in the OAuth2 RFC-6749. The client id and secret are base64 encoded in a Basic authentication scheme as described in RFC-7617
You should be able to get a token using Python code like:
import requests
import base64
url = 'https://ops.epo.org/3.2/auth/accesstoken'
data = {"grant_type": "client_credentials"}
creds = base64.b64encode("O220VlTQqAmodifiedsf0YeqgM6c:swWmodified3edjORU".encode())
headers = {'Authorization': 'Basic ' + creds.decode('UTF-8'), 'Content-Type': 'application/x-www-form-urlencoded'}
response = requests.post(url, headers=headers, data=data)
access_token = response.json()["access_token"]
When using the previous response I can obtain a token. (Thanks a lot for your answer)
So I tried :
myUrl = 'http://ops.epo.org/3.2/rest-services/register/publication/EPODOC/EP2814089/biblio'
header = {'PRIVATE-TOKEN': myToken}
response = requests.get(myUrl, headers=header)
print(response.text)
but I obtained a 403 error.
I finally got a specific library to do the job :
EPO OPS Library
But I still don't know how to do it on my own...

How to write a python script to authenticate to Azure DevOps REST API and get the access token?

How can I authenticate to Azure DevOps REST API in a python script?
I found that there are 2 methods :
Using personal access token (PAT)
Using OAuth 2.0
I am using the second method. Followed the steps in this documentation:
https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops
I wrote this function to autherize to azure DevOps using OAuth 2.0:
def get_authenticated():
client_id = < my client ID as a string >
state = "user1"
scope = "vso.graph_manage%20vso.identity_manage%20vso.profile_write%20vso.project_manage%20vso.tokenadministration%20vso.tokens"
callback_URL = < Callback URL to my azure devops account >
# Azure DevOps Services authorization endpoint
Auth_URL = "https://app.vssps.visualstudio.com/oauth2/authorize?client_id=" + client_id + "&response_type=Assertion&state=" + state + "&scope=" + scope + "&redirect_uri=" + callback_URL
headers = {'Accept': 'application/json;api-version=1.0'}
print(Auth_URL)
response = requests.get(Auth_URL,headers = headers)
print(response)
print(response.status_code)
print(response.headers['content-type'])
response.raise_for_status()
But when calling this function, output I am getting is:
<Response [203]>
203
text/html; charset=utf-8
The auth URL is correct because when I tried to access the same URL in a browser it successfully redirects to a form to enter azure user credentials.
The expected behavior of the script is, when the auth_url is requested, Azure DevOps Services should ask the user to authorize. I think that should be done by prompting for username&password in terminal/via a browser.
I am totally new to python scripting and REST APIs.
Can someone help me by pointing out the faults in my code or pointing to some samples?
The http error 203 indicates that the returned metainformation is not a definitive set of the object from a server with a copy of the object, but is from a private overlaid web. In your code,you added headers = {'Accept': 'application/json;api-version=1.0'}, but in fact the content type should be application/x-www-form-urlencoded.
You can use some OAuth2 library for python to authenticate to Azure DevOps REST API, such as OAuthLib. It includes sevelral samples.
Also, you can refer to following topic, hope it is helpful for you.
Tutorial for using requests_oauth2

Youtube API request credentials

I created an python application that is using the Youtube api (so examples are in python, but doesn't really matter, the concepts should be the same). I managed to get it working where I can connect and make api calls. However, when I connect to the api, I have to define a flow that checks if a the credentials storage file exists. If it doesn't, then I have to manually sign in using the flow. After sign in the file (main.py-oauth2.json), is created with the token. I would like to be able to download the credentials without having to sign manually sign in. I was hoping there was a way to make a POST request for that token, like I have seen here, but I have been able to do this with Youtube api. Does anyone know how to implement the desired feature ?
main.py
flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE,
scope=YOUTUBE_UPLOAD_SCOPE,
message=MISSING_CLIENT_SECRETS_MESSAGE)
storage = Storage(OAUTH_CREDENTIALS)
credentials = storage.get()
if credentials is None or credentials.invalid:
# manual / UI login
credentials = run_flow(flow, storage, args)
Trying to use a google service account throws 401 errors on upload.
credentials = Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=YOUTUBE_UPLOAD_SCOPES)
if credentials is None or credentials.expired:
raise ValueError('Invalid credentials')
return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
credentials=credentials)
...
status, response = insert_request.next_chunk()
# <HttpError 401 "Unauthorized">
Evidence this can be done
The oauth2client.service_account.ServiceAccountCredentials class is
only used with OAuth 2.0 Service Accounts. No end-user is involved
for these server-to-server API calls, so you can create this object
directly without using a Flow object.
youtube api
Oauth flow docs
https://developers.google.com/identity/protocols/OAuth2#serviceaccount
The problem is that most YouTube data is private user data. Being that it is private user data you must be authenticated as a user who has access to the data in question in order to access it. To do that we use Oauth2 and login to our account and get an access token and a refresh token returned.
The access token can be used to request data from the Youtube Api, the refresh token can be used to request a new access token when ever the access token expires (After an hour)
Normally i would say that you should consider using a service account. Services accounts are dummy users who can be preconfigured with access to user data. Unfortunately the Youtube api does not support service accounts.
What you should be doing and what i have done a number of times in the past is to authenticate your code once. Get the refresh token and save it. In the future whenever you wish to run your application you simply use the refresh token to request a new access token and you will be able to access the api. You wont have to manually type your login and password and consent to the access anymore everything can be done in the background using the refesh token.
Note: You will need to watch it there are some cases that can cause a refresh token to expire but you shouldn't worry for the most part they are good for as long as you continue to use them regularly.
I am not a python dev but found this
from oauth2client import client, GOOGLE_TOKEN_URI
CLIENT_ID = "client_id"
CLIENT_SECRET = "client_secret"
REFRESH_TOKEN = "refresh_token"
credentials = client.OAuth2Credentials(
access_token = None,
client_id = CLIENT_ID,
client_secret = CLIENT_SECRET,
refresh_token = REFRESH_TOKEN,
token_expiry = None,
token_uri = GOOGLE_TOKEN_URI,
token_ id = None,
revoke_uri= None)
http = credentials.authorize(httplib2.Http())

Categories