Twilio IP messaging set up with JS SDK - twilio

I'm having some serious troubles setting up IPMessaging services with Twilio.
I've set up the backend (PHP) for user authentication. I'm able to generate a Token with the Twillio Library.
I've also set up the user related APIs, in order to create/update/delete users for the Chat Service from the backend. I had to do this with curl however, because the library kept returning fatal errors, even if I downloaded the PHP SDK via composer and simply copy/pasted the docs examples.
I've finally got to the point in which I can create users via the backend, and then create tokens with the ServiceSId, user Identity and deviceId.
On the JS client side it's again impossible to set up the service, as following the Docs results in multiple errors.
I have first included the JS SDK from Twilio CDN (same v. of the docs).
I have then tried to initialize the client as so:
var token = gottenFromOurAPI;
var chatClient = new Twilio.Chat.Client(token);
chatClient.initiazlie()
.then(testchat);
function testchat(){ };
(as explained here: https://www.twilio.com/docs/api/chat/guides/initializing-sdk-clients)
I get this in the console:
angular.js:13920 chatClient: t {_events: Object, _maxListeners: undefined, _config: e, _channelsPromise: Promise, _channels: t…}
angular.js:13920 TypeError: chatClient.getChannels is not a function
at testchat (supportchat.controller.js:33)
at processQueue (angular.js:16383)
at angular.js:16399
at Scope.$eval (angular.js:17682)
at Scope.$digest (angular.js:17495)
at Scope.$apply (angular.js:17790)
at done (angular.js:11831)
at completeRequest (angular.js:12033)
at XMLHttpRequest.requestLoaded (angular.js:11966)(anonymous function) # angular.js:13920(anonymous function) # angular.js:10467processQueue # angular.js:16391(anonymous function) # angular.js:16399$eval # angular.js:17682$digest # angular.js:17495$apply # angular.js:17790done # angular.js:11831completeRequest # angular.js:12033requestLoaded # angular.js:11966
twilio-chat.min.js:246 POST https://cds.twilio.com/v3/Lists 401 (Unauthorized)(anonymous function) # twilio-chat.min.js:246t # twilio-chat.min.js:196s # twilio-chat.min.js:246p.post # twilio-chat.min.js:246f.post # twilio-chat.min.js:246value # twilio-chat.min.js:245value # twilio-chat.min.js:244(anonymous function) # twilio-chat.min.js:244
twilio-chat.min.js:150 Chat E: Failed to create session Objectbody: Objectdescription: "Unauthorized"headers: ObjectContent-Type: "application/json"Twilio-Request-Id: "RQb761bc6d95034e23b4ba244e965334d4"__proto__: Objectstatus: 401__proto__: Objectvalue # twilio-chat.min.js:150(anonymous function) # twilio-chat.min.js:150
:3000/#/dashboard:1 Uncaught (in promise) Object {status: 401, description: "Unauthorized", headers: Object, body: Object}
:3000/#/dashboard:1 Uncaught (in promise) Objectbody: Objectdescription: "Unauthorized"headers: Objectstatus: 401__proto__: Object
:3000/#/dashboard:1 Uncaught (in promise) Objectbody: Objectdescription: "Unauthorized"headers: Objectstatus: 401__proto__: Object
Since I'm having so many troubles simply setting up the thing, any advices on alternative (better) services?
 EDIT - PHP CODE FOR TOKEN CREATION
$endpointId = $this->idService . ':' . $this->idUser . ':' . $this->idDevice;
// Create access token, which we will serialize and send to the client
$token = new AccessToken(
TWILIO_ACCOUNT_SID,
TWILIO_API_KEY,
TWILIO_API_SECRET,
3600,
$this->idUser
);
// Grant access to IP Messaging
$grant = new IpMessagingGrant();
$grant->setServiceSid($TWILIO_IPM_SERVICE_SID);
$grant->setEndpointId($endpointId);
$token->addGrant($grant);
// return serialized token and the user's randomly generated ID
$tokenArr = [
'identity' => $this->idUser,
'token' => $token->toJWT(),
];
return $tokenArr;

Related

Google OAuth 2.0 failing with Error 400: invalid_request for some client_id, but works well for others in the same project

We have some apps (or maybe we should call them a handful of scripts) that use Google APIs to facilitate some administrative tasks. Recently, after making another client_id in the same project, I started getting an error message similar to the one described in localhost redirect_uri does not work for Google Oauth2 (results in 400: invalid_request error). I.e.,
Error 400: invalid_request
You can't sign in to this app because it doesn't comply with Google's
OAuth 2.0 policy for keeping apps secure.
You can let the app developer know that this app doesn't comply with
one or more Google validation rules.
Request details:
The content in this section has been provided by the app developer.
This content has not been reviewed or verified by Google.
If you’re the app developer, make sure that these request details
comply with Google policies.
redirect_uri: urn:ietf:wg:oauth:2.0:oob
How do I get through this error? It is important to note that:
The OAuth consent screen for this project is marked as "Internal". Therefore any mentions of Google review of the project, or publishing status are irrelevant
I do have "Trust internal, domain-owned apps" enabled for the domain
Another client id in the same project works and there are no obvious differences between the client IDs - they are both "Desktop" type which only gives me a Client ID and Client secret that are different
This is a command line script, so I use the "copy/paste" verification method as documented here hence the urn:ietf:wg:oauth:2.0:oob redirect URI (copy/paste is the only friendly way to run this on a headless machine which has no browser).
I was able to reproduce the same problem in a dev domain. I have three client ids. The oldest one is from January 2021, another one from December 2021, and one I created today - March 2022. Of those, only the December 2021 works and lets me choose which account to authenticate with before it either accepts it or rejects it with "Error 403: org_internal" (this is expected). The other two give me an "Error 400: invalid_request" and do not even let me choose the "internal" account. Here are the URLs generated by my app (I use the ruby google client APIs) and the only difference between them is the client_id - January 2021, December 2021, March 2022.
Here is the part of the code around the authorization flow, and the URLs for the different client IDs are what was produced on the $stderr.puts url line. It is pretty much the same thing as documented in the official example here (version as of this writing).
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
def user_credentials_for(scope, user_id = 'default')
token_store = Google::Auth::Stores::FileTokenStore.new(:file => token_store_path)
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(base_url: OOB_URI)
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts url
code = $stdin.readline.chomp
$stderr.puts "-----------------------------------------------"
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
credentials
end
Please see https://stackoverflow.com/a/71491500/1213346 for a "proper" solution. This answer is just an ugly workaround that the community seems to like.
...
Here is a cringy workaround for this situation:
Replace urn:ietf:wg:oauth:2.0:oob with http://localhost:1/ in the code posted in the question. This makes the flow go through, my browser gets redirected and fails and I get an error messages like:
This site can’t be reached
The webpage at http://localhost:1/oauth2callback?
code=4/a3MU9MlhWxit8P7N8QsGtT0ye8GJygOeCa3MU9MlhWxit8P7N8QsGtT0y
e8GJygOeC&scope=email%20profile%20https... might be temporarily
down or it may have moved permanently to a new web address.
ERR_UNSAFE_PORT
Now copy the code code value from the failing URL, paste it into the app, and voila... same as before :)
P.S. Here is the updated "working" version:
def user_credentials_for(scope, user_id = 'default')
token_store = Google::Auth::Stores::FileTokenStore.new(:file => token_store_path)
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store, "http://localhost:1/")
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts url
$stderr.puts
$stderr.puts "At the end the browser will fail to connect to http://localhost:1/?code=SOMECODE&scope=..."
$stderr.puts "Copy the value of SOMECODE from the address and paste it below"
code = $stdin.readline.chomp
$stderr.puts "-----------------------------------------------"
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code)
end
credentials
end ```
I sent off an email to someone on the Google OAuth team. This is the gist of their response.
As I feared your issue is related to Making Google OAuth interactions safer by using more secure OAuth flows
The current recommendation from google is to move to use localhost/loopback redirects as recommended here: instructions-oob or use the OAuth for devices flow if you are using non-sensitive scopes and need a headless solution.
A solution for python.
As google_auth_oauthlib shows, InstalledAppFlow.run_console has been deprecated after Feb 28, 2022. And if you are using google-ads-python, you can just replace flow.run_console() by flow.run_local_server().
Let me post the "proper" solution as a separate answer, which is to actually follow the recommended procedure by implementing an HTTP listener in the ruby app. If this is running on an offline machine the listener will never get the code, but you can still paste the code from the failing URL.
require 'colorize'
require 'sinatra/base'
# A simplistic local server to receive authorization tokens from the browser
def run_local_server(authorizer, port, user_id)
require 'thin'
Thin::Logging.silent = true
Thread.new {
Thread.current[:server] = Sinatra.new do
enable :quiet
disable :logging
set :port, port
set :server, %w[ thin ]
get "/" do
request = Rack::Request.new env
state = {
code: request["code"],
error: request["error"],
scope: request["scope"]
}
raise Signet::AuthorizationError, ("Authorization error: %s" % [ state[:error] ] ) if state[:error]
raise Signet::AuthorizationError, "Authorization code missing from the request" if state[:code].nil?
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id,
code: state[:code],
scope: state[:scope],
)
[
200,
{ "Content-Type" => "text/plain" },
"All seems to be OK. You can close this window and press ENTER in the application to proceed.",
]
end
end
Thread.current[:server].run!
}
end
# Returns user credentials for the given scope. Requests authorization
# if requrired.
def user_credentials_for(scope, user_id = 'default')
client_id = Google::Auth::ClientId.new(ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'])
token_store = Google::Auth::Stores::FileTokenStore.new(:file => ENV['GOOGLE_CREDENTIAL_STORE'])
port = 6969
redirect_uri = "http://localhost:#{port}/"
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store, redirect_uri)
credentials = authorizer.get_credentials(user_id)
if credentials.nil? then
server_thread = run_local_server(authorizer, port, user_id)
url = authorizer.get_authorization_url
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id.yellow}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts
$stderr.puts url.yellow.bold
$stderr.puts
$stderr.puts "⚠️ If you are authorizing on a different machine, you will have to port-forward"
$stderr.puts "so your browser can reach #{redirect_uri.yellow}"
$stderr.puts
$stderr.puts "⚠️ If you get a " << "This site can't be reached".red << " error in the browser,"
$stderr.puts "just copy the failing URL below. Copy the whole thing, starting with #{redirect_uri.yellow}."
$stderr.puts "-----------------------------------------------"
code = $stdin.readline.chomp
server_thread[:server].stop!
server_thread.join
credentials = authorizer.get_credentials(user_id)
# If the redirect failed, the user must have provided us with a code on their own
if credentials.nil? then
begin
require 'uri'
require 'cgi'
code = CGI.parse(URI.parse(code).query)['code'][0]
rescue StandardException
# Noop, if we could not get a code out of the URL, maybe it was
# not the URL but the actual code.
end
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id,
code: code,
scope: scope,
)
end
end
credentials
end
credentials = user_credentials_for(['https://www.googleapis.com/auth/drive.readonly'])
In short, we run a web server expecting the redirect from the browser. It takes the code the browser sent, or it takes the code pasted by the user.
For headless Python scripts that need sensitive scopes, continuing to use run_console now produces the following (and the flow likely fails):
DeprecationWarning: New clients will be unable to use `InstalledAppFlow.run_console` starting on Feb 28, 2022. All clients will be unable to use this method starting on Oct 3, 2022. Use `InstalledAppFlow.run_local_server` instead. For details on the OOB flow deprecation, see https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob
The official solution is to migrate to a flow that spins up a local server to handle the OAuth redirect, but this will not work on remote headless systems.
The solution Google adopted in gcloud is to run a local server on the same machine as the user's browser and then have the user copy the redirect URL requested from this local server back to the remote machine. Note that this requires having gcloud installed both on the remote machine and on the user's workstation.
As a hack for situations where installing a script to echo back the redirect URL on the workstation is not practical, we can use a redirect URL that is guaranteed to fail and just have the user copy back the URL of the error page on which they will land after authorization is complete.
import urllib
from google_auth_oauthlib.flow import InstalledAppFlow
def run_console_hack(flow):
flow.redirect_uri = 'http://localhost:1'
auth_url, _ = flow.authorization_url()
print(
"Visit the following URL:",
auth_url,
"After granting permissions, you will be redirected to an error page",
"Copy the URL of that error page (http://localhost:1/?state=...)",
sep="\n"
)
redir_url = input("URL: ")
query = urllib.parse.urlparse(redir_url).query
code = urllib.parse.parse_qs(query)['code'][0]
flow.fetch_token(code=code)
return flow.credentials
scopes = ['https://www.googleapis.com/auth/drive.file']
flow = InstalledAppFlow.from_client_secrets_file(secrets_file, scopes)
credentials = run_console_hack(flow)
We could also ask the user to pass back the code query string parameter directly but that is likely to be confusing and error-prone.
The use of 1 as the port number means that the request is guaranteed to fail, rather than potentially hit some service that happens to be running on that port. (e.g. Chrome will fail with ERR_UNSAFE_PORT without even trying to connect)
"Hello world" for this error:
Generating an authentication URL
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
const {google} = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// generate a url that asks permissions for Blogger and Google Calendar scopes
const scopes = [
'https://www.googleapis.com/auth/blogger',
'https://www.googleapis.com/auth/calendar'
];
const url = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
// If you only need one scope you can pass it as a string
scope: scopes
});
If something goes wrong the first step is to Re Check again the three values of the google.auth.OAuth2 function.
1 of 2
Compare to the store values under Google APIs console:
YOUR_CLIENT_ID
YOUR_CLIENT_SECRET
YOUR_REDIRECT_URL -
For example http://localhost:3000/login
2 of 2 (environment variables)
A lot of times the values store inside .env. So re-check the env and the output under your files - for example index.ts (Even use console.log).
.env
# Google Sign-In (OAuth)
G_CLIENT_ID=some_id_1234
G_CLIENT_SECRET=some_secret_1234
PUBLIC_URL=http://localhost:3000
index
const auth = new google.auth.OAuth2(
process.env.G_CLIENT_ID,
process.env.G_CLIENT_SECRET,
`${process.env.PUBLIC_URL}/login`
);
SUM:
Something like this will not work
const oauth2Client = new google.auth.OAuth2(
"no_such_id",
"no_such_secret",
"http://localhost:3000/i_forgot_to_Authorised_this_url"
);
I've fixed this problem with recreate my App in google console. And I think the problem was with redirect_url. I had this problem when I was using 'Android' type of App in google console (in this case you can't configure redirect url). In my android App I'm using google auth with WebView so the best option here use use 'Web' type for your app in google console.
In my case, had to update plugins. by running following command-
bundle exec fastlane update_plugins
With this redirect uri was getting created properly as
https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com&include_granted_scopes=true&redirect_uri=http://localhost:8081&response_type=code&scope=https://www.googleapis.com/auth/cloud-platform&state=2ce8a59b2d403f3a89fa635402bfc5c4
steps.oauth.v2.invalid_request 400 This error name is used for multiple different kinds of errors, typically for missing or incorrect parameters sent in the request. If is set to false, use fault variables (described below) to retrieve details about the error, such as the fault name and cause.
GenerateAccessToken GenerateAuthorizationCode
GenerateAccessTokenImplicitGrant
RefreshAccessToken
Google Oauth Policy

How to implement google smart lock one tap sign in

I want to implement Google's One tap sign-up and automatic sign-in in my website with the help of documentation https://developers.google.com/identity/one-tap/web/ but I am getting confused on how to implement in python.
def smartlock(request):
try:
CLIENT_ID='*******'
csrf_token_cookie = self.request.cookies.get('g_csrf_token')
if not csrf_token_cookie:
webapp2.abort(400, 'No CSRF token in Cookie.')
csrf_token_body = self.request.get('g_csrf_token')
if not csrf_token_body:
webapp2.abort(400, 'No CSRF token in post body.')
if csrf_token_cookie != csrf_token_body:
webapp2.abort(400, 'Failed to verify double submit cookie.')
# Specify the CLIENT_ID of the app that accesses the backend:
idinfo = id_token.verify_oauth2_token(csrf_token_cookie, requests.Request(), CLIENT_ID)
# Or, if multiple clients access the backend server:
# idinfo = id_token.verify_oauth2_token(token, requests.Request())
# if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
# raise ValueError('Could not verify audience.')
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')
# If auth request is from a G Suite domain:
# if idinfo['hd'] != GSUITE_DOMAIN_NAME:
# raise ValueError('Wrong hosted domain.')
# ID token is valid. Get the user's Google Account ID from the decoded token.
userid = idinfo['sub']
except ValueError:
# Invalid token
pass
'''
As mentioned in the 'Key Point' section of this page: The ID token is returned in the credential field, instead of the g_csrf_token field.
So, you need to get the idinfo with the code as below:
credential = self.request.get('credential')
idinfo = id_token.verify_oauth2_token(credential, requests.Request(), CLIENT_ID)
The g_csrf_token parameter is for different purpose. It makes sure the request was submitted from a page in your own domain, so as to prevent the cross-site-request-forge attacks.

How to replace an existing video in Dailymotion API?

I'm building an app using Dailymotion API
https://developer.dailymotion.com/
with official python sdk
https://github.com/dailymotion/dailymotion-sdk-python
and writing video CRUD(Create, Read, Update, Delete) simply.
Create, Read, Delete were succeccfully completed, but encountering a weird response of the API about "Update".
Here is a simplified snippet of my code in my Django project,
def update(request, video_id):
user = request.user
video = get_object_or_404(Video, pk=video_id)
file_path = # define file_path from uploaded file object
input_title = # define input_title from post request
input_description = # define input_description from post request
d = get_dailymotion_d(user)
if d == 'revoked':
# do actions of logout and delete the user
try:
# get url for upload with the file_path on my server
url = d.upload(file_path)
# update
response = d.post('/video/' + video.dailymotion_video_id, {'url': url, 'title': input_title, 'description': input_description, 'published': 'true', 'channel': 'creation'})
# delete the video from my sever
video.file_field.delete()
return redirect('/videos')
except Exception as e:
print(e.args)
print('update failed..!')
# delete the video from my server
video.file_field.delete()
return redirect('/videos')
def get_dailymotion_d(user):
d = dailymotion.Dailymotion()
d.set_grant_type('token', api_key=settings.DAILYMOTION_API_KEY, api_secret=settings.DAILYMOTION_API_SECRET, scope=['email', 'userinfo', 'manage_videos'], info={'redirect_uri': settings.DAILYMOTION_REDIRECT_URI})
# get credentiaols from database
access_token = user.dailymotionuser.access_token
expires = user.dailymotionuser.expires
refresh_token = user.dailymotionuser.refresh_token
session_params = {'access_token': access_token, 'expires': expires, 'refresh_token': refresh_token}
# set the credentials
d._session_store.set(session_params)
# check if the user revoked or not
try:
force_refreshed_access_token = d.get_access_token(force_refresh=True)
except dailymotion.DailymotionAuthError as e:
print(e.args[0])
return 'revoked'
# get valid access token
valid_access_token = d.get_access_token()
# update database with the valid access token
DailymotionUser.objects.filter(user=user).update(access_token=valid_access_token, expires=expires, refresh_token=refresh_token)
# prepare dic of the valid access token
valid_access_token_dic = {'access_token': valid_access_token}
# set the valid access token
d._session_store.set(valid_access_token_dic)
return d
but the update fails except title field with the following message,
'access_forbidden: You are not allowed to change existing video source.'
from doc,
access_forbidden: Thrown when the user doesn't have the permission to access the data (e.g. missing a required scope to access certain fields).
but I'm sure that the permission has manage_videos scope which is an enough scope to update the existing video source because the doc says,
manage_videos: Allows to modify or delete the user's uploaded videos and to publish new ones.
and described above,
Only the title field of the video is updated with the input_title properly.
Thanks for reading, I researched their docs carefully, but still don't understand this response.
Only partner users can update the video source urls.
Best,

Google YouTube API v3 :: Server-to-Server Upload Service

Our project lets users upload videos to OUR public youtube channel when they are signed into our session. We do not want to require additional OAuth2 verification, and instead follow the follow of Google API v2.
My code (php) and error are below, but my general question is: can my server make the insert-video POST using my API key/secrets without requiring the user to authenticate.
This question - Google Calendar API v3 hardcoded credentials - is very similar. Howevr if CURLing Google for the access_token is the only answer, i'll be disappointed. Thanks regardless.
Google API V3 Settings:
Client ID: xxxx.apps.googleusercontent.com
Email address: xxxx#developer.gserviceaccount.com
Client secret: xxxx
Redirect URIs: http://fbtabs.imagendigital.co/unilever/sedal/surprise/galeria
JavaScript origins: https://fbtabs.imagendigital.co
API key:
AIzaSyBgS-jMJQUPIiAyX20xC-encFWTPsR7qxQ
Referers:
Any referer allowed
Activated on: Jul 4, 2013 1:21 PM
Activated by: xxxx#gmail.com – you
Code:
<?php
require_once BASE_CD . 'app/src/Idframework/Tools.php';
require_once BASE_CD . 'app/vendor/google-api-php-client/src/Google_Client.php';
require_once BASE_CD . 'app/vendor/google-api-php-client/src/contrib/Google_YouTubeService.php';
$client = new Google_Client();
$client->setApplicationName('Google+ PHP Starter Application');
$client->setClientId('xxx.apps.googleusercontent.com');
$client->setClientSecret('xxxx');
$client->setRedirectUri('http://fbtabs.imagendigital.co/unilever/sedal/surprise/galeria');
$client->setDeveloperKey('xxxx');
$youtube = new Google_YoutubeService($client);
$snippet = new Google_VideoSnippet();
$status = new Google_VideoStatus();
$video = new Google_Video();
if (isset($_POST['tip_desc'])) $snippet->setDescription($_POST['tip_desc']);
$snippet->setTitle($_POST['tip_name']);
$snippet->setTags(array("sedal","hair","pello","cabello"));
$status->privacyStatus = "public";
$video->setSnippet($snippet);
$video->setStatus($status);
$filename = $_FILES['videoInp']['tmp_name'];
$mime = \Idframework\Tools::get_mime($filename);
try {
$part = "status";
$obj = $youtube->videos->insert($part, $video,
array("data"=>file_get_contents($filename),
"mimeType" => $mime));
} catch(Google_ServiceException $e) {
print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage(). " <br>";
print "Stack trace is ".$e->getTraceAsString();
}
Error:
Notice: Undefined index: content-type in C:\DATA\IDInteractive\SedalSurprise\app\vendor\google-api-php-client\src\service\Google_MediaFileUpload.php on line 99
Caught Google service Exception 401 message is Error calling POST https://www.googleapis.com/upload/youtube/v3/videos?part=player&uploadType=multipart&key=AIzaSyBgS-jMJQUPIiAyX20xC-encFWTPsR7qxQ: (401) Login Required
Stack trace is #0 C:\DATA\IDInteractive\SedalSurprise\app\vendor\google-api-php-client\src\io\Google_REST.php(36): Google_REST::decodeHttpResponse(Object(Google_HttpRequest)) #1 C:\DATA\IDInteractive\SedalSurprise\app\vendor\google-api-php-client\src\service\Google_ServiceResource.php(186): Google_REST::execute(Object(Google_HttpRequest)) #2 C:\DATA\IDInteractive\SedalSurprise\app\vendor\google-api-php-client\src\contrib\Google_YouTubeService.php(789): Google_ServiceResource->__call('insert', Array) #3 C:\DATA\IDInteractive\SedalSurprise\youtuber.php(56): Google_VideosServiceResource->insert('player', Object(Google_Video), Array) #4 {main}
First off, letting arbitrary users upload videos into a "master" YouTube channel is not recommended, for the reasons outlined in this blog post.
That being said, if you're determined to do it, then you need to include an access token associated with your channel in each upload request. The appropriate way to get a fresh access token (they expire after an hour) is to take a refresh token that you've previously generated and stored somewhere and make an HTTP request to the appropriate URL to get back an access token, as explained in the OAuth 2 documentation. There's no way around that—I'm not sure whether there's a native method in the Google APIs PHP client library that will automate the process for you, but that's what needs to happen under the hood.
The information in Google Calendar API v3 hardcoded credentials does seem relevant.

Twitter 3-legged authorization in Ruby

I am trying my hand ruby on rails. Mostly I have written code in Sinatra. Anyway this question may not have to do anything with framework. And this question may sound a very novice question. I am playing with Twitter 1.1 APIs and OAuth first time.
I have created an app XYZ and registered it with Twitter. I got XYZ's consumer key i.e., CONSUMER_KEY and consumer secret i.e. CONSUMER_SECRET. I also got XYZ's own access token i.e ACCESS_TOKEN and access secret i.e. ACCESS_SECRET
XYZ application type: Read, Write and Access direct messages
XYZ callback URL: http://www.mysite.com/cback
And I have checked: Allow this application to be used to Sign in with Twitter
What I am trying to do is very simple:
1) Users come to my website and click a link Link your twitter account (not signin with twitter)
2) That opens twitter popup where user grants permission to XYZ to perform actions on his/her behalf
3) Once user permits and popup gets closed, XYZ app gets user's access token and secret and save in the database.
4) Then XYZ uses that user's token and secret to perform actions in future.
I may be moron that such work flow has been implemented on several thousands sites and Twitter API documentations explain this 3-legged authentication, still I am unable to figure it out.
I have read https://dev.twitter.com/docs/auth/3-legged-authorization and https://dev.twitter.com/docs/auth/implementing-sign-twitter Unfortunately no ruby code found on internet that explains with step by step example.
What link should be used to open twitter authentication page when user clicks Link your twitter account.
Can anyone here, write some pseudo code with my pseduo credential above to achieve my goal from beging till end of this work flow? Thanks.
UPDATE:
I started with requesting request token as
require 'oauth'
consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET,
{ site: "https://twitter.com"})
request_token = consumer.get_request_token oauth_callback: 'http://www.mysite.com/tauth'
redirect_to request_token.authorize_url
I'm not familiar with ROR but here is the workflow of the OAuth 'dance' that you need to follow when the user clicks your button:
Obtain an unauthorized request token from Twitter by sending a
request to
POST https://api.twitter.com/oauth/request_token
signing the request using your consumer secret. This will be done in the background and
will be transparent to the user.
You will receive am oauth_token and oauth_token_secret back from
twitter.
Redirect the user to
https://api.twitter.com/oauth/authorize?oauth_token=[token_received_from_twitter]
using the oauth token value you received from Twitter in step 2.
When the user authorizes your app they will be redirected to your
callback url with oauth_token and oauth_verifier appended to the
url. i.e.
http://www.mysite.com/cback?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0&oauth_verifer=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY
Convert the request token into an access token by sending a signed
request along with the oauth_verifier to
POST
https://api.twitter.com/oauth/access_token
signing your request
with your consumer secret and the token secret received in step 2.
If everything goes ok, you will receive a new oauth_token and
oauth_token_secret from Twitter. This is your access token for the
user.
Using the access token and secret received in step 6 you can make
Twitter api calls on behalf the the user by sending signed requests
to the appropriate api endpoints.
Hope you solved your problem by this time, but I built this sample Sign in with Twitter ruby web app that provide all explanation you need to do this integration. Below there's a class that implements all necessary methods with comments:
require "net/https"
require "simple_oauth"
# This class implements the requests that should
# be done to Twitter to be able to authenticate
# users with Twitter credentials
class TwitterSignIn
class << self
def configure
#oauth = YAML.load_file(TWITTER)
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 1)
def request_token
# The request to get request tokens should only
# use consumer key and consumer secret, no token
# is necessary
response = TwitterSignIn.request(
:post,
"https://api.twitter.com/oauth/request_token",
{},
#oauth
)
obj = {}
vars = response.body.split("&").each do |v|
obj[v.split("=").first] = v.split("=").last
end
# oauth_token and oauth_token_secret should
# be stored in a database and will be used
# to retrieve user access tokens in next requests
db = Daybreak::DB.new DATABASE
db.lock { db[obj["oauth_token"]] = obj }
db.close
return obj["oauth_token"]
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 2)
def authenticate_url(query)
# The redirection need to be done with oauth_token
# obtained in request_token request
"https://api.twitter.com/oauth/authenticate?oauth_token=" + query
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 3)
def access_token(oauth_token, oauth_verifier)
# To request access token, you need to retrieve
# oauth_token and oauth_token_secret stored in
# database
db = Daybreak::DB.new DATABASE
if dbtoken = db[oauth_token]
# now the oauth signature variables should be
# your app consumer keys and secrets and also
# token key and token secret obtained in request_token
oauth = #oauth.dup
oauth[:token] = oauth_token
oauth[:token_secret] = dbtoken["oauth_token_secret"]
# oauth_verifier got in callback must
# to be passed as body param
response = TwitterSignIn.request(
:post,
"https://api.twitter.com/oauth/access_token",
{:oauth_verifier => oauth_verifier},
oauth
)
obj = {}
vars = response.body.split("&").each do |v|
obj[v.split("=").first] = v.split("=").last
end
# now the we got the access tokens, store it safely
# in database, you're going to use it later to
# access Twitter API in behalf of logged user
dbtoken["access_token"] = obj["oauth_token"]
dbtoken["access_token_secret"] = obj["oauth_token_secret"]
db.lock { db[oauth_token] = dbtoken }
else
oauth_token = nil
end
db.close
return oauth_token
end
# This is a sample Twitter API request to
# make usage of user Access Token
# See https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
def verify_credentials(oauth_token)
db = Daybreak::DB.new DATABASE
if dbtoken = db[oauth_token]
# see that now we use the app consumer variables
# plus user access token variables to sign the request
oauth = #oauth.dup
oauth[:token] = dbtoken["access_token"]
oauth[:token_secret] = dbtoken["access_token_secret"]
response = TwitterSignIn.request(
:get,
"https://api.twitter.com/1.1/account/verify_credentials.json",
{},
oauth
)
user = JSON.parse(response.body)
# Just saving user info to database
user.merge! dbtoken
db.lock { db[user["screen_name"]] = user }
result = user
else
result = nil
end
db.close
return result
end
# Generic request method used by methods above
def request(method, uri, params, oauth)
uri = URI.parse(uri.to_s)
# always use SSL, you are dealing with other users data
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# uncomment line below for debug purposes
#http.set_debug_output($stdout)
req = (method == :post ? Net::HTTP::Post : Net::HTTP::Get).new(uri.request_uri)
req.body = params.to_a.map { |x| "#{x[0]}=#{x[1]}" }.join("&")
req["Host"] = "api.twitter.com"
# Oauth magic is done by simple_oauth gem.
# This gem is enable you to use any HTTP lib
# you want to connect in OAuth enabled APIs.
# It only creates the Authorization header value for you
# and you can assign it wherever you want
# See https://github.com/laserlemon/simple_oauth
req["Authorization"] = SimpleOAuth::Header.new(method, uri.to_s, params, oauth)
http.request(req)
end
end
end
More detailed explanation at:
https://github.com/lfcipriani/sign_in_with_twitter_sample

Resources