am trying to get authenticated with a Python to OneDrive (personal, not for business). I've registered an app in Azure AD, got client id and secret (turned to be not needed, as I am using 'desktop' app which is public and not using a secret), and Using browser and postman, managed to obtain an access token.
trying 2 different options now, both with no luck.
import hidden
from hidden import oauthr
import requests
import json
client_secret = oauthr()["consumer_secret"]
client_id = oauthr()["consumer_key"]
scope = 'Files.ReadWrite.All'
redirect_uri = "http://localhost/auth-response"
code = oauthr()["code"]
token = oauthr()["token_secret"]
RootFolder = 'https://api.onedrive.com/v1.0/drive/root:/'
r = requests.get(RootFolder, headers = {'Authorization': 'Bearer ' + token})
content=json.loads(r.content)
print(content)
This one results in: {'error': {'code': 'unauthenticated', 'message': 'Authentication failed'}}
Second thing I try is MSAL:
import hidden
from hidden import oauthr
import requests
import json
from msal import PublicClientApplication
client_secret = oauthr()["consumer_secret"]
client_id = oauthr()["consumer_key"]
scopes = ['https://graph.microsoft.com/.default']
redirect_uri = "http://localhost/auth-response"
code = oauthr()["code"]
token = oauthr()["token_secret"]
user = input("user: ")
pwd = input("pwd: ")
print('scopes are: ', scopes, ' the data type is: ',type(scopes) )
app = PublicClientApplication(
client_id,
authority="https://login.microsoftonline.com/UsadyProgimnasia.onmicrosoft.com")
result = None
flow = app.initiate_device_flow(scopes = scopes)
accounts = app.get_accounts()
if accounts:
# If so, you could then somehow display these accounts and let end user choose
print("Pick the account you want to use to proceed:")
for a in accounts:
print(a["username"])
# Assuming the end user chose this one
chosen = accounts[0]
# Now let's try to find a token in cache for this account
result = app.acquire_token_silent([scopes], account=chosen)
if not result:
print('So no suitable token exists in cache. Let\'s get a new one from Azure AD')
#result = app.acquire_token_by_username_password(user, pwd, scopes)
result = app.acquire_token_by_authorization_code(code, scopes, redirect_uri=redirect_uri, nonce=None, claims_challenge=None)
#result = app.acquire_token_by_device_flow(flow, claims_challenge=None)
if "access_token" in result:
print(result["access_token"]) # Yay!
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bug
This gives a invalid_grant AADSTS70000121: The passed grant is from a personal Microsoft account and is required to be sent to the /consumers or /common endpoint.
Would appreciate an advice, chaps
Regards
Related
I am very new to Python and Stackoverflow. I am working on connecting my Google Ads account with Python to automate few standard charts creation and email them to my team members. Please help me resolve this as I had not been able to find an answer to it upon Googling either.
Let me know if I have missed out on any info which might provide more context to the question here.
I have been using the steps as mentioned by #msaniscalchi. Created client ID and client Secret from https://console.developers.google.com and created googleads.yaml file in the same directory as the generate_refresh_token.py. When I run the script with respective client ID and client Secret values, I am getting "invalid syntax" error. I have verified my multiple times my client secret and ID values multiple times.
"""Generates refresh token for AdWords using the Installed Application flow."""
import argparse
import sys
from google_auth_oauthlib.flow import InstalledAppFlow
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError
# Your OAuth2 Client ID and Secret. If you do not have an ID and Secret yet,
# please go to https://console.developers.google.com and create a set.
DEFAULT_CLIENT_ID = 609XXXXXXX22-58mbhXXXXXXXXXXXXXXXXXX6ri.apps.googleusercontent.com
DEFAULT_CLIENT_SECRET = 7uO7XXXXXXXXXXXXXX7dKBAP
# The AdWords API OAuth2 scope.
SCOPE = u'https://www.googleapis.com/auth/adwords'
# The redirect URI set for the given Client ID. The redirect URI for Client ID
# generated for an installed application will always have this value.
_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
parser = argparse.ArgumentParser(description='Generates a refresh token with '
'the provided credentials.')
parser.add_argument('--client_id', default=DEFAULT_CLIENT_ID,
help='Client Id retrieved from the Developer\'s Console.')
parser.add_argument('--client_secret', default=DEFAULT_CLIENT_SECRET,
help='Client Secret retrieved from the Developer\'s '
'Console.')
parser.add_argument('--additional_scopes', default=None,
help='Additional scopes to apply when generating the '
'refresh token. Each scope should be separated by a comma.')
class ClientConfigBuilder(object):
"""Helper class used to build a client config dict used in the OAuth 2.0 flow.
"""
_DEFAULT_AUTH_URI = 'https://accounts.google.com/o/oauth2/auth'
_DEFAULT_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token'
CLIENT_TYPE_WEB = 'web'
CLIENT_TYPE_INSTALLED_APP = 'installed'
def __init__(self, client_type=None, client_id=None, client_secret=None,
auth_uri=_DEFAULT_AUTH_URI, token_uri=_DEFAULT_TOKEN_URI):
self.client_type = client_type
self.client_id = client_id
self.client_secret = client_secret
self.auth_uri = auth_uri
self.token_uri = token_uri
def Build(self):
"""Builds a client config dictionary used in the OAuth 2.0 flow."""
if all((self.client_type, self.client_id, self.client_secret,
self.auth_uri, self.token_uri)):
client_config = {
self.client_type: {
'client_id': self.client_id,
'client_secret': self.client_secret,
'auth_uri': self.auth_uri,
'token_uri': self.token_uri
}
}
else:
raise ValueError('Required field is missing.')
return client_config
def main(client_id, client_secret, scopes):
"""Retrieve and display the access and refresh token."""
client_config = ClientConfigBuilder(
client_type=ClientConfigBuilder.CLIENT_TYPE_WEB, client_id=client_id,
client_secret=client_secret)
flow = InstalledAppFlow.from_client_config(
client_config.Build(), scopes=scopes)
# Note that from_client_config will not produce a flow with the
# redirect_uris (if any) set in the client_config. This must be set
# separately.
flow.redirect_uri = _REDIRECT_URI
auth_url, _ = flow.authorization_url(prompt='consent')
print('Log into the Google Account you use to access your AdWords account '
'and go to the following URL: \n%s\n' % auth_url)
print('After approving the token enter the verification code (if specified).')
code = input('Code: ').strip()
try:
flow.fetch_token(code=code)
except InvalidGrantError as ex:
print('Authentication has failed: %s' % ex)
sys.exit(1)
print('Access token: %s' % flow.credentials.token)
print('Refresh token: %s' % flow.credentials.refresh_token)
if __name__ == '__main__':
args = parser.parse_args()
configured_scopes = [SCOPE]
if not (any([args.client_id, DEFAULT_CLIENT_ID]) and
any([args.client_secret, DEFAULT_CLIENT_SECRET])):
raise AttributeError('No client_id or client_secret specified.')
if args.additional_scopes:
configured_scopes.extend(args.additional_scopes.replace(' ', '').split(','))
main(args.client_id, args.client_secret, configured_scopes)
When I run the above code, I am getting the "Invalid Syntax" error highlighting at the numeric part of the Client ID and Secret.
Syntax error screenshot attached here
Editor Highlighter screenshot attached here
I want to upload files and create folders on OneDrive with Python. So i copied the code from the OnDrive GitHub GitHub, registered my App at Azure, copied the ID and created an secret. So far so good.
But now, if i run my code. The Browser opens asking for the permission to login automatically, agreed and then i get this error:
Exception: invalid_request
I think it has something to do with the redirect_uri because if i copy this into my browser i cant access it.
Here is my code:
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
redirect_uri = 'http://localhost:8080/'
client_secret = 'The secret i created on Azure'
scopes=['wl.signin', 'wl.offline_access', 'onedrive.readwrite']
client = onedrivesdk.get_default_client(
client_id='The ID Azure created for me', scopes=scopes)
auth_url = client.auth_provider.get_auth_url(redirect_uri)
#this will block until we have the code
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
client.auth_provider.authenticate(code, redirect_uri, client_secret)
I also tried it with an Proxy:
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
from onedrivesdk.helpers import http_provider_with_proxy
redirect_uri = 'http://localhost:8080'
client_secret = 'Secret created with Azure'
client_id = 'ID id got from Azure'
scopes=['wl.signin', 'wl.offline_access', 'onedrive.readwrite']
client = onedrivesdk.get_default_client(client_id, scopes=scopes)
auth_url = client.auth_provider.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
proxy = {
'http': 'http://localhost:8888',
'https': 'https://localhost:8888'
}
http = http_provider_with_proxy.HttpProviderWithProxy(proxy, verify_ssl=True)
auth = onedrivesdk.AuthProvider(http, client_id, ['onedrive.readwrite'])
client = onedrivesdk.OneDriveClient(redirect_uri, auth, http)
f = onedrivesdk.Folder()
i = onedrivesdk.Item()
i.name = 'New Folder'
i.folder = f
returned_item = client.item(drive='me', id='root').children.add(i)
That gives me this error message:
RuntimeError: Session must be authenticated
before applying authentication to a request.
Your code works - e.g. it sends the info you want to send. However the credentials you have entered will of course return an invalid request - you're trying to connect to azure with:
client_id: 'The ID Azure created for me'
Which I'm pretty sure doesn't exists. The issue is you need an account & pass your script those (valid) account informations to connect to it.
I found a piece of code on Azure documentation that allows getting credentials without MFA. But I'm wondering if is possible to use it to connect to PowerBI API.
The piece of code that I'm using is:
import adal
import requests
from msrestazure.azure_active_directory import AADTokenCredentials
def authenticate_client_key():
authority_host_uri = 'https://login.microsoftonline.com'
tenant = 'tenant'
authority_uri = authority_host_uri + '/' + tenant
resource_uri = 'https://management.core.windows.net/'
client_id = 'clientid'
client_secret = 'client-secret'
context = adal.AuthenticationContext(authority_uri, api_version=None)
mgmt_token = context.acquire_token_with_client_credentials(resource_uri, client_id, client_secret)
credentials = AADTokenCredentials(mgmt_token, client_id)
return credentials
source: https://azure.microsoft.com/en-us/resources/samples/data-lake-analytics-python-auth-options/
According to the code written on PowerShell, the aim is to insert the access_token into the header of the following POST request
POST https://api.powerbi.com/v1.0/myorg/groups/me/datasets/{dataset_id}/refreshes
Source:https://powerbi.microsoft.com/en-us/blog/announcing-data-refresh-apis-in-the-power-bi-service/
I have tried to use the credentials into the POST request, but seems is not working.
I have tried
url = 'https://api.powerbi.com/v1.0/myorg/groups/me/datasets/datasetid/refreshes'
requests.post(url,data=mgmt_token)
Is it possible to merge this two codes?
Regards,
You can use the pypowerbi package to refresh Power BI datasets or you can check how to do it yourself by inspecting the code. https://github.com/cmberryau/pypowerbi
pip install pypowerbi
import adal
from pypowerbi.client import PowerBIClient
# you might need to change these, but i doubt it
authority_url = 'https://login.windows.net/common'
resource_url = 'https://analysis.windows.net/powerbi/api'
api_url = 'https://api.powerbi.com'
# change these to your credentials
client_id = '00000000-0000-0000-0000-000000000000'
username = 'someone#somecompany.com'
password = 'averygoodpassword'
# first you need to authenticate using adal
context = adal.AuthenticationContext(authority=authority_url,
validate_authority=True,
api_version=None)
# get your authentication token
token = context.acquire_token_with_username_password(resource=resource_url,
client_id=client_id,
username=username,
password=password)
# create your powerbi api client
client = PowerBIClient(api_url, token)
# Refresh the desired dataset (dataset and group IDs can be taken from the browser URL)
client.datasets.refresh_dataset(dataset_id='data-set-id-goes-here',
notify_option='MailOnCompletion',
group_id='group-id-goes-here')
Your code for acquiring an access token looks ok, but to use it with Power BI REST API, you must change resource_uri to be https://analysis.windows.net/powerbi/api.
When making a request to Power BI REST API, you must add Authorization header with value Bearer {accessToken}, where {accessToken} is the token acquired. I can't write in python, but you should do something like this:
headers = {'Authorization': 'Bearer ' + accessToken, 'Content-Type': 'application/json'}
url = 'https://api.powerbi.com/v1.0/myorg/groups/me/datasets/datasetid/refreshes'
requests.post(url, headers=headers)
(of course, you need to replace datasetid with actual value in url).
For example, here is how it can be done in C#:
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
string resourceUri = "https://analysis.windows.net/powerbi/api";
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
string powerBIApiUrl = $"https://api.powerbi.com/v1.0/myorg/datasets/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/refreshes";
AuthenticationContext authContext = new AuthenticationContext(authorityUri, new TokenCache());
var authenticationResult = await authContext.AcquireTokenAsync(resourceUri, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));
var accessToken = authenticationResult.AccessToken;
var request = WebRequest.Create(powerBIApiUrl) as HttpWebRequest;
request.KeepAlive = true;
request.Method = "POST";
request.ContentLength = 0;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
using (Stream writer = request.GetRequestStream())
{
var response = (HttpWebResponse)request.GetResponse();
}
I'm attempting to call the Spotify API and have set up an app/got my client ID and Secret. Here's an example of my code (with specifics blocked out):
import spotipy
import spotipy.util as util
from spotipy.oauth2 import SpotifyClientCredentials
cid ="xx"
secret = "xx"
username = "xx"
client_credentials_manager = SpotifyClientCredentials(client_id=cid, client_secret=secret)
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
scope = 'user-library-read playlist-read-private'
token = util.prompt_for_user_token(username,scope,client_id='http://localhost:8888/callback/',client_secret='http://localhost:8888/callback/',redirect_uri='http://localhost:8888/callback/')
if token:
sp = spotipy.Spotify(auth=token)
else:
print("Can't get token for", username)
cache_token = token.get_access_token()
sp = spotipy.Spotify(cache_token)
currentfaves = sp.current_user_top_tracks(limit=20, offset=0, time_range='medium_term')
print(currentfaves)
I've made sure my URL is exactly the same as what's registered in my Spotify app development page, and I've added the client ID, redirect URIs and client Secret keys to my environment variables.
So far a separate tab opens up (https://accounts.spotify.com/authorize?client_id=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&scope=playlist-read-private+user-library-read) but I'm only getting 'INVALID_CLIENT: Invalid client' on that page. What can I do/change to make this work?
token = util.prompt_for_user_token(username,scope,client_id='http://localhost:8888/callback/',client_secret='http://localhost:8888/callback/',redirect_uri='http://localhost:8888/callback/')
Is there a typo in here, since you copied the redirect uri also as the client id and secret?
I've managed to setup an API Gateway secured with Cognito. The unauthenticated user role has an access policy that should grant it access to the gateway. I've also managed to use boto3 to retrieve an identity ID from the pool and obtain the associated open ID token, as well as the associated secret and access keys.
How do I now make a call to the gateway using these credentials? Is there a way to use boto3 to handle signing a request to a particular method on the API?
My code is based largely on the questioner's own answer, but I've tried to make it clearer where all the values come from.
import boto3
import requests
from requests_aws4auth import AWS4Auth
# Use 'pip install boto3 requests requests-aws4auth' to get these
region_name = 'ap-southeast-2' # or 'us-west-1' or whatever
# 12 decimal digits from your AWS login page
account_id = '123456789012'
# I've only found this in the sample code for other languages, e.g. JavaScript
# Services→Cognito→Manage Federated Identities→(your-id-pool)→Sample code
identity_pool_id = 'ap-southeast-2:fedcba98-7654-3210-1234-56789abcdef0'
# Create a new identity
boto3.setup_default_session(region_name = region_name)
identity_client = boto3.client('cognito-identity', region_name=region_name)
identity_response = identity_client.get_id(AccountId=account_id,
IdentityPoolId=identity_pool_id)
# We normally wouldn't log this, but to illustrate:
identity_id = identity_response['IdentityId']
print ('identity_id:', identity_id) # good idea not to log this
# Get the identity's credentials
credentials_response = identity_client.get_credentials_for_identity(IdentityId=identity_id)
credentials = credentials_response['Credentials']
access_key_id = credentials['AccessKeyId']
secret_key = credentials['SecretKey']
service = 'execute-api'
session_token = credentials['SessionToken']
expiration = credentials['Expiration']
# Again, we normally wouldn't log this:
print ('access_key_id', access_key_id)
print ('secret_key', secret_key)
print ('session_token', session_token)
print ('expiration', expiration)
# The access_key_id will look something like 'AKIABC123DE456FG7890', similar to
# Services→IAM→Users→(AWS_USER_NAME)→Security credentials→Access key ID
# Get the authorisation object
auth = AWS4Auth(access_key_id, secret_key, region_name, service,
session_token=session_token)
current_app['auth'] = auth
# Just an illustration again:
print ('auth: %(service)s(%(date)s) %(region)s:%(access_id)s' % auth.__dict__)
# We'll use that object to send a request to our app. This app doesn't
# exist in real life, though, so you'll need to edit the following quite
# heavily:
# Services→Cognito→Manage your User Pools→(your-user-pool)→Apps→App name
app_name = 'my-app-name'
api_path = 'dev/helloworld'
method = 'GET'
headers = {}
body = ''
url = 'https://%s.%s.%s.amazonaws.com/%s' % (app_name, service, region_name,
api_path)
response = requests.request(method, url, auth=auth, data=body, headers=headers)
The following code (and the requests-aws4auth library) did the job:
import boto3
import datetime
import json
from requests_aws4auth import AWS4Auth
import requests
boto3.setup_default_session(region_name='us-east-1')
identity = boto3.client('cognito-identity', region_name='us-east-1')
account_id='XXXXXXXXXXXXXXX'
identity_pool_id='us-east-1:YYY-YYYY-YYY-YY'
api_prefix='ZZZZZZZZZ'
response = identity.get_id(AccountId=account_id, IdentityPoolId=identity_pool_id)
identity_id = response['IdentityId']
print ("Identity ID: %s"%identity_id)
resp = identity.get_credentials_for_identity(IdentityId=identity_id)
secretKey = resp['Credentials']['SecretKey']
accessKey = resp['Credentials']['AccessKeyId']
sessionToken = resp['Credentials']['SessionToken']
expiration = resp['Credentials']['Expiration']
print ("\nSecret Key: %s"%(secretKey))
print ("\nAccess Key %s"%(accessKey))
print ("\nSession Token: %s"%(sessionToken))
print ("\nExpiration: %s"%(expiration))
method = 'GET'
headers = {}
body = ''
service = 'execute-api'
url = 'https://%s.execute-api.us-east-1.amazonaws.com/dev/helloworld' % api_prefix
region = 'us-east-1'
auth = AWS4Auth(accessKey, secretKey, region, service, session_token=sessionToken)
response = requests.request(method, url, auth=auth, data=body, headers=headers)
print(response.text)
Next code is working really well.
Hope to help:
from pprint import pprint
import requests
from pycognito import Cognito
USER_POOL_ID = 'eu-central-1_XXXXXXXXXXX'
CLIENT_ID = 'XXXXXXXXXXXX'
CLIENT_SECRET = 'XXXXXXXXXXX'
u = Cognito(USER_POOL_ID,CLIENT_ID, client_secret=CLIENT_SECRET, username='cognito user name')
u.authenticate('cognito user password')
id_token = u.id_token
headers = {'Authorization': 'Bearer ' + id_token}
api_url = 'https://XXXXXXXXXXX.execute-api.eu-central-1.amazonaws.com/stage/XXXXXXXXXXX'
r = requests.get(api_url, headers=headers)
pprint(dict(r.headers))
print(r.status_code)
print(r.text)
Here is an example from our public docs: http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
Cognito creds are no different than any other temporary creds, and the signing process is also the same. If you want to move back to Python the example above should be good, or I would guess that there are third-party libraries out there to do the signature for you.
identity_pool_id how to get
If you have not federated pool which could give you "identity_pool_id" ,
execution code below will give you identity_pool_id
import boto3
boto3.setup_default_session(
aws_access_key_id='AKIAJ7TBC72BPWNEWIDQ',
aws_secret_access_key='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',
region_name='ap-southeast-2')
client = boto3.client('cognito-identity')
response = client.list_identity_pools(MaxResults=3,)
print("IdentityPoolId-- ", response)