I'm working with an S3 target that I think is requiring V2 signature authentication, but I have been unable for the life of me to get this working.
I'm using the Sprightlysoft AWS component.
'create an instance of the REST class
Dim MyREST As New SprightlySoftAWS.REST
Dim RequestURL As String
'Build the URL to call. Do not specify a bucket name or key name when listing all buckets
RequestURL = MyREST.BuildS3RequestURL(True, "s3target.com", "", "", "")
Dim RequestMethod As String
RequestMethod = "GET"
Dim ExtraRequestHeaders As New Dictionary(Of String, String)
'add a date header
ExtraRequestHeaders.Add("x-amz-date", DateTime.UtcNow.ToString("r"))
Dim AuthorizationValue As String
'generate the authorization header value
AuthorizationValue = MyREST.GetS3AuthorizationValue(RequestURL, RequestMethod, ExtraRequestHeaders, TextBoxAWSAccessKeyId.Text, TextBoxAWSSecretAccessKey.Text)
'add the authorization header
ExtraRequestHeaders.Add("Authorization", AuthorizationValue)
'call MakeRequest to submit the REST request
Dim RetBool As Boolean
RetBool = MyREST.MakeRequest(RequestURL, RequestMethod, ExtraRequestHeaders, "")
The error message I get is:
"The AWS Access Key ID and Signature did not match your provided.
Please check your key and signing method"
According to errors and my research, it would seem that the target is requiring a v2 signature, and there seems to be a v2 signature function available, within the Sprightlysoft AWS component.
https://sprightlysoft.com/AWSComponent/Help/html/2605bafc-43dc-df45-7700-bd2fd74e1505.htm
Public Function GetSignatureVersion2Value (RequestURL As String, RequestMethod As String, SendData As String, AWSSecretAccessKey As String ) As String
I've tried to get this working, but I'm just guessing to how/where this needs to be used. I wondered if someone may know how to use this so I can authenticate properly, if this is indeed the missing piece of the puzzle.
Related
I am using Postman to send in an authorization request to google to retrieve an access token. When I enter all the required information on the Authorization tab and click on the Get New Access Token button, I get a response back from google with an access token. This proves that the data I am sending in is valid and my project is set up properly on google. Now, I need to convert this request to a VBA request.
When I view the Postman log I can see the data that Postman sends to Google. This is what I see:
And this is the response that google sends back:
This is the VBA code that I am using to try to replicate the Postman call (I have the redirect url set to my local machine which is in the list of approved redirect url's on my google project):
Dim sClient_ID As String
Dim sClient_Secret As String
Dim sGrant_Type As String
Dim sCode As String
Dim sRedirect_URI As String
sClient_ID = "{the client id that google provided to me}"
sClient_Secret = "{the client secret that google provided to me}"
sGrant_Type = "authorization_code"
sCode = "4/0AWtgzh75ZFps55t9vPx-gm_rm8W_uyWQbZwBF1qTLthpUuPjaAXBr9iywT-RweVvagcGPg"
sRedirect_URI = "https://localhost:8080"
' build the json string which will be sent to get the google access token
Dim a As New Scripting.Dictionary
a.Add "grant_type", sGrant_Type
a.Add "code", sCode
a.Add "redirect_uri", sRedirect_URI
a.Add "client_id", sClient_ID
a.Add "client_secret", sClient_Secret
Dim Json_Get_Access_Token As String
Json_Get_Access_Token = JsonConverter.ConvertToJson(a, Whitespace:=" ")
' send the json string via POST
Set httpCall = CreateObject("MSXML2.ServerXMLHTTP")
Dim sTokenURL As String
sTokenURL = "https://oauth2.googleapis.com/token"
httpCall.Open "POST", sTokenURL, False
httpCall.setRequestHeader "Content-Type", "application/json;charset=UTF-8"
httpCall.Send Json_Get_Access_Token
Dim sReturnToken As String
sReturnToken = httpCall.responseText
When I run the code and look at the value is sReturnToken, I see this:
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
Any idea what I'm not setting up properly?
Thank you.
EDIT:
You can actually send the requests as json or encoded URL
Send request as JSON
Set Content-Type to application/json and convert the a dictinary to json
Dim Json_Get_Access_Token As String
Json_Get_Access_Token = JsonConverter.ConvertToJson(a, Whitespace:=" ")
'...
httpCall.Send Json_Get_Access_Token
'...
Send request as Encoded URL
Set Content-Type to application/x-www-form-urlencoded and concatenate the values of the a dictionary like this: data=value&data2=value2, each value must be encoded with WorksheetFunction.EncodeURL function that only works in Excel 2013+ if you have an older version or non-Excel app then use this function
Dim strQueryString as String
For Each vKey In a.Keys()
strQueryString = strQueryString & vKey & "=" & WorksheetFunction.EncodeURL(a(vKey)) & "&"
Next
' Remove last &
strQueryString = Left(strQueryString, Len(strQueryString) - 1)
'...
httpCall.Send strQueryString
'...
Here more info
Tested output:
{
"access_token": "ya29.xxx",
"expires_in": 3599,
"refresh_token": "1//xxx",
"scope": "https://www.googleapis.com/auth/calendar.events.readonly",
"token_type": "Bearer"
}
I am trying to set up a class that can wrap around the .NET Google API so that I can use an Access Token that I have previously obtained to access a user's Google Drive. As of right now, I am just trying to get it to work so that I do not require a Refresh Token (more on that in a second). The ultimate goal is for somebody to go through a web page I have set up to authenticate where I obtain both an Access Token and a Refresh Token by directly calling to the Google Rest API (which I store in a database). They can then request to upload/download files onto their Drive on a different page which will first obtain the appropriate information from the database and then use the .NET Google API Library when accessing Drive.
However, when I attempt to access their Drive I get the the following error:
The access token has expired and could not be refreshed. Errors: refresh error, refresh error, refresh error
I know that the Access Token is valid because I obtain it only seconds earlier during my testing. Here is my code for setting up the Drive Service:
' NOTE: Code altered for brevity
Public Sub Initialize(accessToken As String)
' Set up the client secret information based on the default constants
Dim clientSecrets As New ClientSecrets()
clientSecrets.ClientId = DEFAULT_CLIENT_ID
clientSecrets.ClientSecret = DEFAULT_CLIENT_SECRET
' Set up a token based on the token data we got
' NOTE: Is it OK to leave some strings as NULL?
Dim token As New Responses.TokenResponse()
token.AccessToken = accessToken
token.RefreshToken = ""
token.TokenType = "Bearer"
token.IssuedUtc = DateTime.Now
token.ExpiresInSeconds = 3600
token.Scope = "drive"
token.IdToken = ""
' Set up a flow for the user credential
Dim init As New GoogleAuthorizationCodeFlow.Initializer()
init.ClientSecrets = clientSecrets
init.Scopes = New String() {DriveService.Scope.Drive}
init.Clock = Google.Apis.Util.SystemClock.Default
' Set up everything else and initialize the service
Dim baseInit As New BaseClientService.Initializer()
baseInit.HttpClientInitializer = New UserCredential(New GoogleAuthorizationCodeFlow(init), "user", token)
baseInit.ApplicationName = APP_NAME
_service = New DriveService(baseInit)
End Sub
Shortly after that, I then use the following code to request a folder so I can check to see if it exists or not.
Private Function GetDriveFolder(folderPath As String, ByRef folderIds As String(), Optional createMissingFolders As Boolean = False, Optional parentFolderId As String = "root") As Data.File
Dim creatingFolderPath As Boolean = False
Dim currentFolder As Data.File = Nothing
Dim folderPathSplit As String() = folderPath.Replace("/", "\").Trim("\").Split("\")
Dim folderIdList As New List(Of String)
folderIds = {}
' Loop through each folder in the path and seek each out until we reach the end
For x As Integer = 0 To folderPathSplit.Length - 1
Dim result As FileList = Nothing
If Not creatingFolderPath Then
' Build a list request which we will use to seek out the next folder
Dim request As FilesResource.ListRequest = _service.Files.List()
request.Q = "mimeType='application/vnd.google-apps.folder' and name='" & folderPathSplit(x) & "'"
If currentFolder Is Nothing Then
request.Q &= " and '" & EscapeDriveValue(parentFolderId) & "' in parents"
Else
request.Q &= " and '" & EscapeDriveValue(currentFolder.Id) & "' in parents"
End If
request.Spaces = "drive"
request.Fields = "files(id, name)"
' Execute the search, we should only get a single item back
' NOTE: Error thrown on this request
result = request.Execute()
End If
' So on.....
So, I'm just trying to get it to work with only the Access Token for the time being because if it ends up getting refreshed I'll need to know so that I can update my database. However, if I do include the Refresh Token I get the following error:
Error:"unauthorized_client", Description:"Unauthorized", Uri:""
I'm guessing this has something to do with the way I have configured my application through the Dev Console but if I authenticate through the Google API Library by having it launch a browser to get my credentials everything works fine. So, I'm really not sure where to go from here as I haven't found anybody having similar problems and the guides don't cover specifying your own Access Token.
Also, as a quick note this is the URL I am using when having the user authenticate:
String.Format("https://accounts.google.com/o/oauth2/v2/auth?client_id={0}&state={1}&redirect_uri={2}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&include_granted_scopes=true&prompt=select_account%20consent&response_type=code", GOOGLEAPI_CLIENTID, validateState, redirectUri)
Thanks for the help!
If you have an access-token then the simplest way to create a google credential is to use the GoogleCredential.FromAccessToken() method passing in your access token.
This returns you a GoogleCredential instance which you can use to set the HttpClientInitializer property when building the DriveService.
If you then still get an error when accessing the drive service, then it's likely there's something incorrect in how you are asking for the access-token.
I am making an FTP request and whenever the parameter remoteFilePath contains a string with "%20" I get an error that the file cannot be found. How can I get around this?
Dim remoteFileWriteTime As Date = Ftp.GetDateTimeStamp(remoteFilePath).ToLocalTime()
Note: I am not responsible for naming the files as it is not my server.
Try encoding the filename using this:
Dim baseUri as String = "ftp://SomeFtpServer/"
Dim file as String = "Strangely%20Named%20File.pdf"
Dim myUri as String = baseUri & HttpUtility.UrlEncode(file)
Dim remoteFileWriteTime As Date = Ftp.GetDateTimeStamp(myUri).ToLocalTime()
You might need to encode using Uri.EscapeDataString if HttpUtility isn't encoding it correctly. I'm not sure exactly which encoding scheme FTP expects offhand.
I have nearly completed a basic OneDrive interface, and am able to handle creating and writing folders, files, and so forth. The last element to get working is to update a file's date/time stamp to match that of the local (originating) file.
When I drop a file via the browser interface, the file's date/time stamp shows correctly in the view. This is reflected in the "client_updated_time" when I read the file's properties later in my application. Clear enough.
However, I cannot find any way to update this field programmatically from within my application. I am using the following code, to no avail. I have a valid _accessToken value, a valid fileId for the new file, and the call results always indicates success.
The "name" and "updated_time" elements are just in there to see if anything happens, and the file will indeed rename if I mangle the fileName variable a bit. I didn't expect the "updated_time" to updated, but it seems to me that the "client_updated_time" element should work.
Using fiddler, it appears that the browser-based interface (java?) opens a session, sends the file over, and then in the close-session call uses a header entry labelled "X-Last-Modified-ISO8601" to set the file's date-time stamp. However, using the REST interface, I cannot find any examples of this. The documentation for setting file properties mentions renaming only (which works in this code).
Any feedback on how to accomplish setting "client_updated_time" with REST calls would be much appreciated!
Here's the relevant code:
Private _liveURL As String = "https://apis.live.net/v5.0/"
Private Sub AddAuthorizationHeader(hc As HttpClient, authorization As String)
If Len(authorization) > 0 Then
hc.DefaultRequestHeaders.Authorization = New Headers.AuthenticationHeaderValue("Bearer", authorization)
End If
End Sub
Public Function WebPUT(uri As String, contentType As String, authorization As String, data As String) As String
Dim response As String = String.Empty
Try
Dim hc As New HttpClient()
Dim content As New Http.StringContent(data, System.Text.Encoding.UTF8, contentType)
AddAuthorizationHeader(hc, authorization)
Using r = hc.PutAsync(uri, content).Result
response = r.Content.ReadAsStringAsync.Result
End Using
Catch ex As Exception
' fake it
response = "{""error"": {""code"": ""invalid_request"", ""message"": """ + ex.Message + """}}"
End Try
Return response
End Function
Private Function UpdateFileDateTime(fileId As String, fileName As String, fileDt As String) As Boolean
Dim response As Boolean = False
Dim wr As String = WebHelper.WebPUT(_liveURL + fileId, "application/json", _accessToken, "{ ""name"": """ + fileName + """, ""updated_time"": """ + fileDt + """, ""client_updated_time"": """ + fileDt + """ }")
'... parse wr for response
Return response
End Function
Based on the file object reference, that field is read-only via the REST API: http://msdn.microsoft.com/en-us/library/dn631834.aspx
I'm trying to make a stripe payment work from a VB website. I know, I know, "I should use C#". I can't because the site is already in VB. Nothing I can do about it.
Anyway, I have most of it figured out:
User clicks submit button with valid info
Form submits to Stripe
Stripe sends a token back
A jQuery ajax function posts the data to donate/pay-by-stripe
I have this line of code in my Global.asax.vb
routes.MapRoute("pay-by-stripe", "donate/pay-by-stripe", New With{.controller = "Dynamic", .action = "PayByStripe"})
So my PayByStripe function in the Dynamic Controller looks like this:
Function PayByStripe()
''The Stripe Account API Token
Dim STR_Stripe_API_Token As String = "sk_test_*****"
''The Stripe API URL
Dim STR_Stripe_API_URL As [String] = "https://api.stripe.com/v1/charges"
''The Stripe Card Token
Dim token As String = HttpContext.Request.Form("token")
Dim description As String = HttpContext.Request.Form("description")
Dim amount As Single = HttpContext.Request.Form("amount")
''Creates a Web Client
Dim OBJ_Webclient As New System.Net.WebClient()
''Creates Credentials
Dim OBJ_Credentials As New System.Net.NetworkCredential(STR_Stripe_API_Token, "")
''Sets the Credentials on the Web Client
OBJ_Webclient.Credentials = OBJ_Credentials
''Creates a Transaction with Data that Will be Sent to Stripe
''Dim OBJ_Transaction As New System.Collections.Specialized.NameValueCollection()
Dim OBJ_Transaction As NameValueCollection = New NameValueCollection()
OBJ_Transaction.Add("amount", amount)
OBJ_Transaction.Add("currency", "usd")
OBJ_Transaction.Add("address-country", "US")
OBJ_Transaction.Add("description", "")
OBJ_Transaction.Add("card", token)
''The Stripe Response String
Dim STR_Response As String = Encoding.ASCII.GetString(OBJ_Webclient.UploadValues(STR_Stripe_API_URL, OBJ_Transaction))
'Response.Redirect("/donate/?transaction=success");
Return STR_Response
End Function
I'm getting a 400 bad request error on the STR_Response line:
Dim STR_Response As String = Encoding.ASCII.GetString(OBJ_Webclient.UploadValues(STR_Stripe_API_URL, OBJ_Transaction))
I'm a VB and Stripe noob, and not sure what this means. My main theory now is that it's because I don't have a /donate/pay-by-stripe/ page, but I don't know what I'd even put in there if I did create it.
Any help would be great!
That's a webservice you are calling, right?
A 400 Bad Request with a webservice means your XML request is malformed.
Example, in my request, part of it is a UTC in a certain date format. Example: <pp:utc>2013-05-24 2025</pp:utc>
So, if I were to malform my request to this <pp:utc>2013-05-24 2025</pp:utc2> it would result in:
HTTP/1.1 400 Bad Request
Cache-Control: private
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.5
So, check your request and make sure everything is properly formatted.
EDIT: just noticed I put the "incorrect" utc tags incorrectly.
Please notice the opening tag <pp:utc> is being closed with a </pp:utc2>, which is the reason why you see 400 bad request
I had to put my password in System.Net.NetworkCredentials, and address-country is not a usable field. The only usable fields when submitting a charge are amount, currency, description, and card (which is actually the token). This is the final, working version of my PayByStripe Function in my Dynamic Controller:
Function PayByStripe()
'' The Stripe Account API Token - change this for testing
Dim STR_Stripe_API_Token As String = ""
If (this_is_a_test) Then
' Test Secret Key
STR_Stripe_API_Token = "sk_test_***"
Else
' Prod Secret Key
STR_Stripe_API_Token = "sk_live_***"
End If
''The Stripe API URL
Dim STR_Stripe_API_URL As [String] = "https://api.stripe.com/v1/charges"
''The Stripe Card Token
Dim token As String = HttpContext.Request.Form("token")
Dim description As String = HttpContext.Request.Form("description")
Dim amount As Single = HttpContext.Request.Form("amount")
''Creates a Web Client
Dim OBJ_Webclient As New System.Net.WebClient()
''Creates Credentials
Dim OBJ_Credentials As New System.Net.NetworkCredential(STR_Stripe_API_Token, "YOUR PASSWORD FOR STRIPE")
''Sets the Credentials on the Web Client
OBJ_Webclient.Credentials = OBJ_Credentials
''Creates a Transaction with Data that Will be Sent to Stripe
Dim OBJ_Transaction As New System.Collections.Specialized.NameValueCollection()
OBJ_Transaction.Add("amount", amount)
OBJ_Transaction.Add("currency", "usd")
OBJ_Transaction.Add("description", description)
OBJ_Transaction.Add("card", token)
''The Stripe Response String
Dim STR_Response As String = Encoding.ASCII.GetString(OBJ_Webclient.UploadValues(STR_Stripe_API_URL, OBJ_Transaction))
Return STR_Response
End Function
I've never had to pass in my password when connecting to Stripe's API. Simply pass in your private API Key through an authorization header with no password. It may also help to pass in a version header as well, something Stripe recommends. The following lines of card are in C#, I know your question was in VB, but I'm sure you can easily adaptive this:
webrequest.Headers.Add("Stripe-Version", "2014-12-22");
webrequest.Headers.Add("Authorization", String.Concat("Basic ", (Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:", "sk_test_XXXXXXXXXXXXXXXXXXX"))))));
Also, it may help to know that Stripe sends a 400 Bad Request when an expired or invalid card token is sent.