I'm trying to integrate Withings with a rails apps. I'm using an Omniauth provider someone wrote called omniauth-withings. I was able to configure the provider to allow me to visit /auth/withings which redirects to the Withings authorization page. After I allow access, the browser is redirected to the callback url /auth/withings/callback. I have this routed to a controller action that attempts to get the measurement data from Withings using the simplificator-withings gem.
Withings.consumer_secret = ENV['withings_app_key']
Withings.consumer_key = ENV['withings_app_secret']
auth_hash = request.env['omniauth.auth']
user_id = auth_hash.extra.raw_info.body.users.first.id
withings_user = User.authenticate(user_id, auth_hash.credentials.token, auth_hash.credentials.secret)
measurements = withings_user.measurement_groups(:device => Withings::SCALE)
The problem happens when I call User.authenticate(), I get this:
An unknown error occurred - Status code: 2555
Is there something I'm missing here?
I was getting the same error with a django app. It turns out I was using the wrong token and secret. I was using the oauth_token and oauth_token_secret returned from step 1 of the authorization process, rather than the oauth_token and oauth_token_secret from step 3. Make sure you are using the values from step 3. The API documentation shows the same values returned from these calls, but they will be different. Hopefully this helps you too.
Related
I am trying to use etsy API and validate with oauth gem. I have successfully got a successful token by doing this in the first request url:
scopes = ["email_r", "feedback_r", "listings_r", "transactions_r"]
oauth_consumer = OAuth::Consumer.new(Rails.application.secrets.etsy_api_key,
Rails.application.secrets.etsy_api_secret,
site: "https://openapi.etsy.com/v2"
)
oauth_consumer.options[:request_token_path] = "/oauth/request_token?scope=#{scopes.join('%20')}"
request_token = oauth_consumer.get_request_token(oauth_callback: new_etsy_authentication_url)
redirect_to request_token.params[:login_url]
Then the user passes the etsy validation pages and on callback url I have the following:
current_user.update(etsy_auth: {
oauth_token: params["oauth_token"],
oauth_verifier: params["oauth_verifier"],
oauth_created_at: Time.current.to_i
})
Where I save etsy oauth_token and oauth_verifier successfully.
The problem starts after that. I've tried many things to do a request to user but I always got oauth_token=rejected. Here a sample of what I've done so far:
oauth_consumer = OAuth::Consumer.new(Rails.application.secrets.etsy_api_key,Rails.application.secrets.etsy_api_secret,site: "https://openapi.etsy.com/v2")
access_token = OAuth::AccessToken.new(oauth_consumer, oauth_token: current_user.etsy_auth["oauth_token"], oauth_secret: current_user.etsy_auth["oauth_verifier"])
access_token.request(:get, "/users/__SELF__")
Should I do another request before that, to actually got another temporary oauth_token and oauth_secret?
I've tried doing this:
request_token = oauth_consumer.get_request_token and I got a temporary oauth_token and oauth_token_secret as well oauth_consumer_key (which I am not sure how I should use). I got the temporary tokens and tried many combinations without much success, I am always getting oauth_token=rejected . I've still haven't figured out if and where should I use oauth_consumer_key and if oauth_verifier is actually the oauth_secret.
What am I missing? Any help is appreciated.
I finally managed to find what needed to do. After the initial request to Etsy, I have to store oauth_token and oauth_secret. Then Etsy returns as well the oauth_verifier.
For fetching and doing each request after that, you need to send all three of them to work.
I am using Rails + Garb Gem (Sija Branch) + omniauth-google-oauth2 Gem and I can successfully authenticate with the Google Analytics API and extract data that our app is generating when using a user login, e.g.:
Garb::Session.login('USERNAME', '<PASSWORD>')
I can then use Garb to connect to the Analytics Profile I want and pull the data from it and display some charts on a webpage. This all works fine.
However, I want to use oAuth2 to authenticate with Analytics which is why I had to install the Sija branch of the Garb Gem from Github (it supports oAuth2) and I also installed the omniauth-google-oauth2 Gem. Now in theory I should be able to authenticate using the following code:
Garb::Session.access_token = access_token # an instance of OAuth2::Client
It's at this point that it gets a little hazy for me and I would greatly appreciate some guidance. Here's how far I have gotten:
1) I created a Project in the Google API console and turned on Analytics API under Services
2) This provided me with a Client ID and Client Secret
3) I came across this code which I could populate with the ID and Secret above:
client = OAuth2::Client.new(
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => '/o/oauth2/auth',
:token_url => '/o/oauth2/token'
})
4) Then there is the next bit of code:
response = OAuth2::AccessToken.new(
client,
STORED_TOKEN, {
refresh_token: STORED_REFRESH_TOKEN,
expires_at: STORED_EXPIRES_AT
})
5) and then in theory connect with:
Garb::Session.access_token = response
The problem I have is I don't have the token information in Point (4) above. It seems to me that with oAuth2 I need to do a "handshake" once and print out the return token values? Perhaps through Rails code which prints the values returned out and then paste the token values into a constant in the Rails app so that I can use them in the above code? I really am confused. As I mentioned earlier, the web app works fine using the user login authentication. All the web app is doing is authenticating with analytics, pulling down some data and drawing a chart. But I am stuck converting it over to oAuth2 as I just do not know how to get the Access Token that the Garb Gem is looking for. I should also note that this is not a public website with multiple users authenticating, this is a CMS website that is connecting to our own Analytics data.
I have seen some partial snippets of aspects of this but not a fully explained or working example. I would really appreciate any guidance and help with this question.
Many thanks in advance,
JR
I've soldiered through this over the last few weeks, so let me share what worked:
To use Oauth2 you need to get a 'refresh token' that you use to 're-authenticate' with google each time you make an API call. The steps for this are as follows:
1) Setup your account in the API console - https://code.google.com/apis/console/b/0/ (seems like you've done that well)
2) In your API account, make sure you have a redirect URI pointing back to your application:
http://some-url.com/auth/google_oauth2/callback
http://localhost:3000/auth/google_oauth2/callback
Note here that google won't let you call back to your local machine as 0.0.0.0:3000... so you'll need to use localhost explicitly
3) In your route file, tie that redirect url to an action in the controller where you're going to create the project or authentication
match '/auth/:provider/callback' => 'authentications#create'
The ':provider' simply lets you match on multiple types of oauth, but you could just put 'google_oauth2' there as well.
4) Now create that action in your controller
def create
auth = request.env["omniauth.auth"]
params = request.env["omniauth.params"]
project = Project.find(params['project_id'])
Authentication.create(:project_id => project.id, :provider => auth['provider'], :uid => auth['uid'], :access_token => auth['credentials']['refresh_token'])
flash[:notice] = "Authentication successful."
redirect_to owner_view_project_path(project)
end
5) The controller action should retrieve the relevant fields from the response object (details of response object here: https://github.com/zquestz/omniauth-google-oauth2) - in particular, you need to get the 'refresh_token' and save that to your project or authentication object - if you haven't added an 'access_token' attribute to the desired object, go do that now with a migration, then start saving the refresh token to that attribute
6) Now when you're ready to call that particular authentication and get API data for it, you can load up that object where you saved the access token, and use that to get a new session with the google API as follows:
#authentication = Authentications.find(params[:id])
client = OAuth2::Client.new GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => "/o/oauth2/auth",
:token_url => "/o/oauth2/token",
}
response = OAuth2::AccessToken.from_hash(client, :refresh_token => #authentication.access_token).refresh!
Garb::Session.access_token = response
#profiles = Garb::Management::Profile.all
What this code did was create an OAuth2 access token (response) by specifying the client and then a refresh_token, then calling 'refresh!' to get a refreshed access token... then use that access token to establish your Garb session, then call down all the profiles for a given account using the Gard::Management::Profile.all
Hope this helps - let me know if you have questions!
Just a note on what worked for me in:
For steps 3, 4 & 5 I used cURL instead to retrieve the Access/Refresh token. Step 6 is then the same for me (using the Sija branch of the Garb Gem). So using cURL:
Using the details associated with your Google app POST the following using cURL:
curl --data "code=<APP_CODE>&redirect_uri=http://localhost:3000/oauth2callback&client_id=<CLIENT_ID>.apps.googleusercontent.com&scope=&client_secret=<CLIENT_SECRET>&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token
The response takes the form:
{
"access_token" : "<ACCESS_TOKEN>",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "<REFRESH_TOKEN>"
}
which you can plug into the Garb Gem as per part 6.
The answer by #CamNorgate is valid.
If you don't have a "refresh_token" back from Omniauth on the callback make sure you are correctly initializing :google_oauth2
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{ :scope=>"https://www.google.com/m8/feeds, https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile",
:approval_prompt=>"force", access_type="offline"
}
end
Make sure to include :approval_prompt=>"force", access_type="offline" in order for the refresh_token to be sent back. The refresh_token is only provided on the first authorization from the user.
Our Rails 3 app uses facebook-omniauth, allowing users to authenticate with facebook.
It'd be nice to use as much of the web based authentication system as possible, so I tried following the answer (the one that hasn't been down-voted) to this SO question but I can't get it to work.
The gist of the answer is:
omniauth-facebook will handle requests to the callback endpoint with an access_token parameter without any trouble. Easy :)
So to test this, in my browser I'm issuing the following request:
/users/auth/facebook_api/callback?access_token=BAAB...
But in my server log, I see:
(facebook) Callback phase initiated.
(facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
{"error":{"message":"Missing authorization code","type":"OAuthException","code":1}}
I can't figure out what I'm doing wrong. Is the fact that I'm trying to do this through the browser to test messing something up? Any other ideas on how I can reuse my www based auth logic for my ios app?
UPDATE:
I'm not sure, but I'm following this guide in order to have multiple facebook omniauth strategies, one for www and another for mobile.
I never found a solution in line with what I was asking originally, but here is how I solved it: Take the access token you get on the iPhone and send it up to your server and perform the login manually.
def facebook_login
graph = Koala::Facebook::API.new(params[:user][:fb_access_token])
profile = graph.get_object('me')
omniauth = build_omniauth_hash(profile)
#user = User.find_or_create_for_facebook_oauth(omniauth)
end
On www we already had a method called find_or_create_for_facebook_oauth, and that took the result from Omniauth and either found the user or created a new one. In order to utilize that method for mobile, I had to build up a similar structure by hand so I could pass it as an argument.
def build_omniauth_hash(profile)
struct = OpenStruct.new
struct.uid = profile['id']
struct.info = OpenStruct.new
struct.info.email = profile['email']
struct.info.image = "http://graph.facebook.com/#{profile['id']}/picture?type=square"
struct.info.first_name = profile['first_name']
struct.info.last_name = profile['last_name']
struct.info.bio = profile['bio']
struct.info.hometown = profile['hometown']['name'] if profile['hometown']
struct.info.location = profile['location']['name'] if profile['location']
struct
end
I want use this API in rails.
It says should include an Authorization header.(use oauth2)
So I use google-api-ruby-client this lib like below.
I write below code by this sample.
#client = Google::APIClient.new
#client.authorization.client_id = CONSUMER_KEY
#client.authorization.client_secret = CONSUMER_SECRET
#client.authorization.scope = 'https://apps-apis.google.com/a/feeds/domain/'
#client.authorization.redirect_uri = "http://#{request.host}:#{request.port.to_s}
/google_app/oauth2callback"
redirect_to #client.authorization.authorization_uri.to_s
But it cause redirect_uri_mismatch error.
I don't know whether my usage is correct.
Note:
Before use this API, I have logined with Google Openid successfully.
might be a duplicate of OAuth 2.0 sample error when accessing Google API
the problem there is, that the javascript is missing the port-number.
Does anyone have sample code or clear instructions on how to use libEtPan to connect to a GMail account using OAuth? I couldn't find anything.
Details for OAuth in GMail are here: http://code.google.com/apis/gmail/oauth/
libetpan has some documentation in its header files, for IMAP it's in https://github.com/dinhviethoa/libetpan/blob/master/src/low-level/imap/mailimap_oauth2.h
/*
mailimap_oauth2_authenticate()
Authenticates the client using using an oauth2 token.
To gather a deeper understanding of the OAuth2 aunthentication
process refer to: https://developers.google.com/gmail/xoauth2_protocol
For a quick start you may follow this brief set of steps:
1. Set up a profile for your app in the Google
API Console: https://code.google.com/apis/console
2. With your recently obtained client_id and secret
load the following URL (everything goes ina single line):
https://accounts.google.com/o/oauth2/auth?client_id=[YOUR_CLIENT_ID]&
redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&
response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&
&access_type=offline
3. The user most follow instructions to authorize application access
to Gmail.
4. After the user hits the "Accept" button it will be redirected to another
page where the access token will be issued.
5. Now from the app we need and authorization token, to get one we issue a POST request
the following URL: https://accounts.google.com/o/oauth2/token using these parameters:
client_id: This is the client id we got from step 1
client_secret: Client secret as we got it from step 1
code: This is the code we received in step 4
redirect_uri: This is a redirect URI where the access token will be sent, for non
web applications this is usually urn:ietf:wg:oauth:2.0:oob (as we got from step 1)
grant_type: Always use the authorization_code parameter to retrieve an access and refresh tokens
6. After step 5 completes we receive a JSON object similar to:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"refresh_token":"1/fFAGRNJrufoiWEGIWEFJFJF",
"expires_in":3920,
"token_type":"Bearer"
}
The above output gives us the access_token, now we need to also retrieve the user's e-mail,
to do that we need to perform an HTTP GET request to Google's UserInfo API using this URL:
https://www.googleapis.com/oauth2/v1/userinfo?access_token=[YOUR_ACCESS_TOKEN]
this will return the following JSON output:
{
"id": "00000000000002222220000000",
"email": "email#example.com",
"verified_email": true
}
#param session IMAP session
#param auth_user Authentication user (tipically an e-mail address, depends on server)
#param access_token OAuth2 access token
#return the return code is one of MAILIMAP_ERROR_XXX or
MAILIMAP_NO_ERROR codes
*/
LIBETPAN_EXPORT
int mailimap_oauth2_authenticate(mailimap * session, const char * auth_user,
const char * access_token);
LIBETPAN_EXPORT
int mailimap_has_xoauth2(mailimap * session);
I haven't tried it out myself yet, but when I get around to implement it I'll post a link of the implementation.
Update March 2021
I finally got around to implement support for Google OAuth 2.0 in my email client nmail now. The commit can be viewed here but essentially I ended up doing steps 2-6 above in a separate external script, as libetpan does not do the token generation/refresh for us. The token handling is fairly straight-forward - see oauth2nmail.py for example.