Can't get client-credentials access token to authorize Power BI - azure

I'm trying to use the Power BI REST API, using an access token acquired with the "client credentials" method, but I keep getting 403 Forbidden on my requests.
My code follows the pattern demonstrated in this AzureAD sample. In fact, to isolate this problem, I'm running that sample code (with my own values in the parameters.json, of course):
{
expiresIn: 3599,
tokenType: 'Bearer',
expiresOn: Tue Sep 01 2015 16:56:07 GMT-0500 (CDT),
resource: '00000002-0000-0000-c000-000000000000',
accessToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8xM2QxNzIwNC0wZGU2LTQ1NzQtOTgzYS05NjFhYjk0M2M3Y2UvIiwiaWF0IjoxNDQxMTQwNjcwLCJuYmYiOjE0NDExNDA2NzAsImV4cCI6MTQ0MTE0NDU3MCwidmVyIjoiMS4wIiwidGlkIjoiMTNkMTcyMDQtMGRlNi00NTc0LTk4M2EtOTYxYWI5NDNjN2NlIiwib2lkIjoiYzM1ZWQyYTktYTYzZS00YzAwLThmYmYtY2FlYjlmZjYwMjYwIiwic3ViIjoiYzM1ZWQyYTktYTYzZS00YzAwLThmYmYtY2FlYjlmZjYwMjYwIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvMTNkMTcyMDQtMGRlNi00NTc0LTk4M2EtOTYxYWI5NDNjN2NlLyIsImFwcGlkIjoiNDQ2Y2Y5OTItMDQzYS00YjgxLWJhYzQtY2RlZWYyNGFhNzFjIiwiYXBwaWRhY3IiOiIxIn0.YTGJfdW1wP09bDHwwsv3FPAmEpmQdc_kifvgY-1KjhkZWANfYtd050wfeZdNgMUeSPZyFdWnoBjnJ4xrlDtnsADwV1Grr6TXYcymPLofbY-xy0cjyvzxTmM11DJ9XN8A4tkgvK0jtR-YyIjPw5EKJSKyeEbD9U3mWsE_gu7IzKzXl8e-dfVAqRYS6WHZy6_0FaNmppPDls5s_QIPOHofFSiWVISw41Mz0fQnP2QEGyceOCvKYJtrUOCDwfVuwFS-gSLmYvEGOJfmIjftP3srda0JPirVzBeU0IFJJ1KW81kE5cfKw1KkBB04VVetRUs_7HqloYaKKiTybauhXAodRQ',
isMRRT: true,
_clientId: '[snip]',
_authority: 'https://login.windows.net/[snip]'
}
When I use that access token in a curl request, as follows, I get a 403:
curl -vv -X GET https://api.powerbi.com/v1.0/myorg/datasets -H"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8xM2QxNzIwNC0wZGU2LTQ1NzQtOTgzYS05NjFhYjk0M2M3Y2UvIiwiaWF0IjoxNDQxMTQwNjcwLCJuYmYiOjE0NDExNDA2NzAsImV4cCI6MTQ0MTE0NDU3MCwidmVyIjoiMS4wIiwidGlkIjoiMTNkMTcyMDQtMGRlNi00NTc0LTk4M2EtOTYxYWI5NDNjN2NlIiwib2lkIjoiYzM1ZWQyYTktYTYzZS00YzAwLThmYmYtY2FlYjlmZjYwMjYwIiwic3ViIjoiYzM1ZWQyYTktYTYzZS00YzAwLThmYmYtY2FlYjlmZjYwMjYwIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvMTNkMTcyMDQtMGRlNi00NTc0LTk4M2EtOTYxYWI5NDNjN2NlLyIsImFwcGlkIjoiNDQ2Y2Y5OTItMDQzYS00YjgxLWJhYzQtY2RlZWYyNGFhNzFjIiwiYXBwaWRhY3IiOiIxIn0.YTGJfdW1wP09bDHwwsv3FPAmEpmQdc_kifvgY-1KjhkZWANfYtd050wfeZdNgMUeSPZyFdWnoBjnJ4xrlDtnsADwV1Grr6TXYcymPLofbY-xy0cjyvzxTmM11DJ9XN8A4tkgvK0jtR-YyIjPw5EKJSKyeEbD9U3mWsE_gu7IzKzXl8e-dfVAqRYS6WHZy6_0FaNmppPDls5s_QIPOHofFSiWVISw41Mz0fQnP2QEGyceOCvKYJtrUOCDwfVuwFS-gSLmYvEGOJfmIjftP3srda0JPirVzBeU0IFJJ1KW81kE5cfKw1KkBB04VVetRUs_7HqloYaKKiTybauhXAodRQ"
Wondering if that curl request was flawed somehow, I snooped out an access token “the wrong way” via browser webtools, and the above works fine, returning a 200 and a JSON response listing my datasets.
I did also notice that the return code is 403 (forbidden), not 401 (unauthorized), so I wondered if the authorization was okay but the permissions on the Power BI side were wrong. But I also get 403 when I use any garbage text for the access token (e.g., Authorization: Bearer foo), so I discarded that theory.
So. I think I have a valid test, and I’m getting what I think is a valid access token (from that client-credentials-sample.js code), but it’s still not working. What am I missing?

With the assistance of some Microsoft folks (thanks, Jon Gallant & Josh Caplan), I've learned that authenticating with an OAuth client-credentials flow, as I was doing with that JavaScript sample, provides insufficient access. To use Power BI, authentication needs to be based on a particular user.
I tried using:
the similar JavaScript sample username-password-sample.js
a resource value of https://analysis.windows.net/powerbi/api (thanks, slugslog)
adding username and password to the parameters.json
That got me closer, but I was still getting a 400 response: "error_description":"AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'. …".
A hack to the adal-node library (hardcoding the client secret, i.e., oauthParameters[OAuth2Parameters.CLIENT_SECRET] = "my-client-secret"; after line 217 of token-request.js) was enough to get back an access token which works in the Authorization header for my original curl call.
Of course hardcoding that value in there isn't my final solution. I don't plan to use the adal-node library, anyway. But as far as this proof-of-concept for this authentication case goes, that's the answer I came to.

Make sure that your app that you registered with AAD has the read write all datasets permission. That should solve the problem.

This is not an answer but one step forward in the debug process. I think the resource for which the token is requested should be "https://analysis.windows.net/powerbi/api". I've seen these in multiple references; one of them is linked below. Even after changing this, I still get a 403. As the OP mentioned if we use the accessToken from the powerBI portal, everything works.

So I tried this with my own app, the following command works (for me):
curl -vv -X GET https://api.powerbi.com/v1.0/myorg/datasets -H"Authorization: Bearer ey....qqqq"
BTW, the extra "v" after -v seems redundant.
So what I can conclude is that your application is missing the required permissions to call Power BI's APIs.
One thing you might try is grab one of our samples, create a new application in AAD for it, and then see if the authorization token works for it. Here's a good one to try: https://github.com/PowerBI/Integrate-a-tile-into-an-app

Related

Docusign error unauthorized_client when trying to generate the access token

I am using the Docusign REST API collection. Following the videos to set up and get access token. I successfully get the code from this URL:
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature&client_id={clientID}&redirect_uri={URL}
I take the code returned, place it into my {codeFromURL} variable. I click on submit. I get an unauthorized_client error.
I verified the integration key, secret key were correctly encoded and used in the header authorization.
I searched everywhere. I can not figure out how to fix and get past this error. I am hoping someone on hear will be of some help.
Here is the snippet shown by postman. I replaced some of the values with asterisks for privacy.
POST /oauth/token HTTP/1.1
Host: account-d.docusign.com
Authorization: Basic Y2VmMDkxODktOWU4Yi00YzZhL******
Content-Type: application/x-www-form-urlencoded
Content-Length: 708
code=eyJ0eXAiOiJNVCIsImFsZyI6IlJTMjU2I*******&grant_type=authorization_code
Possible causes of this error can be:
"1- Valid integration key and Secret Key were replaced in Authorization: "Basic encodedBase64(integrationKey:SecretKey)" so it's the actual values you obtained from your DocuSign Developer Account. (the way you had the quotes suggest you have this as a string value without the actual values encoded).
2- The code is valid for 2 minutes only.
3- The same IK that was used to obtain the code is used in the header."
If the problem persisted after you verified these 3 possible root cause, thanks to open a ticket support to Docusign, in order we could make further research based on your Ids

Source of oauth_token_secret for Evernote Authentication

I am working with the Evernote Python SDK, and proceeding through the Oauth workflow description here.
http://dev.evernote.com/doc/articles/authentication.php
How do I get a oauth_token_secret? I have my consumer secret, but don't see how to get the oauth_token_secret.
To retrieve an access token, I believe I will need to use the "get_access_token" function. One of the required arguments there is the oauth_token_secret.
https://github.com/evernote/evernote-sdk-python/blob/master/lib/evernote/api/client.py
I have the other pieces required (oauth_token, oauth_verifier).
I think you can leave that blank.
https://discussion.evernote.com/topic/18710-access-token-secret-returning-blank/

Spotify API Token Scope Issue

I have been at this for sometime now and wanted to see if anyone had and idea of what I could be doing wrong. What I am trying to do is add a song to a playlist using the provided Spotify Web APIs. According to the documentation on this https://developer.spotify.com/documentation/web-api/reference/playlists/add-tracks-to-playlist/ I need to establish the scope of the user.
"adding tracks to the current user’s private playlist (including collaborative playlists) requires the playlist-modify-private scope" I have created the playlist as collaborative and I am using the login credentials of my personal account to reach this playlist I created. all this is under the same login.
What I am finding is that my scope is not getting added to my token on my call for my token causes a 403 error when I try to add the song.
Here is what that call looks like
https://accounts.spotify.com/authorize/?client_id=mynumber&response_type=code&scope=playlist-modify-private&redirect_uri=http:%2F%2Flocalhost:55141/Home/GetToken/
here are the docs on using authorization to get the correct token.
https://accounts.spotify.com/authorize/?client_id=894400c20b884591a05a8f2432cca4f0&response_type=code&scope=playlist-modify-private&redirect_uri=http:%2F%2Flocalhost:55141/Home/GetToken/
further more if I go into the dev support here
https://developer.spotify.com/documentation/web-api/reference/playlists/add-tracks-to-playlist/
and click the green try button and then request a new token it works.
Bottom line some how my request is not taking my scope request. Any Ideas?
Thanks
To get the token with a specific scope you need to go to the authorize endpoint and get the code. The code is what you want to get to be able http post to the endpoint https://accounts.spotify.com/api/token and get a token with your desired scopes. You can simply get the code by pasting a url like this in your browser...
https://accounts.spotify.com/authorize?client_id=<client_id>&response_type=code&scope=streaming%20user-read-email%20user-read-private&redirect_uri=<redirect_uri>
Only add %20 in between scopes if you have multiple ones
You will then be sent to spotify's website and they'll verify you want to do this. Once you verify it your browser will redirect you to what you set the redirect_uri to be in the url above. At the end of the url that you are sent to, you should be able to see the parameter name code with the code value assigned to it. You then get that code and put it in your http post body params to the https://accounts.spotify.com/api/token endpoint. Make sure you accurately follow the query params requirements in your post method.
An example of the post in python using the requests library:
authorization = requests.post(
"https://accounts.spotify.com/api/token",
auth=(client_id, client_secret),
data={
"grant_type": "authorization_code",
"code": <code>,
"redirect_uri": <redirect_uri>
},
)
authorization_JSON = authorization.json()
return authorization_JSON["access_token"]
In the end you should get a json that shows the scopes you set a long with a refresh the token later on to make more requests.
I know this answer is quite late but I was experiencing the same issue as well which is how I came across this question. I hope this helps anyone that sees this at a later date.
Source: https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow

DocuSign Authorization Code Grant flow returns invalid_grant error

The DocuSign documentation goes through an easy to follow authorization flow for code grant. I'm able to get the "code" from the initial GET request to /oath/auth but getting the tokens gives me an error of "invalid_grant" when I try in postman. I've followed the steps and have a request that looks like this using account-d.docusign.com for host:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic MjMwNTQ2YTctOWM1NS00MGFkLThmYmYtYWYyMDVkNTQ5NGFkOjMwODc1NTVlLTBhMWMtNGFhOC1iMzI2LTY4MmM3YmYyNzZlOQ==
grant_type=authorization_code&code=ey2dj3nd.AAAA39djasd3.dkn4449d21d
Two other members of my team have also tried with their developer accounts and all are getting invalid_grant errors. Is this no longer supported or are there common errors associated with this error that we might be able to investigate?
Re-check all of your values.
I was also getting the same invalid_grant response and could not figure out why at first. It turns out that I had a typo in the Content-Type header. I was using application/x-www-form-urlencode instead of application/x-www-form-urlencoded.
You may not be, but if you are submitting the exact Authorization Header as you've posted it here in your question (MjMwNTQ2YTctOWM1NS00MGFkLThmYmYtYWYyMDVkNTQ5NGFkOjMwODc1NTVlLTBhMWMtNGFhOC1iMzI2LTY4MmM3YmYyNzZlOQ==) it will fail with that message.
That is the base64 value for the sample integration key and sample secret key provided in their documentation. If you decode that string with an online base64decoder it will result in 230546a7-9c55-40ad-8fbf-af205d5494ad:3087555e-0a1c-4aa8-b326-682c7bf276e9. This is the same sample integration key and secret in the documentation.
Check the Authorization header you are submitting by encoding your integration key and secret (integrationKey:secret) using this online base64encoder. This will make sure the issue isn't with your base64 encoding of your integration key and secret. Once you have that value make sure your Authorization uses the word Basic before the value you got from this website. (Basic base64stringFromOnlineEncoder)
Check that the code your are submitting in the body of the post is not the sample code from their documentation. ey2dj3nd.AAAA39djasd3.dkn4449d21d is the sample code from their documentation. You may just be using that in your question as a placeholder but if you are submitting any of those values it will return invalid_grant. Make sure that the body of your post does not have any leading or trailing spaces.
Have the correct Content-Type set application/x-www-form-urlencoded
Have the correct Authorization header set Basic base64EncodedIntegrationKey:Secret
Have the correct body using the valid code received from the GET request to /oauth/auth with no leading or trailing spaces, making sure you're not using the values from your question.
If you are still having trouble and you are not doing a user application but are doing a service integration you can use Legacy Authentication to get your oAuth2 token.
Alternative Method using Legacy Authentication for Service Integrations
This method does not use a grant code. You pass in the integration key, username and password into the X-DocuSign-Authentication header in JSON format.
Demo Server: demo.docusign.net
Production Server: www.docusign.net API
Version: v2
POST https://{server}/restapi/{apiVersion}/oauth2/token
Content-Type: application/x-www-form-urlencoded
X-DocuSign-Authentication: {"IntegratorKey":"your_integrator_key","Password":"docusign_account_password","Username":"docusign_account_username"}
grant_type=password&client_id=your_integrator_key&username=docusign_account_username&password=docusign_account_password&scope=api
If you are building a user application that requires the user enter their docusign credentials to generate the token, this alternative will not work for you.
For anyone who is facing this error, I'd like to point out this note in the documentation:
Note: The obtained authorization code is only viable for 2 minutes. If more then two minutes pass between obtaining the authorization code and attempting to exchange it for an access token, the operation will fail.
I was struggling with the same error until I spotted the note and sped up my typing to meet the 2 minutes.
Hope it helps someone else.
In my case the problem was related to having set a wrong value for Content-Type header, namely "application/x-www-form-URIencoded" instead of the correct "application/x-www-form-urlencoded". Note though that in my case the problem was not a "typo" but an excessive trust in DocuSign documentation.
Indeed the wrong Content-Type is, at the time of writing, suggested directly into the documentation page where they describe the Authorization Code Grant workflow, see the image below for the relevant part.
Hopefully they will fix the documentation soon but for the time being be careful not to blindly copy & paste the code from their examples without thinking, as I initially did.
anyone have an idea what is wrong here I am getting a BadRequest with the following
{"error":"invalid_grant","error_description":"unauthorized_client"}
var client = new RestClient(ESIGNURL);
var request = new RestRequest("/oauth/token");
request.Method = Method.POST;
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(integrationkey+ ":" + secret)));
string body = "grant_type=authorization_code&code=" + code;
request.Parameters.Clear();
request.AddParameter("application/x-www-form-urlencoded", body, ParameterType.RequestBody);
var response = client.Execute(request);
I was getting this error as well. What I realized is I was appending the state at the end of the code before passing it to the oauth token endpoint.
This snippet is from Docusign explaining what are some other reasons for getting that error.
Invalid-error explanation
I just spent a day doing this (in NodeJS). I'll add a couple of things to the answers from before. First, I had to put:
"Content-Type": "application/x-www-form-urlencoded"
in the header. Otherwise it gave me the message:
{
"error": "invalid_grant",
"error_description": "unsupported_grant_type"
}
Second, the base64 encoding:
I used this in NodeJS and it worked
const integration_key = process.env.INTEGRATION_KEY;
const secret_key = process.env.SECRET_KEY;
const authinfo =
integration_key.toString("utf8") + ":" + secret_key.toString("utf8");
const buff2 = Buffer(authinfo, "utf8").toString("base64");
If you use "base64url" it dosen't work because it strips the == off of the end of the string. The = symbol is used as padding and apparently it's needed. You see a similar difference on this site https://www.base64encode.org/ when you toggle the url safe encoding option. If you don't have the padding on the end of your base64 encoded string (or if it's generally incorrect) you get this message:
{
"error": "invalid_grant",
"error_description": "unauthorized_client"
}
Finally, if you're using Postman (I'm using DocuSign's Postman Collection) remember to reset and save the codeFromUrl variable after you update it. Otherwise it doesn't update and you get the message:
{
"error": "invalid_grant",
"error_description": "expired_client_token"
}
This means the old URL code has expired and your new one didn't save.

Instagram-Node calls keep returning "access code invalid"

I recently opened a new Instagram account and registered a new client. The information I received back from Instagram included the client_id and the client_secret.
I will only run the script on my machine, and be the sole user of the project, so I don't need any user to "log in". But for some reason, whenever I try to make any calls to the Instagram-Node API, it returns the following error:
{ [Error: OAuthAccessTokenException: The access_token provided is invalid.]
code: 400,
error_type: 'OAuthAccessTokenException',
error_message: 'The access_token provided is invalid.',
retry: [Function] }
That's weird to me, because I have an identical setup with an older Instagram account and different credentials, that seem to be working just fine. Then again, that was before November, when Instagram changed some of their API policies.
I use the Instagram-Node like so:
ig.use({
client_id: "dxxxxxxxxxxxxxxxxxxxxxxx2",
client_secret: "4b0xaxaxaxaxaxaxaxaxaxaxa53c03100e4"
});
and make my first call like this:
ig.user_media_recent(user.toString(), options,...
I tried handling the authentication by rerouting my request through the redirect_uri as shown in the Instagram-Node documentation, but even then, all of my requests are unsigned, so it's not clear to me what I would do with the access_token any way.
Any help would be much appreciated!
Okay, the problem is a misunderstanding of the limits of the Sandbox Mode, they essentially make it impossible to look up media from users who are not in your sandbox.
The documentation here says:
The first point is important and it means that the API behaves as if
the only users on Instagram were your sandbox users, and the only
media ever posted were the last 20 for each of these users.
I was trying to find media from users who are not in my sandbox, and so I received an error. The reason that my previous credentials weren't affected is because they are still grandfathered into the grace period, June 2016, at which time they will expire as well. Thanks!
I ran into this same issue. If your app is using oauth, you will cause Instagram to spaz out if you pass the client_secret and client_id stuff again. I had to take these out of my code. So I just pass the access_token now.
ig.use({ access_token: token });
ig.user_media_recent(config.userid, { count: 20 }, function(err, medias, pagination, remaining, limit) {
Were my equivalant statements.
EDIT: I forget to mention. This is after a cycle of these for those confused:
api.authorize_user
api.get_authorization_url

Resources