It's my first attempt to call a Graph API (with VBA), but I still could not get one access token for this.
Although I have read the documentation, I do not know what flow I need to apply to get access token without forcing the user to log in. Is this possible on my OneDrive Personal Accounts?
Here is what I have tried
Sub Test_GetToken()
Dim xml As New MSXML2.XMLHTTP60
Dim url As String
url = "https://login.microsoftonline.com/namelles_test#outlook.com/oauth2/v2.0/token"
url = url & "?client_id=eca0b14c-1154-4768-8e45-2cb5639b7e0d"
url = url & "grant_type=client_credentials"
url = url & "&client_secret=VksZ8FeDF5XiRfqTBsgs628"
url = url & "&resource=https://graph.microsoft.com"
xml.Open "POST", url, False
xml.setRequestHeader "application", "x-www-form-urlencoded"
xml.send ("")
Debug.Print "status=" & xml.Status, "readyState=" & xml.readyState
Debug.Print xml.responseText
Set xml = Nothing
End Sub
I created an account just for testing (username: nameless_test#outlook.com, userpass: nameless1234). Therefore, the real credentials can be used.
Can someone show me the flow I need to apply and how exactly the request is built?
Thanks in advance
Looks like you put the email incorrectly in your code.
url = "https://login.microsoftonline.com/namelles_test#outlook.com/oauth2/v2.0/token"
Should be:
url = "https://login.microsoftonline.com/nameless_test#outlook.com/oauth2/v2.0/token"
Also, based on your comment, it seems that you have to put the grant_type in the body of the request instead of a query parameter in the URL.
xml.send ("grant_type=client_credentials")
Related
I'm trying to improve my knowledge of VBA, learning about GET, POST and stuff, because I've seen many examples, and can't get what I'm doing wrong. Probably is the Oauth part.
The main problem is that I'm just an Excel guy. I'm not web developer, so my knowledge is almost null, and probably I'm missing a lot of basic stuff.
I hope this question is not too broad.
BACKGROUND: I'm trying to get the ResponseText of a JSON object, from a tweet. The information is public and you don't need to be logged in to see the info I want to get, and you don't need a Twitter account.
For testing, I'm using this tweet: https://twitter.com/StackOverflow/status/1273391252357201922
WHAT I WANT: Checking the code with Developer Tools (I'm using Firefox), I've seen this:
This GET request returns this ResponseText:
So I would like to get that ResponseText into VBA.
MY CODE: Checking different codes here in SO, I've build up this:
Sub test()
Dim MiHttp As Object
Dim MiUrl As String
Set MiHttp = CreateObject("MSXML2.XMLHTTP")
MiUrl = "https://api.twitter.com/2/timeline/conversation/1273391252357201922.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&simple_quoted_tweet=true&count=20&ext=mediaStats%2ChighlightedLabel&include_quote_count=true"
With MiHttp
.Open "GET", MiUrl
.Send
DoEvents
Debug.Print .responseText
End With
MiHttp.abort
Set MiHttp = Nothing
End Sub
And it runs, no coding errors, but I get this:
{"errors":[{"code":200,"message":"Forbidden."}]}
So I tried adding RequestHeaders with Authoritation:
adding this line of code before .Send:
.setRequestHeader "authorization", "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
And then I get this in the debugger:
{"errors":[{"message":"Rate limit exceeded","code":88}]}
So checked the Twitter library for developers looking info about Bearer stuff and tokens and I must admit I got overwhelmed.
About
Bearer
About
Tokens
And now I'm lost. I thought this would be kind of easy, because it's public info that everyone can get manually, from any tweet, without using any app or logging in Twitter, but it's looks like I'm wrong, and I'm kind of lost.
FINAL QUESTION: I would like to know if I can get that Bearer token in any way, then apply it into my code, to get that JSON responseText (dealing with the JSON and learning about them would be a totally different question, out of scope here).
And I would like to achieve this with VBA, no other apps or languages, because I've have no idea.
Actually I'm not even interested in the full text, just the part surrounded with red line.
Looking for some help, guide, light.
Thanks in advance and I hope this question is not too broad.
Thanks!
UPDATES: Tested #ChristosLytras's answer. I get this error:
UPDATE JULY 2020: now the working url is:
https://api.twitter.com/2/timeline/conversation/1273391252357201922.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&simple_quoted_tweet=true&count=20&ext=mediaStats%2ChighlightedLabel&include_quote_count=true
You have to pass a valid fetched Guest Token in the request header along with authorization Bearer and you'll have the response. The twitter public API bearer never changes.
In order to get a new and valid Guest Token for each request, you can make a HEAD request using WinHttp.WinHttpRequest.5.1 instead of MSXML2.XMLHTTP and read the gt cookie using a regular expression like gt=(\d+);. That will fetch the cookie headers each time it's being called. You cannot use MSXML2.XMLHTTP because it uses cache and you won't get a new Guest Token each time you request the HEAD.
Working code tested using Excel 2013 with VBA 7.1:
Dim MiHttp As Object
Dim GuestTokenRE As Object
Dim MiUrl As String
Set MiHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Set GuestTokenRE = CreateObject("VBScript.RegExp")
MiUrl = "https://api.twitter.com/2/timeline/conversation/1273391252357201922.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&simple_quoted_tweet=true&count=20&ext=mediaStats%2ChighlightedLabel&include_quote_count=true"
With MiHttp
' Make a HEAD request with no cache to get the Guest Token cookie
.Open "HEAD", "https://twitter.com", False
.setRequestHeader "User-Agent", "Firefox"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "Cache-Control", "no-cache"
.Send
DoEvents
' Use a regular expression to extract guest token from response headers
GuestTokenRE.Pattern = "Set-Cookie: gt=(\d+);"
GuestTokenRE.IgnoreCase = True
Dim matches as Object
Set matches = GuestTokenRE.Execute(.getAllResponseHeaders())
If matches.Count = 1 Then
Dim guestToken As String
guestToken = matches.Item(0).Submatches.Item(0)
' Print the Guest Token for validation
Debug.Print "Got Guest Token", guestToken
' Now we have a valid guest token, make the request
.Open "GET", MiUrl, False
' Authorization Bearer is always the same
.setRequestHeader "authorization", "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
.setRequestHeader "x-guest-token", guestToken
.Send
DoEvents
Debug.Print "Got response", .responseText
Else
Debug.Print "Could not fetch Guest Token"
End If
End With
MiHttp.abort
Set MiHttp = Nothing
Set GuestTokenRE = Nothing
Regarding 80072efe error
You'll have to get WinHttp.WinHttpRequest.5.1 to work. The 80072efe error indicates the connection terminates abnormally and you can read more about it here. I didn't have such an issue so these errors do not originate from the endpoint.
Screen capture of the code in action
I need to login a https website via httprequest.
I am trying to use the code from this post VBA WinHTTP to download file from password proteced https website
but i only get that answer: "User not found" - but I know the user and password works fine when I login manually.
My main doubt is where the parameters in the string strAuthenticate came from?
And also why I can not see any http header with the "authorization" word in it or with my username/password in it when i use a http sniffer program.
The website is a form-base authentication type. Is there a way (or should I) inform in my code any reference to the HTML textboxes objects for username and password?(And in this case how could I do it?)
Sub SaveFileFromURL()
Dim WHTTP As WinHttp.WinHttpRequest
Set WHTTP = New WinHttpRequest
mainUrl = "https://www.somesite.com.br/Login.php"
myuser = "userA"
mypass = "passuserA"
strAuthenticate = "start-url=%2F&user=" & myuser & "&password=" & mypass & "&switch=Log+In"
WHTTP.Open "POST", mainUrl, False
WHTTP.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
WHTTP.SetRequestHeader "Authorization", "Basic " & EncodeBase64(StrConv( myuser & ":" & mypass, vbFromUnicode))
WHTTP.Send
End Sub
Thanks very much for all the help. Turns out the key for find the answer was to use the right tools.
As #chillin recommended using a traffic analyser was essential. I was trying to get the HTTP headers with "Live HTTP Headers" chrome extension, but that only gives my information about tha manual authentication process and even then INCOMPLETE information.
So I downloaded "WireShark" and try to sniff the HTTP traffic, but I couldn't since it was encrypted. Then I did some research and found this way of workaround the encryption:
https://redflagsecurity.net/2019/03/10/decrypting-tls-wireshark/
After this step-by-step guide and apliccate an http packets filter (just write http in the wireshark filter textbox) I was able to sniff the HTTP traffic (the one I generate when I log in manually to the website and the one generated via excel(vba) HTTPREQUEST.
After this everything got easier and I end up with the code below:
Sub HTTPRESQUEST()
'https://stackoverflow.com/questions/22051960/vba-winhttp-to-download-file-from-password-proteced-https-website/
'https://redflagsecurity.net/2019/03/10/decrypting-tls-wireshark/
'https://wiki.wireshark.org/TLS?action=show&redirect=SSL
'https://wiki.wireshark.org/TLS#Using_the_.28Pre.29-Master-Secret
Dim WHTTP As WinHttp.WinHttpRequest
Set WHTTP = New WinHttpRequest
'Logon page:
mainUrl = "https://www.somewebsite/Logar.php"
myuser = "myuser"
mypass = "mypassword"
strAuthenticate = "username=" & myuser & "&bypass=" & mypass
WHTTP.Open "POST", mainUrl, False
WHTTP.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
WHTTP.Send strAuthenticate
End Sub
That was enough to do the website log in. No need to encode the username and password.
PS: In the strAuthenticate the "username" and the "bypass" are the HTML objects ids for the username textbox and the password textbox.
I hope this answear can help other people. Thanks again for all the help!
i am trying to get past the simple log template in with DocuSign. Can anyone kindly tell me what is missing in the VBA code. Is there quotes or anything I need to make this work. I added some quotes around my email address. I have a sandbox account and my developer key, but i get a 401 error whether i send execute the "GET" or not. i did take this code from another tread in stackoverflow but i dont know what was in the excel cells to make this code work.
Public Sub APICallTest()
Dim httpRequest As MSXML2.XMLHTTP60
Dim httpResult As MSXML2.DOMDocument60
' defined request and result variables
Set httpRequest = New XMLHTTP60
Set httpResult = New DOMDocument60
'open login information url https://demo.docusign.net/restapi/v2
httpRequest.Open "GET", "https://demo.docusign.net/restapi/v2/login_information.XML", False
httpRequest.setRequestHeader "X-DocuSign-Authentication: <DocuSignCredentials><Username>MyUserName</Username><Password>" + Chr(34) + "my#myemail.com" + Chr(34)</Password><IntegratorKey>myintegratorkey</IntegratorKey></DocuSignCredentials>Accept: application/xml Content-Type: application/xml", "text"
' send login information request
httpRequest.send
Debug.Print httpRequest.Status, "A"
Debug.Print httpRequest.statusText, "B"
Debug.Print httpRequest.responseText, "C"
Exit Sub
The error message is telling you what the problem is- your Integrator Key is either not present in the request or is invalid (i.e. incorrect). I see your VB code that shows you apparently including the key in the header, however since you haven't posted the raw request you're sending out my guess is that your code is not working properly and the key or the header is not being set properly.
Try doing this:
Login to your demo account, go into your preference, and enable the Request Logging feature. (more on how to do that below)
Run your code.
Go back into your account preferences and retrieve the log that would have been created.
Inspect the log and make sure your Integrator Key and X-DocuSign-Authentication header are present and correct.
For a more complete guide on how to enable request logging in your account see here:
https://support.docusign.com/guides/ndse-user-guide-api-request-logging
Or here if you're using the Classic DocuSIgn UI:
https://support.docusign.com/articles/API-Request-Logging
I suspect that the issue is caused because you are trying to set more than one header in the httpRequest.setRequestHeader statement.
I recommend splitting them up to seperate statements
httpRequest.setRequestHeader("X-DocuSign-Authentication","<DocuSignCredentials><Username>MyUserName</Username><Password>" + Chr(34) + "my#myemail.com" + Chr(34)+"</Password><IntegratorKey>myintegratorkey</IntegratorKey></DocuSignCredentials>")
httpRequest.setRequestHeader("Accept","application/xml");
httpRequest.setRequestHeader("Content-Type","application/xml");
https://msdn.microsoft.com/en-us/library/ms766589(v=vs.85).aspx
Hope this proves helpful.
It works now!! this is what it ended up looking in VBA. How does someone know to put "application/xml" on the end of the Header definition?
Public Sub APICallTest()
Dim httpRequest As MSXML2.XMLHTTP60
Dim httpResult As MSXML2.DOMDocument60
'defined request and result variables
Set httpRequest = New XMLHTTP60
Set httpResult = New DOMDocument60
'open login information url https://demo.docusign.net/restapi/v2
httpRequest.Open "GET", "https://demo.docusign.net/restapi/v2/login_information"
httpRequest.setRequestHeader "X-DocuSign-Authentication", "<DocuSignCredentials><Username>my#email.com</Username><Password>mypassword</Password><IntegratorKey>mykey</IntegratorKey></DocuSignCredentials>"
httpRequest.setRequestHeader "Accept", "application/xml"
httpRequest.setRequestHeader "Content-Type", "application/xml"
httpRequest.send
Debug.Print httpRequest.Status, "A"
Debug.Print httpRequest.statusText, "B"
Debug.Print httpRequest.responseText, "C"
Set httpRequest = Nothing
Set httpResult = Nothing
Exit Sub
I'm building a VB macro that shortens URLs in bulk (we're talking thousands). My macro works just fine, until I encountered a long URL with "%3d", which translates into the equals symbol (=).
Here is and example of the long URL: http://domain.com/se/?st=YmUQaIg9PCoCs3vex5XHE1NnqfurVpWsXMXix0QkyO4%3d&p=A43S8C
My macro sends the entirety of the URL to Bit.ly, but Bit.ly's response text shows that it shortened this: http://domain.com/se/?st=YmUQaIg9PCoCs3vex5XHE1NnqfurVpWsXMXix0QkyO4=
And in fact, when I go to the shortened link, I'm not directed to the full URL.
This is the portion of my code that prompts Bit.ly and gets the response:
ApiRequest = "https://api-ssl.bitly.com/v3/shorten?access_token=" & Token & "&longUrl=" & LongURL
With HttpRequest
.Open "GET", ApiRequest, False
.Send
End With
Response = HttpRequest.ResponseText
HttpRequest.WaitForResponse
BeginCar = InStr(Response, "hash")
EndCar = BeginCar + 15
BitlyResult = Right(Mid(Response, BeginCar, (EndCar - BeginCar)), 7)
Range("J" & l).Value = "http://bit.ly/" & BitlyResult
How can I tell Bit.ly not to shorten a truncated URL and to keep the symbols such as "%3d"?
Thanks,
I'd think that you must escape the entire url when it is used as a parameter inside another url. The problem may not be %3d but the & that follows after it (it starts the next parameter in the bitly-url).
Try to escape this
http://domain.com/se/?st=YmUQaIg9PCoCs3vex5XHE1NnqfurVpWsXMXix0QkyO4%3d&p=A43S8C
entirely to
http%3A%2F%2Fdomain.com%2Fse%2F%3Fst%3DYmUQaIg9PCoCs3vex5XHE1NnqfurVpWsXMXix0QkyO4%253d%26p%3DA43S8C
Here are ways to do this in Excel.
in VBA i am creating a URL:
URL = "http://api.local.yahoo.com/MapsService/V1/geocode?appid=" & yahoo & "&street=" & street & "&city=" & city & "&state=" & state & "&zip=" & zip
for example it sets itself to equal this:
http://api.local.yahoo.com/MapsService/V1/geocode?appid=username123&street=1893 n. clyde morris blvd &city=daytona beach&state=FL&zip=32117
when i manually go to this URL, IT WORKS FINE.
however when i do this:
'Create Http object
If IsEmpty(http) Then Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
'Send request To URL
http.Open "GET", URL
http.send
'Get response data As a string
response = http.responseText
it's giving me this for the response:
response="Watch : : response : "<!doctype html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><title>Yahoo! - 400 Bad Request</title><style>
/* nn4 hide */
/*/*/
bod........"
please note that 50% of the time it returns the correct URL and 50% it gives me a 400 bad request
what am i doing wrong?
wrikken has suggested that i get a URLencoder, but i believe that it was encoding it correctly!
You need to url-encode all your query parameters, otherwise they may result in invalid URLs, depending on their actual value (for instance, a space should be a + or a %20). Google has a multitude of VBA url encode examples, it appears there's no built-in function for it.