I'm trying to use OAuth2 for Elixir to get an access token to interact with the Google Cloud Storage API. I've been experimenting with the OAuth2 Playground and I can make the requests and they give me back the token. Upon doing a basic GET request (to list the buckets in the project) it all works correctly, but of course I need to get the token automatically inside my app instead of copy pasting.
def token do
client = OAuth2.Client.new([
strategy: OAuth2.Strategy.AuthCode, #default
client_id: "myClientID",
client_secret: "myClientSecret",
site: "https://www.googleapis.com",
redirect_uri: "https%3A%2F%2Fdevelopers.google.com%2Foauthplayground" #I HAVE NO IDEA WHAT SHOULD BE HERE???
])
token =
client
|> OAuth2.Client.put_param(:code, "myAuthCode")
|> OAuth2.Client.get_token!()
I keep getting a "Not Found" message.
I'm quite confused by this, if someone could help I'd be most thankful.
EDIT:
Apparently the request should be something like this:
https://accounts.google.com/o/oauth2/v2/auth?response_type=token&client_id=myClientID&nonce=someRandomStringIthink&scope=https://www.googleapis.com/auth/devstorage.full_control&redirect_uri=WHEREDOIGETTHIS
I still have no idea where to find the Redirect URI. The documentation says it should be in the developer's console.
You can check an example for Google Auth here. It covers everything you need to interact with gcs.
Related
I am currently coding a service on a perl server, that shall send a request to the Firebase Cloud Messaging API, which will then send push notifications to an app instance.
Since FCM is part of the Google API family, an OAuth2 token is required to access the API. During my research I found this perl solution. Because my service is running on an non-Google server environment, I can't use Google Application Default Credentials, but have to provide them manually, so I downloaded a json containing a private key following this description.
Reading the documentation of LWP::Authen::OAuth2 I got a little confused, where to put which parameter from the json into the $oauth2 object, because often different names are used to reference to the same values, like I suspect.
The json related to my firebase project:
{
"type": "service_account",
"project_id": "my_project_id",
"private_key_id": "some_key_id",
"private_key": "-----BEGIN PRIVATE KEY-----very_long_key-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-o8sf4#<my_project_id>.iam.gserviceaccount.com",
"client_id": "some_client_id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o8sf4%40<my_project_id>.iam.gserviceaccount.com"
}
The implementation of the $oauth object looks like this:
my $oauth2 = LWP::Authen::OAuth2->new(
client_id => "Public from service provider",
#probably that will be "some_client_id" from above
client_secret => "s3cr3t fr0m svc prov",
#the "very_long_key"?
service_provider => "Google",
#the "auth_uri"? That's what I would suggest here
#I've read some about the LWP::Authen::OAuth2::ServiceProvider module
#do I have to create an instance of that here?
#if so, which params do I need for that from the json?
redirect_uri => "https://your.url.com/",
#the FCM api I want to call?
# Optional hook, but recommended.
save_tokens => \&save_tokens,
save_tokens_args => [ $dbh ],
# This is for when you have tokens from last time.
token_string => $token_string.
#yes, i copy-pasted that from the docs
);
Now, as a beginner in Perl and a disliker of ambiguous key-values names, i'm a little confused, which value to put where and would be glad if anyone could help me with a guide here, what to put where even if this seems like very rookie question, it is important for me :D. So i'm thankful for every helpful answer!
EDIT
When trying to generate a JSON Web Token manually in my perl service using Crypt::JWT, i came across another trip wire, which made me doubt that the according authentication API from Google "https://www.googleapis.com/auth/firebase.messaging" still accepts Bearer tokens ... I tried the generate my JWT, which seemed to be successful, but the request I sent to the actual FCM API then gave me this:
Request had invalid authentication credentials.
Expected OAuth 2 access token, login cookie
or other valid authentication credential
In the response printed as String I then found this little guy, which confused me a lot:
Client-Warning: Unsupported authentication scheme 'bearer'
Now I'm very unsure, it bearer tokens are still supported for the FCM API, even they are used in an example on the referring docs page. Does anyone have any up-to-date information about that? Thank you very much!
Digging deeper in various documentations and testing some things I realized, that LWP::Authen::OAuth2 is not only a bit of overhead, for creating a very tiny HTTPS request to an OAuth2 protected API, it is currently not possible either.
The caveat is hidden in the service_provider, who hosts the authentication API, that I have to call to authenticate and authorize my service for accessing the actual Firecase Cloud Messaging API. In my case, this is Google, more precisely https://www.googleapis.com/auth/firebase.messaging, also referred to as scope in code.
Now one service provider can provide different authentication APIs for different clients. That's why some service providers require an additional parameter - the client_type or type respectively (for unambiguity I will use client_type) - that also affects the authentication and authorization process via OAuth2.
When creating a new $oauth2-object, and assigning a value to the field service_provider an object of module LWP::Authen::OAuth2::ServiceProvider gets created, that may need also its parameters, like the scope, to define, for which API family you want to be authorized and depending on that a client_type.
Now Google is not a no-name Service Provider, so there already is a prebuild ServiceProvider module, explicitily for Google APIs : LWP::Authen::OAuth2::ServiceProvider::Google. This automatically fills in some parameters of a ServiceProvider object, and holds a hash of available client_types to make sure you use one of them, because depending on the client_type a specific sub-module of ServiceProvider::Google gets created internally.
So I tried to test that like so:
my $oauth2 = LWP::Authen::OAuth2->new(
service_provider => "Google",
scope => "https://www.googleapis.com/auth/firebase.messaging",
client_type => "service_account", #referring to 'type' in the json
client_id => "Public from service provider",
client_secret => "s3cr3t fr0m svc prov",
redirect_uri => "this-server.mydomain.com"
);
Follwing the further description here and sending a request with the built-in LWP::UserAgent object I still got an 401 error UNAUTHENTICATED which confused me a lot. So when I read the documentation the I-don't-know-what time I stepped across this tiny line in the client_types chapter of ServiceProvider::Google:
Service Account
This client_type is for applications that login to the developer's account using the developer's credentials. See https://developers.google.com/accounts/docs/OAuth2ServiceAccount for Google's documentation.
This is not yet supported, and would require the use of JSON Web Tokens to support.
Yep, that kinda knocks out the usage of the whole LWP:Authen::OAuth family to access Firebase APIs, as almost all of them have the client_type => "service_account". Now I have a deadline for my project and can't wait for the module to get extended or extending it myself would exceed my perl skills.
But between tears of desperation there is always a glimpse of hope, as I found this Google docs page with a live-saving addendum, that it's possible to use a JSON Web Token as a Bearer token. Researching for that I found more than one Perl solution to generate JWT from JSONs like a service account, and also this very helpful Stackoverflow answer that showed me the way out of the choke point.
I have to create an webpage where i need to show all the events from the past or upcoming ones from an facebook page. I have to retrive the title, description, image, time and place of every single one and make a box with it.
After that, i have to retrive all the interested people.
I'm a beginner trying to make big things! Thank you very much.
This isnt a simple problem - I have just done this so will give you a start.
You need to use Facebooks Graph API to get the data, you will need to read up on the Graph API and how to use it. Documentation can be found here. Its worth noting due to a security breach the API isn't offering all of its services to new developers currently but you can get most - read about this here.
You can query the API using any language but I suggest do it with python and use the requests module. Alternatively you could use this SDK which makes things a lot easier, trust me! With the SDK you need to get a user access token which you can get from here.
SDK
With the SDK its possible to do this
import facebook
graph = facebook.GraphAPI(access_token="your_token", version="2.12")
event = graph.get_object(id='event_id',
fields='attending_count,declined_count')
print(event['attending_count'])
print(event['declined_count'])
You can add more fields to the fields list. However you can use any language you choose to request the data, as you can just construct your own URL's.
Graph API & Requests
This is how I did it in python using requests to query the API.
import requests, json
#Make a requests session to fetch data from URL's
session = requests.Session()
session.mount('https://', requests.adapters.HTTPAdapter(max_retries=10))
#API base URL
baseUrl = 'https://graph.facebook.com/v2.11/'
#Fields you want to get
fields = ['id', 'name', 'start_time', 'end_time', 'description', 'place']
#replace CLIENT_ID and CLIENT_SECRET with ones from a facebook app (make one)
tokenPath= 'oauth/access_token?client_id={0}&client_secret={1}&&'\
'grant_type=client_credentials'.format(CLIENT_ID, CLIENT_SECRET)
#Get the generated token
token = session.get(baseUrl + tokenPath).json()['access_token']
event = session.get(baseUrl, params={
"ids": "event_id",
"fields": ",".join(fields),
"access_token": token,
}
).json()
print(event["name"])
print(event["place"])
You can make a facebook app here which is where the CLIENT_ID & CLIENT_SECRET come from.
Why would you use URL requests and not the SDK?
Using the SDK the generated user access tokens expire every day, its possible to extend them but only for 60 days. If you want to use the SDK it has a method built in which allows you to extend the token, in order to extend the token you also need to create a facebook app. Its also possible to extend tokens using this website, though I cant vouch for its security - it could potentially access and exploit your data.
To extend the token with SDK:
import facebook
graph = facebook.GraphAPI(user_access_short_lived_token)
extended_token = graph.extend_access_token(app_id, app_secret)
print(extended_token)
Using the URL approach, you create a client access token at runtime (read about user access token vs client access token here) and the token is valid so long as your app is active. So if you are going to use this a lot or want to create it and not have to change/extend tokens then use the URL approach.
Hope this gets you started.
In order to test if the urls I made is correct, I put them in my browser to see if I can GET results.
First, I tried GET /v1/products which should return all types of cars and I put it in the browser like this:
https://api.uber.com/v1/products?server_token=MY_TOKEN&latitude=39.914286&longitude=116.461745
Which works fine and gives me a big JSON of products:
{"products":[{"capacity":4,"product_id":"0ed2dbad-c769-41f5-b66d-0767da627f9e","price_details":{"service_fees":[],"cost_per_minute":0.25,"distance_unit":"km","minimum":10.0,"cost_per_distance":1.5,"base":0.001,"cancellation_fee":8.0,"currency_code":"CNY"},"image":"http:\/\/static.uberx.net.cn\/car-types\/mono\/mono-peoplesuber2.png","short_description":"People's Uber +","display_name":"People\u2019s Uber +","description":"People's Uber +"},{"capacity":4,"product_id":"6bf8dc3b-c8b0-4f37-9b61-579e64016f7a","price_details":{"service_fees":[],"cost_per_minute":0.25,"distance_unit":"km","minimum":10.0,"cost_per_distance":1.5,"base":0.001,"cancellation_fee":8.0,"currency_code":"CNY"},"image":"http:\/\/static.uberx.net.cn\/car-types\/mono\/mono-peoplesuber2.png","short_description":"People's Uber","display_name":"People's Uber","description":"People's Uber"},{"capacity":4,"product_id":"93a40036-2670-4a41-bc59-1e901ca33632","price_details":{"service_fees":[],"cost_per_minute":0.4,"distance_unit":"km","minimum":20.0,"cost_per_distance":2.3,"base":15.0,"cancellation_fee":15.0,"currency_code":"CNY"},"image":"http:\/\/static.uberx.net.cn\/car-types\/mono\/mono-china-uberx.png","short_description":"uberX","display_name":"uberX","description":"The low-cost Uber"},{"capacity":4,"product_id":"259df3b5-e062-4b2b-ab81-3e3fbba4b423","price_details":{"service_fees":[],"cost_per_minute":0.7,"distance_unit":"km","minimum":30.0,"cost_per_distance":3.85,"base":18.0,"cancellation_fee":20.0,"currency_code":"CNY"},"image":"http:\/\/d1a3f4spazzrp4.cloudfront.net\/car-types\/mono\/mono-black.png","short_description":"UberBLACK","display_name":"UberBLACK","description":"The original Uber"},{"capacity":4,"product_id":"d1b0005e-8e33-44f1-b1e5-b3f98138642a","price_details":{"service_fees":[],"cost_per_minute":0.25,"distance_unit":"km","minimum":10.0,"cost_per_distance":1.5,"base":0.001,"cancellation_fee":8.0,"currency_code":"CNY"},"image":"http:\/\/d1a3f4spazzrp4.cloudfront.net\/car-types\/mono\/mono-electric.png","short_description":"Electric Vehicles","display_name":"a","description":"a"}]}
Then, I tried GET /v1/estimates/time, with the SAME COORDINATES and SAME SERVER TOEKN, I made the url like this:
https://api.uber.com/v1/estimates/time?server_token=MY_TOKEN&start_latitude=39.914286&start_longitude=116.461745
But it returns:
{"message":"No authentication provided.","code":"unauthorized"}
If the token has expired it should not work with the Products API which actually is working fine, so I don't think that's the issue. Please help, thanks.
BTW, I am testing Uber API in China in case the location may affect the results here.
The GET /v1/estimates/time endpoint has a regional dependency which is probably why it's not working for the China location. Try it out after creating your app on the China Developer dashboard: https://developer.uber.com.cn/ and using that server token with api.uber.com.cn.
Documentation for this can be found here: https://developer.uber.com/docs/china
I am writing a simple procedure that automatically makes a facebook
post. From what I understand, I need to have a "user access token" to
do this. I am using Koala (but the philosophy is similar for other
libraries). Anyway, I create a new OAuth account:
#oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
The koala instructions then become somewhat unclear. The next two lines are:
#oauth.url_for_oauth_code # generate authenticating URL
#oauth.get_access_token(code) # fetch the access token once you have the code
Where does the "code" variable come from? It doesn't say in the
documentation. Also, does the "get_access_token" method get an "app
access token" or a "user_access_token"? The method name is not clear.
I tried going to the url that the [url_for_oauth_code] method gave me,
but it gives me no code! Where does the "code" variable come from?
On the front page of Koala it states you need to go through the OAuth process described at http://developers.facebook.com/docs/authentication/ (this is an old link but the content within is valid)
Specifically
#oauth.url_for_oauth_code
https://github.com/arsduo/koala/blob/master/lib/koala/oauth.rb#L85
Generates a URL that you need to direct the user to based on the repo it's something like
https://www.facebook.com/dialog/oauth?
client_id={app-id}&
redirect_uri={redirect-uri}&
scope=email
Based on the documentation https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#login, when the response_type is omitted the default response type is code. So the above is equivalent to
https://www.facebook.com/dialog/oauth?
client_id={app-id}&
response_type=code&
redirect_uri={redirect-uri}&
scope=email
So on redirect to redirect-uri, this URL will be appended with the code param which you must handle then supply to
#oauth.get_access_token(code)
The access token is a user access token.
I'm using the Javascript SDK inside a node.js (Express) App.
Connecting Users works fine, but the connection does not persist between page reloads.
Is this the default behaviour, or is the User supposed to stay connected in new requests?
Do I have to use OAuth token authentication and if so, how can this be done with the JS-SDK?
Inside the "Permission"-Popup, Users are already logged in with soundlcoud, though.
(just have to click the "connect" button each time)
Figured I'd share my answer for those who are unsatisfied with the current answers for automated oauth:
Retrieving access_token:
I had to define get and set cookie functions and then I use the functions to set and retrieve a function holding the access token. I'm not going to give these functions for conciseness but you can easily find them with a google search. I then use this line of code to get the SC access token (once the user has authenticated for the first time)
SC.accessToken()
Setting token:
So this is kind of just an elephant in the room in my opinion that for some reason no one has mentioned. How in the **** do you connect w/ SC using the access token? Do you set it as oauth param? On each call pass it? Well, after experimenting with putting the parameter in every single place I could think, I found out you have to do something like this:
SC.initialize({
client_id: '[removed for security reasons]',
client_secret: '[removed for security reasons]',
redirect_uri: '[removed for security reasons]',
access_token: getCookie("sc_lm2"),
scope: 'non-expiring'
});
//Where "sc_lm2" is the name of my cookie
Hope the helps! Took me a while to figure this out for such a simple thing
EDIT
Using PHP and Wordpress:
$json = wp_remote_get("http://api.soundcloud.com/users/[user_id]/tracks.json?client_id=[client_id]");
$soundcloudData = json_decode($json['body'],true);
(substitue cURL functionality if you're not using Wordpress). #krafty I assume you would just change the endpoint from "/tracks" to "/users" but I can't say I have ever really needed to grab anything but tracks using the Soundcloud API. Hope this helps, though I'm not sure I fully understand what it is that you are trying to accomplish (or rather, how exactly you're going about it) - are you trying to allow user logins? If you want to explain fully what you're trying to accomplish and the steps you're taking I'd be happy to take a crack at it.
Yep, this is the way to do it, officially. :)
For the Next SoundCloud site, we store the token in localStorage (until the user logs out, of course). In each AJAX request to the API from the front end, we put the oauth token in a request header:
jqXHR.setRequestHeader('Authorization', 'OAuth ' + the_oauth_token);