I'm trying to upgrade may program to support the new oAuth that Google uses in the AdWords.
I've my on-line part, which work fine, and get the access tokens (token, secret and consumer key).
My problem is when I try to make a soap request later with those credentials.
A. Which information do I need to save from the OnLine part? so far I save only the accessToken and the accessTokenSecret.
B. How do I use the accessToken, accessTokenSecret and what ever else I've saved in order to make a SOAP requests?
Some info on my process:
Not using the Client Library from Google (too much over head, and so far I didn't needed them)
Using the auto-generated code using VS2005 WSDL on the services I'm using.
C#
The problem is that what you're trying to do is a bit involved. It's got more to do with the oAuth process itself than the AdWords API implementation. I think that this is a case where it would be very beneficial to use the client library.
You actually need to use your token and consumer key to sign the request. This can either be done using RSA-SHA1 or RSA-HMAC; in the first instance you sign it with a private key file and upload the public key to Google; for RSA-HMAC you just use the token and consumer secrets instead of the public/private key.
You can find out more about doing this here, but I would recommend just using Google's implementation unless you're interested in the internals of oAuth. I tried it myself a while ago and found it quite complicated.
Related
I have an app that allow me to read the data from Google Spreadsheet using API Key. I just make HTTP GET to this address and get a response with data.
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A1?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
But when I try to do same to write data using HTTP PUT to address
https://sheets.googleapis.com/v4/spreadsheets/18soCZy9H4ZGuu**********BeHlNY1lD8at-Pbjmf8c/values/Sheet1!A4?valueInputOption=RAW?key=AIzaSyAYJ***********pB-4iKZjYf4y0vhXP8OM
its gives me 401 error.
Code to make PUT request:
using (WebClient wc = new WebClient())
{
byte[] res = wc.UploadData(link, "PUT", Encoding.ASCII.GetBytes(textBox1.Text));
MessageBox.Show(Encoding.Default.GetString(res));
}
Also spreadsheet is fully public with permission to read and write by anyone without auth. My guess is that I can't use API Key to write data to spreadsheet, and only way to do this is using OAuth.
UPDATE:
So i've just tryed Google.Apis.Sheets.v4 to write values, and now i'm almost 100% sure that API Key can't be used to write data to Google Spreadsheet. Well, then I'll use OAuth 2.0.
Well, maybe you are correct and the problem here is the API_KEY itself.
If you check the Sheets API documentation, it is stated that every request your application sends to the Google Sheets API needs to identify your application to Google. There are two ways to identify your application: using an OAuth 2.0 token (which also authorizes the request) and/or using the application's API key. Here's how to determine which of those options to use:
If the request requires authorization (such as a request for an individual's private data), then the application must provide an OAuth 2.0 token with the request. The application may also provide the API key, but it doesn't have to.
If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you.
So meaning either the OAuth 2.0 token or API key will work in your case since the file is public. But the problem is in the PUT request that you are doing, we can assume here that the API key is not working with it. But, we have alternatives for it, and that is the OAuth.
I also found here a related SO question that might help you.
For anyone still hoping for a simple answer, it seems there won't be one - any writing to a sheet, irrespective of the sheets permissions, will require OAuth2:
'This is intentional behavior. While public sheets are anonymously readable, anonymous edits aren't currently supported for a variety of reasons.
In this context, "anyone" == anyone with a google account.' HERE
One option that wasn't mentioned here is to use a service account instead. Service accounts are like users, but without being attached to a person. Instead, they're attached to a project.
Service accounts have an email address as well as a private key. Both can be used to create a JWTClientAuth, and this can be used to authenticate the API while it's being instantiated or to authenticate each and every request.
The advantage of the service account is that it works like an API KEY -- no need to ask a user to copy a URL to the browser and then copy a code back into the application -- but because it can act as an authenticated user, the service account email address can be added to the Google Sheet as an editor. With this in place, the application has full write access to the sheet but without having to deal with authorization codes and refresh tokens and copy/pasting.
You can see a Python example, Python With Google Sheets Service Account Step By Step, and a Node.js example, Accessing Google APIs Using Service Account in Node.js. I followed these examples to get setup.
Since you're using C#, you may find Writing to Google Sheets API Using .NET and a Service Account to be helpful.
This method reads the service account credentials from the JSON file to then instantiate the SheetsService:
private void ConnectToGoogle() {
GoogleCredential credential;
// Put your credentials json file in the root of the solution and make sure copy to output dir property is set to always copy
using (var stream = new FileStream(Path.Combine(HttpRuntime.BinDirectory, "credentials.json"),
FileMode.Open, FileAccess.Read)) {
credential = GoogleCredential.FromStream(stream).CreateScoped(_scopes);
}
// Create Google Sheets API service.
_sheetsService = new SheetsService(new BaseClientService.Initializer() {
HttpClientInitializer = credential, ApplicationName = _applicationName
});
}
Afterwards, you can use the Google Sheets .NET Client Library to write the data.
I have a WCF service that is a relying party for a custom STS, built using WIF. My STS issues holder-of-key tokens to my client application. I have created a new 'backend' WCF service, that I need to call from the existing 'frontend' service. How can I use the incoming secure token in the frontend service to call the backend service, without retrieving a new one from the STS?
So far, in my frontend service, I have no problem accessing the incoming SamlSecurityToken using a custom Saml11SecurityTokenHandler.
After that, I tried two different ways to attach the out of band token to a service call on my target backend service:
Create a custom IssuedSecurityTokenProvider
Use ChannelFactoryOperations.CreateChannelWithIssuedToken
However, both of these attempts result in errors. From what I can tell, it seems to be the same dead end, - they do not accept the signed SamlSecurityToken. It seems that even though both of these methods accept the base SecurityToken class, they both only work if given a GenericXmlSecurityToken instance, instead of a SamlSecurityToken.
Update:
Here is a code sample and the exception details for bullet #1
Update 2:
After doing some more research, the closest thing I can find was an article about using Identity Delegation for WIF/ADFS which basically just uses ActAs tokens, in which the front end service would issue a request to the STS using the token it recieved from the client application. This would require an update to our custom STS, which we're hoping not to do at this time. I'm starting to wonder if the approach that I've illustrated in my diagram is even valid for WIF or WS-Trust?
As it turns out, the the concept of reusing an issued token by a front-end service to call a back-end service IS valid within the confines of WS-Trust protocol. However, for the vast majority of scenarios, it should not be considered a good practice. This is due to security and extensibility concerns. Security-wise, doing so would force both relying parties to use the same token encryption algorithms/keys, and also reduces your ability to authenticate the SAML token's audience restriction. This is exactly why WS-Trust was updated to support Identity Delegation with both ONBEHALFOF and ACTAS tokens. Utilizing either of these will help deal with this exact scenario in a more secure and robust way. It appears that the design of WIF's API follows this line of thinking, which explains why there is no direct API to be found for a front end service to reuse an incoming signed holder-of-key token to call a back-end service.
In conclusion, I have two answers to this question:
A. If you are the owner of your own custom-built STS, you can achieve this scenario outside of the default WIF/WCF pipeline, by following these steps:
In the client requestor application, manually retrieve the token from the STS using either WSTrustChannel or an IssuedSecurityTokenProvider. Notice that the token type will be GenericXmlSecurityToken, or some derivation of that.
Send the token, out-of-band, to the front end service. By out-of-band, I mean send it as an extra contract field, in a message header, or any other way.
Within the front end service, you can easily use the out-of-band token to call the back-end service by using ChannelFactory.CreateChannelWithIssuedToken() or by creating a custom IssuedSecurityTokenProvider. This is not possible when using an incoming bootstrap token, because WIF will always create a bootstrap token as a specific type, such as SamlSecurityToken. Both ChannelFactory and the IssueSecurityTokenProvider will only work with the GenericXmlSecurityToken!
B. Whether you have an out-of-box STS or a custom one, as long as it supports ActAs or OnBehalfOf, you can use proper Identity Delegation.
My conclusions are largely based on the following sources. I hope this ends up helping someone else with similar requirements.
http://www.cloudidentity.com/blog/2008/09/07/delegation-or-traversing-multilayer-architectures/
(Amazing explanation of ACTAS/ONBEHALFOF vs token reuse)
http://msdn.microsoft.com/en-us/library/ee748487.aspx (scroll down to find a comparison of ACTAS and BEHALFOF)
http://docs.oasis-open.org/ws-sx/ws-trust/v1.4/errata01/os/ws-trust-1.4-errata01-os-complete.html
(wstrust protocol, of course)
I've created a web application that uses the OAuth authentication and universal connectors as explained in this tutorial, and started to fiddle around a little to add support for other providers like Yahoo and LinkedIn. So the authentication part works and users are created in the asp.net Membership provider. Also, all the providers return the accesstoken which I supposedly can use to retrieve more information regarding the user.
I'd really like to acquire the profile image, but it seems every provider has a different way of requesting this information. Twitter even describes a way to authorise every request by changing the HTTP header information.
Whilst reading this information on the websites of the various providers I was wondering whether this functionality isn't also already included somewhere in DotNetOpenAuth.AspNet or Microsoft.AspNet.Membership.OpenAuth implementation.
How can I use DotNetOpenAuth.AspNet and/or Microsoft.AspNet.Membership.OpenAuth to request the profile image of the loggedin user using the just acquired accesstoken?
UPDATE in response to Leo's answer
I use the following code to make a call on LinkedIn's API.
string accessToken = extraData["accesstoken"]; // Extra Data received from OAuth containing the accesstoken.
WebRequest request = WebRequest.Create("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token=" + accessToken);
using (WebResponse response = request.GetResponse())
{
// do something with response here.
}
Error message is "The remote server returned an error: (401) Unauthorized.".
What am I doing wrong?
The answer is simple...you can't use any of these. These are wrappers of OAuth and OAuth only specifies how you can authenticate a user. Now, to request the user's profile photo you will need to use the external provider's own API and you will need most likely a valid access token. So, you will need to use one of these implementations of OAuth to authenticate a user and the recieve an access token, store the access token somewhere (usually a cookie) and then use the access token to make sub-sequent calls to the provider's APIs. Examples and links....
Facebook's Graph API allows you to retrieve users profiles
https://developers.facebook.com/docs/graph-api/quickstart/
notice that all examples in the link above will require you to include the access token in a parameter named access_token, for example
https://graph.facebook.com/me?method=GET&format=json&suppress_http_code=1&access_token={your-access-token}
Google...
https://www.googleapis.com/oauth2/v3/userinfo?access_token={your-access-token}
LinkedIn...
https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token={your-access-token}
You can get more specific information from these providers' websites
Let me know if you have any other doubts I might be able to help you since I have implemented stuff like these before.
Cheers, Leo
I'm now building a App that use asp.net webAPI on the server side,I got a problem about the validation:
I want to provide my webAPI for multi-platform,just like browser's javascript,or windows phone ,and so on,so I decide to implicit the validation with HTTP-BASIC,"(forgive my poor English),the problem is ,In the past time.
I always take some User's Information in SESSION,but we know that webAPI with RESTful-style is Session-stateless,so how to store the User's information:
And I get some idea,I hope you can help me to make the right choice,thx a lot
1.
put the information into the browser's cookie except the user's password and other important infos. everytime I make the http-request ,i take the cookies.and on the server-side,I can query the user's infomation.and make other steps.(the sequence will not work on moblie platform,cuz cookies only in browsers)
2.user HTTP-BASIC validation,and everytime the server get the httpRequest,it get the username and password in the HTTP-Headers,and server-side also can query the user's information.
Most REST APIs I've seen handle authentication one of two ways:
HTTP Headers, be it basic auth, or some custom headers to pass credentials. This would be your option 2. This is only really good if you're running over HTTPS, since the credentials will be in clear text in the headers.
Using a pair of tokens, one as an identifier (somewhat like a user name) and one shared secret between the client and the server (somewhat like a password). A hash is then made of the identifier, parts of the request parameters, and the secret. This hash and the identifier is then sent along with the request. The server, knowing the secret, then computes the hash using the same method, and ensures they match (Amazon Web Services uses this method, along with anything using OAuth).
More web APIs seem to be migrating to the second method here, as it is resistant to tampering and replay attacks, unlike basic auth. It is, of course, more complex.
RFC 5849 Section 3.4 for OAuth, while dry reading, goes through the process used for creating the hash, and probably would be a good starting point for implementing, if you desire. A basic implementation in C# is provided on the OAuth Google Code site, and might be a better choice to start with.
I'm Looking at using Amazon S3 and simpleDB in a desktop application.
The main issue I have is that I either need to store my aws credentials in the application or use some other scheme.
I'm guessing that storing them in the application is out of the question as they would be easily picked out.
Another option is to create a web service that creates the aws authentication signature but this has its own issues.
Does the signature require all the data from a file thats being uploaded? If so I would have to transfer all the data twice.
There would then be a central failure point which was one of the main reasons for using aws.
Any ideas?
UPDATE:
I needed to make it a bit clearer that I'm wanting to store my aws credentials in an application handed out to others. DPAPI or any other encryption would be only stop people simply using reflector to get the credentials. Using any encryption still needs the key that is easy to get.
UPDATE 2 - Sept 2011
Amazon have released some details on using the AWS Security Token Service, which allows for authentication without disclosing your secret key. More details are available on this blog post.
Tim, you're indeed hitting on the two key approaches:
NOT GOOD ENOUGH: store the secret key "secretly" in the app. There is indeed a grave risk of someone just picking it out of the app code. Some mitigations might be to (a) use the DPAPI to store the key outside the app binary, or (b) obtain the key over the wire from your web service each time you need it (over SSL), but never store it locally. No mitigation can really slow down a competent attacker with a debugger, as the cleartext key must end up in the app's RAM.
BETTER: Push the content that needs to be protected to your web service and sign it there. The good news is that only the request name and timestamp need to be signed -- not all the uploaded bits (I guess Amazon doesn't want to spend the cycles on verifying all those bits either!). Below are the relevant code lines from Amazon's own "Introduction to AWS for C# Developers". Notice how Aws_GetSignature gets called only with "PutObject" and a timestamp? You could definitely implement the signature on your own web service without having to send the whole file and without compromising your key. In case you're wondering, Aws_GetSignature is a 9-line function that does a SHA1 hash on a concatenation of the constant string "AmazonS3", the operation name, and the RFC822 representation of the timestamp -- using your secret key.
DateTime timestamp = Aws_GetDatestamp();
string signature = Aws_GetSignature( "PutObject", timestamp );
byte[] data = UnicodeEncoding.ASCII.GetBytes( content );
service.PutObjectInline( "MainBucket", cAWSSecretKey, metadata,
data, content.Length, null,
StorageClass.STANDARD, true,
cAWSAccessKeyId, timestamp, true,
signature, null );
EDIT: note that while you can keep the secret key portion of your Amazon identity hidden, the access key ID portion needs to be embedded in the request. Unless you send the file through your own web service, you'll have to embed it in the app.
The main issue I have is that I either need to store my aws credentials in the application or use some other scheme.
Does Windows have a system-wide service similar to Apple's Keychain Manager? If so, put your credentials there. If not, perhaps you can build a watered-down version of it for storing a strongly-encrypted version of your AWS credentials.
Does the signature require all the data from a file thats being uploaded?
The HMAC SHA-1 signature is an encoded encryption of the HTTP request headers. This signature is a hash value and will be very short relative to your data, only 20 bytes long.
You can encrypt the config file and/or use ProtectedData. Here's my blog post on both.
UPDATE: You might be a be to encrypt your app.config as part of an install step. Sample here: http://www.codeproject.com/KB/security/encryptstrings.aspx. Not great, but the best I've found so far.
Will you let anyone that gets a hold of a copy of your program access the data on S3/SimpleDB? If not, you will need your own authentication scheme that's independent from AWS security.
In that case, you could implement a web service that accepts the credentials that you give your customers (a username/password for example, a digital certificate, etc) and then performs the S3/SimpleDB operations that your program requires. That way, the AWS credentials never leave AWS. If a particular user's credentials are compromised, you can cancel those credentials in your web service.