Programmatically upload videos to YouTube using FWT authentication - google-api

Seems like uploading videos to Youtube via its API is restricted to user explicit authorization, and all points that this is only possible via OAuth2 instead of FWT.
Using the V3 API I'm getting a 401 response code, however I can perform read-only operations, like fetch playlists etc...
For server-to-server tasks automation OAuth is not the best solution.
Here's is the test code using node.js:
var fs = require('fs')
var google = require('googleapis')
var youtube = google.youtube('v3')
var authClient = new google.auth.JWT(
'service-account-email#developer.gserviceaccount.com',
'path/to/key.pem',
null,
['https://www.googleapis.com/auth/youtube', 'https://www.googleapis.com/auth/youtube.upload'])
authClient.authorize(function (err, tokens) {
if (err) {
return console.log(err)
}
google.options({ auth: authClient })
youtube.videos.insert({
media: {
body: fs.createReadStream('video.mp4')
},
autoLevels: true,
part: 'status,snippet',
mediaType: 'video/mp4',
resource: {
snippet: {
title: 'test video',
description: 'This is a test video uploaded from the YouTube API'
},
status: {
privacyStatus: 'public'
}
}
}, function (err, res) {
if (err) {
return console.error(err)
}
console.log('Upload done!')
})
})
Google API node.js client original example:
https://github.com/google/google-api-nodejs-client/blob/master/examples/jwt.js

Related

Login with Google in React Access Token problem

I have employed the Login with Google functionality in my React app. I am getting the jwt but there is no access token included in the jwt which I need for sending it to the backend (Laravel). On the backend I use Socialite and I want to get the user back with the access token. Right now I am verifying the user with jwt which is not working.
React Code.
const handleGoogleCallbackResponse = (response) => {
signinWithGoogle(response.credential)
}
const signinWithGoogle = async (jwt) => {
try {
const res = await axios.post("/api/users/loginwithgoogle", {jwt: jwt})
console.log("Google data from backend: ", res.data);
} catch (error) {
console.log("Error at signinWithGoogle : ", error);
}
}
useEffect(() => {
/* global google */
google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: handleGoogleCallbackResponse
})
google.accounts.id.renderButton(document.getElementById("google-btn"), {theme: "outline", size: "large"})
}, [])
Backend:
$user = Socialite::driver('google')->stateless()->userFromToken($request->jwt);

Fitbit URL callback giving a response of NULL

I'm having trouble getting a response from a callback uri and I would really appreciate any help you could give me.
I am trying to use the Fitbit API which requires you to use a callback url to get an Auth Code.
Workflow:
1. Go to Fitbit url to get user to allow the app access to their personal data.
2. User agrees to the conditions
3. User gets redirected to my API
4. The API returns the code from (Code is located in URL and I can access it)
5. I console.log the code out to verify it
6. API returns the code
7. I work with code then exchanging it for an access token.
The problem is that I don't return the code (Or anything )when I return to the app even though I can console.log it on the API. The response I get is NULL
Here is the URL:
url = "https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://REDIRECT_URL&scope=activity%20heartrate%20location%20nutrition%20profile%20settings%20sleep%20social%20weight&expires_in=604800";
I then open the URL in the InAPPBrowser successfully:
if (url !== "") {
const canOpen = await Linking.canOpenURL(url)
if (canOpen) {
try {
const isAvailable = await InAppBrowser.isAvailable()
if (isAvailable) {
const result =InAppBrowser.open(url, {
// iOS Properties
dismissButtonStyle: 'done',
preferredBarTintColor: 'gray',
preferredControlTintColor: 'white',
// Android Properties
showTitle: true,
toolbarColor: '#6200EE',
secondaryToolbarColor: 'black',
enableDefaultShare: true,
}).then((result) => {
console.log("Response:",JSON.stringify(result))
Linking.getInitialURL().then(url => {
console.log("Tests: ",url)
this._setTracker(url as string);
});
})
} else Linking.openURL(url)
} catch (error) {
console.log("Error: ",error)
}
}
}
From here the URL opens successfully.
Here is the API now which is done in Typescript on AWS serverless and Lambda
export const handler: APIGatewayProxyHandler = async (event, _context, callback) =>{
let provider = event.path
//prints code
let x = event.queryStringParameters
console.log("Code: ",x)
const response = {
statusCode: 200,
body: "Success"
};
return response;
}
Please let me know if further detail is required?
Thank you!
Right so it turns out what I was doing was correct apart from the response should have been 301 which is a redirect response.
const response= {
statusCode: 301,
headers: {
"location": `app://CALLBACK RESPONSE ADDRESS?type=${provider}`
},
body: "Boom"
}

Social authentication in GraphQL

I am creating a backend which relies in Express and GraphQL which will serve clients apps (android and react).
I have been following this article on how to nail social authentication in GraphQL using passport.js.
The article uses passport-google-token strategy and is based on Apollo-server but personally I prefer to use express-graphql.
After setting my android app to use google auth and try to send mutation to server I get this error
Google info: { InternalOAuthError: failed to fetch user profile
at E:\_Projects\myProject\myProject-backend\node_modules\passport-google-token\lib\passport-google-token\strategy.js:114:28
at passBackControl (E:\_Projects\myProject\myProject-backend\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (E:\_Projects\myProject\myProject-backend\node_modules\oauth\lib\oauth2.js:157:7)
at IncomingMessage.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1125:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 401,
data:
'{\n "error": {\n "code": 401,\n "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",\n "status": "UNAUTHENTICATED"\n }\n}\n' } }
I believe token I pass maybe does not reach where it supposed but I cant figure out how to solve.
I have tested my token here https://oauth2.googleapis.com/tokeninfo?id_token=MyToken and they are working correctly.
Here is graphQL config in app.js
app.use('/graphql', graphqlHTTP((req, res) => ({
schema,
graphiql: true,
context: {req, res}
})));
Here is google auth mutation
googleAuth: {
type: authPayLoad,
args: {token: {type: new GraphQLNonNull(GraphQLString)}},
async resolve(_, {token}, {req, res}) {
req.body = {
...req.body,
access_token: token,
};
try {
const {data, info} = await authenticateGoogle(req, res);
console.log("Google data: ", data);
if (data) {
const user = await User.upsertGoogleUser(data);
if (user) {
return ({
name: user.name,
username: user.username,
token: user.generateJWT(),
});
}
}
if (info) {
console.log("Google info: ", info);
switch (info.code) {
case 'ETIMEDOUT':
return (new Error('Failed to reach Google: Try Again'));
default:
return (new Error('something went wrong'));
}
}
return (Error('server error'));
} catch (e) {
console.log("Error: ", e);
return e
}
},
},
And here is my auth controller
const passport = require('passport');
const {Strategy: GoogleTokenStrategy} = require('passport-google-token');
// GOOGLE STRATEGY
const GoogleTokenStrategyCallback = (accessToken, refreshToken, profile, done) => done(null, {
accessToken,
refreshToken,
profile,
});
passport.use(new GoogleTokenStrategy({
clientID: 'MY_CLIET_ID',
clientSecret: 'SERVER-SECRET'
}, GoogleTokenStrategyCallback));
module.exports.authenticateGoogle = (req, res) => new Promise((resolve, reject) => {
passport.authenticate('google-token', {session: false}, (err, data, info) => {
if (err) reject(err);
resolve({data, info});
})(req, res);
});
I expected when client app submit mutation with token as arg the request will be sent to google and returns user data. How do I solve this.
passport-google-token is archived and seems deprecated to me. Why don't you try passport-token-google.
It can be used in similar way.
passport.use(new GoogleStrategy({
clientID: keys.google.oauthClientID,
clientSecret: keys.google.oauthClientSecret
},
function (accessToken, refreshToken, profile, done) {
return done(null, {
accessToken,
refreshToken,
profile,
});
}
));
module.exports.authenticate = (req, res) => new Promise((resolve, reject) => {
passport.authenticate('google-token', {session: false}, (err, data, info) => {
if (err) reject(err);
resolve({data, info});
})(req, res);
});
Hope this helps.

How to get space Id of users who have not added Google Chat Bot to their coversation

We are implementing a Google hangout Chat Bot , Which will send proactive notification to the user in domain. To do this Google chat Bot API requires the space Id to send proactive notification to user.
Reference document: https://developers.google.com/hangouts/chat/reference/rest/v1/spaces/list
code :
jwtClient.authorize(function (err) {
if (err) {
console.log(err);
return;
}
else {
chat.spaces.list({
auth: jwtClient
}, function (err, resp) {
if (err)
console.log(err);
else {
chat.spaces.list({
auth: jwtClient
}, function (err, resp) {
if (err)
console.log(err);
else {
var spaceList = resp.data.spaces;
spaceList.forEach(element => {
var spaceUrl = `https://chat.googleapis.com/v1/${element.name}/messages?key=${apiKey}`;
request({
url: spaceUrl,
method: "POST",
headers: {
'Content-Type': 'application/json'
},
json: customMessage
},
function (error, response, body) {
callback(error, body)
}
);
})
};
});
}
});
}
});
}
}
But This API returns space list of only those user , who has added the Bot to their coversation.
Is their any work around to get/create space of/to every user in google domain?
Unfortunately there is no way to extract the Space ID without the user ever interacting with the bot. Allowing this would give the bot ability to spam any user whenever without consent.
I would suggest storing space IDs to a database. So once a user has started a conversation with a bot, you can later message them whenever you want. Adding the bot or interacting with it in a room is the "consent" that is needed for a bot to message a user.

Authenticating my Ionic 3 app against Spring Boot REST API

The question must be very typical, but I can't really find a good comparison.
I'm new to Ionic & mobile dev.
We have a REST API (Spring Boot).
API is currently used by AngularJS 1.5 front-end only.
AngularJS app is authenticated based on the standard session-based authentication.
What should I use to authenticate an ionic 3 app?
As I understand, have 2 options:
Use the same auth as for Angular front-end.
implement oauth2 on the back-end and use the token for the ionic app.
As for now, I understand that implementing oauth2 at back-end is a way to go because with the option #1 I should store the username & password in the local storage (ionic app), which is not safe. Otherwise, if I don't do that - the user will have to authenticate each time the app was launched. Am I right?
So, that leaves me with option #2 - store oauth2 token on the device?
Good to go with #2. Here is how i manage token.
I use ionic storage to store token and a provider config.ts which hold the token during run time.
config.ts
import { Injectable } from '#angular/core';
#Injectable()
export class TokenProvider {
public token: any;
public user: any = {};
constructor( ) { }
setAuthData (data) {
this.token = data.token;
this.user = data
}
dropAuthData () {
this.token = null;
this.user = null;
}
}
auth.ts
import { TokenProvider} from '../../providers/config';
constructor(public tokenProvider: TokenProvider) { }
login() {
this.api.authUser(this.login).subscribe(data => {
this.shared.Loader.hide();
this.shared.LS.set('user', data);
this.tokenProvider.setAuthData(data);
this.navCtrl.setRoot(TabsPage);
}, err => {
console.log(err);
this.submitted = false;
this.shared.Loader.hide();
this.shared.Toast.show('Invalid Username or Password');
this.login.password = null;
});
}
and i do a check when app launch.
app.component.ts (in constructor)
shared.LS.get('user').then((data: any) => {
if (!data) {
this.rootPage = AuthPage;
} else {
tokenProvider.setAuthData(data);
this.rootPage = TabsPage;
}
});
api.provider.ts
updateUser(data): Observable < any > {
let headers = new Headers({
'Content-Type': 'application/json',
'X-AUTH-TOKEN': (this.tokenProvider.token)
});
return this.http.post(`${baseUrl}/updateUser`, JSON.stringify(data), {
headers: headers
})
.map((response: Response) => {
return response.json();
})
.catch(this.handleError);
}
And last logout.ts
logOut(): void {
this.shared.Alert.confirm('Do you want to logout?').then((data) => {
this.shared.LS.remove('user').then(() => {
this.tokenProvider.dropAuthData();
this.app.getRootNav().setRoot(AuthPage);
}, () => {
this.shared.Toast.show('Oops! something went wrong.');
});
}, err => {
console.log(err);
})
}
The final solution i've made:
ionic app:
implemented a jwt token storage similar to Swapnil Patwa answer.
Spring back-end:
Tried to use their original ouath2 package, but found out that as always with spring/java, configs are too time-consuming => made a simple filter which is checking for the manually generated & assigned jwt token.

Resources