I'm developing an application with Cordova and would like to save files in Googe Drive.
I've got success in login to Google, using the cordova-plugin-googleplus (https://github.com/EddyVerbruggen/cordova-plugin-googleplus). However I could not get the plugin returns to me accessToken or idToken so I could use with Google javascript API.
window.plugins.googleplus.login(
{
'scopes': 'https://www.googleapis.com/auth/drive.file profile',
'offline': true,
'webApiKey': ‘CODE’
},
function (obj) {
$scope.$apply(function() {
$scope.srcImage = obj.imageUrl;
$scope.NomeGoogle = obj.displayName;
});
},
function (msg) {
alert('Erro');
alert('error: ' + msg);
}
);
I tried using the code below, but returned me the following error:
"Uncaught gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy"
gapi.load('auth2', function() {
gapi.auth2.init({
client_id: 'REVERSED_CLIENTID',
}).then(function(){
auth2 = gapi.auth2.getAuthInstance();
console.log(auth2.isSignedIn.get()); //now this always returns correctly
});
});
I managed to figure out the problem, why wasn´t getting the serverAuthCode from plugin.
It is necessary to create 2 credentials on the Google Developers Console. The 1st must be Android, this will be for the plugin and the 2nd should be a Web App, this is necessary to achieve serverAuthCode.
The code looks like this
window.plugins.googleplus.login(
{
'scopes': 'https://www.googleapis.com/auth/drive.file profile',
'offline': true,
'webApiKey': ‘REVERSED_CODE of Web App Credential’
},
function (obj) {
$scope.$apply(function() {
$scope.srcImage = obj.imageUrl;
$scope.NomeGoogle = obj.displayName;
});
var data = $.param({
client_id: 'REVERSED_CODE of Web App Credential',
client_secret: 'SECRET_CODE of Web App Credential',
grant_type: 'authorization_code',
code: obj.serverAuthCode
});
var config = {
headers : {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
$http.post("https://www.googleapis.com/oauth2/v3/token", data, config).success(function(data, status) {
//data.access_token;
/** from now you can do use of google API **/
})
.error(function(data, status) {
console.log(data);
console.log(status);
});
},
function (msg) {
alert('Erro');
alert('error: ' + msg);
}
);
Thank you for your reply rojobo
At first I was hoping to skip the need for cordova-plugin-googleplus and just use the gapi within Cordova/PhoneGap to handle authentication with Google, but it appears the gapi client authentication may not work within cordova's file:// protocol.
The answer from #Joao sent me in the right direction, but I kept getting the Invalid cookiePolicy error when trying to use the gapi after retrieving the access_token (this was because I was ignoring step #2 listed below, and after authenticating with the plugini I was mistakenly trying to authenticate again with the gapi).
There is a step (#3 mentioned below) that was unclear to me. To authenticate with Google and then use the gapi in Cordova/PhoneGap, this worked instead...
use the cordova-plugin-googleplus to take care of the authentication and access token retrieval, do not use the gapi at all here
load the gapi client library (skip over the gapi.client.init() call and all the normal gapi authentication procedures)
Attach the access token we got from the plugin to the gapi client, and then make your gapi calls as needed
Step #3 took some digging for me to find, and meant I needed to add the access_token
gapi.client.setToken({access_token:'abc123456xyz'});
Once the access token was attached to the gapi client, I could use the gapi within Cordova/Phonegap:
// Load the YouTube API.
gapi.client.load('youtube', 'v3', function() {
// Do stuff...
};
try
window.plugins.googleplus.login(
{
'scopes': 'https://www.googleapis.com/auth/drive.file profile',
'offline': true,
'cookiepolicy': 'none',
'webApiKey': ‘CODE’
}
Related
I would like to create a YouTube playlist on a users account, but I have struggled to authenticate a POST to the YouTube v3 api.
I'll start by showing how far I have got with this problem.
YouTube API Documentation
The Youtube API Documentation provides details on creating a playlist, and has a working example in the API Explorer
I entered the following code into the request body:
{
"snippet":
{
"title":"Test Playlist"
}
}
This successfully created a playlist on my YouTube account with the same title. So from this I could tell that, a title was required within the body and it would require OAuth 2.0 authentication (an error is displayed if it is not enabled) using one the scopes: youtube, youtube.force-ssl, youtubepartner.
First attempt in react
The First thing I tried was similar to this:
fetch('/youtube/v3/playlists', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer' + api.youtube,
},
body: JSON.stringify({
"snippet":
{
"title":"Test"
}
})
}).then(response => response.json()).then(data => {
console.log(data)
})
api.youtube contains my YouTube api key.
Most of the formatting for this came from another API I have in the same program for getting data from spotify which works.
The response I got from this would say "Login failed" or "Authentication Error" (something along those lines)
Anyway, this is relevant because I know that my first hurdle is getting authentication.
Authentication
The YouTube API Documentation contains a guide titled Implementing OAuth 2.0 Authorization I followed the guide for client side web apps.
The first thing I noticed is that they are using a library, I found this on npm under googleapis and installed it.
When I tried to call this in React using
const {google} = require('googleapis');
I won't get deep into the error but react said "Can't convert undefined to object" and found an issue which said that googleapis is intended for server side not client side, I tried building the react app and putting it on herokuapp but got the same error. Someone else suggested using gapi-client on npm which is a node wrapper for googleapis.
The next thing I did was try the example on the npm page, which is very similar to the google example for configuring the client object. I have it so the import part and function are at the top of my app.js and then the gapi.load part activates after a button is pressed (this could be useless info but w/e)
import gapi from 'gapi-client';
//On load, called to load the auth2 library and API client library.
gapi.load('client:auth2', initClient);
function initClient() {
gapi.client.init({
discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"],
clientId: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/drive.metadata.readonly'
}).then(function () {
// do stuff with loaded APIs
console.log('it worked');
});
}
I copied my client ID in from the API Console and this is the exact response I got:
FireFox
Loading failed for the with source
“https://apis.google.com//scs/apps-static//js/k=oz.gapi.en.WcpMzqgmJZU.O/m=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCNsTS1p4dx0iMhlrwEpiaXw4iMjOg/cb=gapi.loaded_0”.
Chrome
GET
https://apis.google.com//scs/apps-static//js/k=oz.gapi.en.WcpMzqgmJZU.O/m=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCNsTS1p4dx0iMhlrwEpiaXw4iMjOg/cb=gapi.loaded_0
net::ERR_ABORTED 404
That's about as far as I got and I'm not sure what to do from here, so any help is much appreciated. I hope this didn't get too convoluted but I've tried to convey my problem as clearly as possible.
So I was able to authorize the YouTube API and create a playlist.
I have a backend hosted on localhost:8888 (doesn't matter just not what react is hosted on).
here is sample code for what I put in the server.js file (for the backend)
var express = require('express');
var app = express();
var passport = require('passport');
app.use(passport.initialize());
var YoutubeV3Strategy = require('passport-youtube-v3').Strategy;
passport.use(new YoutubeV3Strategy({
clientID: YOUR_CLIENT_ID,
clientSecret: YOUR_CLIENT_SECRET,
callbackURL: 'http://localhost:8888/redirect',
scope: ['https://www.googleapis.com/auth/youtube']
},
function (accessToken, refreshToken, profile, cb) {
var user = {
accessToken: accessToken,
refreshToken: refreshToken
};
return cb(null, user)
}
));
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});
app.get('/authenticate', passport.authenticate('youtube'))
app.get('/redirect', passport.authenticate('youtube', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('http://localhost:3000' + '?access_token=' + req.user.accessToken)
})
app.listen(8888)
This is using Passport.js to do oauth for me, lots of documentation can be found on the site.
In react I have it so a button will open localhost:8888/authenticate and then that will redirect back to my application. If you are using this you need to make sure that on your google API credentials you have the javascript origin as http://localhost:8888 and the redirect URI as http://localhost:8888/redirect and the correct scope and application type.
This is the function I use in my app.js (react) to make the POST
getAPIdata() {
let parsed = queryString.parse(window.location.search);
let accessToken = parsed.access_token
fetch('https://www.googleapis.com/youtube/v3/playlists?part=snippet', {
method: 'POST',
headers: {
'Content-type': 'application/json',
'Authorization': 'Bearer ' + accessToken,
},
body: JSON.stringify({
'snippet':
{
'title':this.state.inputTitle
}
})
}).then(response => response.json()).then(data => {
console.log(data)
window.alert('https://www.youtube.com/playlist?list=' + data.id)
})
}
I was actually mostly correct with the first attempt I just had the authorization incorrect.
Here's a couple sources that helped me make my solution:
Passport.js oauth tutorial
Googles OAuth 2.0 Playground
Passport.js Documentation
Passport.js facebook oauth example
Hopefully this is helpful to someone, You can use the same code i used in server.js to authenticate most services by just changing the strategy.
A live version of my application can be found here. In the console it shows the response from the POST request, this should help if you have any issues. I know the alert is bad ui but this wasn't the intended use.
Thanks for reading :)
We plan to use keycloak to secure a bunch of web apps, some written in Java, some in JavaScript (with React).
After the user is logged in by keycloak, each of those web apps needs to retrieve the user that is logged in and the realm/client roles that the user has.
For Java apps, we tried the keycloak Java API (request -> KeycloakSecurityContext -> getIdToken -> getPreferredUsername/getOtherClaims). They seem to work fine
For JavaScript apps, we tried the following code, but could not get Keycloak to init successfully (Note this is in web app code after the user is already authenticated by keycloak, the app is only trying to retrieve who logged in with what roles):
var kc = Keycloak({
url: 'https://135.112.123.194:8666/auth',
realm: 'oneRealm',
clientId: 'main'
});
//this does not work as it can't find the keycloak.json file under WEB-INF
//var kc = Keycloak('./keycloak.json');
kc.init().success(function () {
console.log("kc.idToken.preferred_username: " + kc.idToken.preferred_username);
alert(JSON.stringify(kc.tokenParsed));
var authenticatedUser = kc.idTokenParsed.name;
console.log(authenticatedUser);
}).error(function () {
window.location.reload();
});
I assume it would be fairly common that web apps need to retrieve current user info. Anyone knows why the above code didn't work?
Thanks.
<script src="http://localhost:8080/auth/js/keycloak.js" type="text/javascript"></script>
<script type="text/javascript">
const keycloak = Keycloak({
"realm": "yourRealm",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "yourRealm/keep it default",
"public-client": true,
"confidential-port": 0,
"url": 'http://localhost:8080/auth',
"clientId": 'yourClientId',
"enable-cors": true
});
const loadData = () => {
console.log(keycloak.subject);
if (keycloak.idToken) {
document.location.href = "?user="+keycloak.idTokenParsed.preferred_username;
console.log('IDToken');
console.log(keycloak.idTokenParsed.preferred_username);
console.log(keycloak.idTokenParsed.email);
console.log(keycloak.idTokenParsed.name);
console.log(keycloak.idTokenParsed.given_name);
console.log(keycloak.idTokenParsed.family_name);
} else {
keycloak.loadUserProfile(function() {
console.log('Account Service');
console.log(keycloak.profile.username);
console.log(keycloak.profile.email);
console.log(keycloak.profile.firstName + ' ' + keycloak.profile.lastName);
console.log(keycloak.profile.firstName);
console.log(keycloak.profile.lastName);
}, function() {
console.log('Failed to retrieve user details. Please enable claims or account role');
});
}
};
const loadFailure = () => {
console.log('Failed to load data. Check console log');
};
const reloadData = () => {
keycloak.updateToken(10)
.success(loadData)
.error(() => {
console.log('Failed to load data. User is logged out.');
});
}
keycloak.init({ onLoad: 'login-required' }).success(reloadData);
</script>
simple javascript client authentication no frameworks.
for people who are still looking...
Your code asks the Keycloak client library to initialize, but it doesn't perform a login of the user or a check if the user is already logged in.
Please see the manual for details: http://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter
What your probably want to do:
Add check-sso to the init to check if the user is logged in and to retrieve the credentials keycloak.init({ onLoad: 'check-sso' ... }). You might even use login-required.
Make sure that you register a separate client for the front-end. While the Java backend client is of type confidential (or bearer only), the JavaScript client is of type public.
You find a very minimal example here: https://github.com/ahus1/keycloak-dropwizard-integration/blob/master/keycloak-dropwizard-bearer/src/main/resources/assets/ajax/app.js
Alternatively you can register a callback for onAuthSuccess to be notified once the user information has been retrieved.
Once you use Keycloak in the front-end, you will soon want to look in bearer tokens when calling REST resources in the backend.
You might have solved the problem by this time. I hope this answer help rest of the people in trouble.
when you use JavaScript Adopter
Below javascript should be added in of html page.
<script src="http://localhost:8080/auth/js/keycloak.js"></script>
<script>
/* If the keycloak.json file is in a different location you can specify it:
Try adding file to application first, if you fail try the another method mentioned below. Both works perfectly.
var keycloak = Keycloak('http://localhost:8080/myapp/keycloak.json'); */
/* Else you can declare constructor manually */
var keycloak = Keycloak({
url: 'http://localhost:8080/auth',
realm: 'Internal_Projects',
clientId: 'payments'
});
keycloak.init({ onLoad: 'login-required' }).then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
function logout() {
//
keycloak.logout('http://auth-server/auth/realms/Internal_Projects/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri')
//alert("Logged Out");
}
</script>
https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter Reference Link.
Note : Read the comments for 2 methods of adding json credentials.
I have been trying this simple google sign-in by following this tutorials:
https://developers.google.com/identity/sign-in/web/
http://www.codexworld.com/login-with-google-account-using-javascript/
The code is very simple and I'm exactly following the same steps as given in these tutorials, but I end up in the below error.
400. That’s an error.
Error: invalid_request
Storagerelay URI is not allowed for 'NATIVE' client type
In my credentials I have the configuration like below:
My Code goes like below:
<meta name="google-signin-client_id" content="377345478968-2opk94iompa38gja0stu1vi821lr3rt0.apps.googleusercontent.com">
<script src="https://apis.google.com/js/client:platform.js?onload=renderButton" async defer></script>
<div id="gSignIn"></div>
function onSuccess(googleUser) {
var profile = googleUser.getBasicProfile();
gapi.client.load('plus', 'v2', function () {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
//Display the user details
request.execute(function (resp) {
console.log(resp);
});
});
}
function onFailure(error) {
alert(error);
}
function renderButton() {
gapi.signin2.render('gSignIn', {
'scope': 'profile email',
'width': 240,
'height': 50,
'longtitle': true,
'theme': 'dark',
'onsuccess': onSuccess,
'onfailure': onFailure
});
}
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
$('.userContent').html('');
$('#gSignIn').slideDown('slow');
});
}
Every time when I click "Sign in with Google" button, new pop-up opens and 400 error throws up.
I also tried growing throw these answers in stackoverflow, but now luck.
Google OAuth 2 authorization - Error: redirect_uri_mismatch
Google Sign-in is not working
My Basic idea is to integrate sign in with google for my web-app, as said in this video, let me know whether this approach is good.
It's because you haven't set up your OAuth 2.0 credentials for a Web Application. You may have specified the application type as "Other" when creating the credentials. I was facing the exactly same problem but then I changed my OAuth 2.0 credentials to a Web Application, and now it's working perfectly.
I know this has been answered but in my case, while developing a desktop app with Electron and React, I had set the OAuth2 credential type as "Desktop". Changing it to "Web application" solved it for me.
I've followed the guide on how to use GCS on their site:
but
Once Unauthorized used
I then get
Failed to load resource: the server responded with a status of 400 ()
{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad
Request"}],"code":400,"message":"Bad Request"}}
Here is my step
create the javascript page from this
authSample.html
change clientId to google console page
OAuth 2.0 user ID:??????????????????????.apps.googleusercontent.com
change apiKey to google console page
OAuth 2.0 user api key
run the authSample.html
What am I doing wrong?
So this bug occurs because of an incorrect way of loading and authorising API's.
The correct way is to first use
gapi to load client (no auth required)
next load, the "storage","v1"
finally Authorise
gapi.load('client', () => {
gapi.client.load('storage', 'v1', authResult =>{
gapi.auth.authorize({
client_id: CLIENT_ID,
scope: SCOPES,
immediate: false
},authResult=>{
if (authResult && !authResult.error) {
var request = gapi.client.storage.buckets.list({
'project': PROJECT_ID
});
request.execute(function(resp) {
console.log(resp)
});
} else {
alert("Un-Authorised")
}
});
});
});
The supported client libraries are limited to a number of languages only. If you wish to build a solution around those, you can refer these here
https://cloud.google.com/storage/docs/json_api/v1/libraries#api-client-libraries
Until recently I was using the javascript Blogger API v3 to fetch posts from a blog to build an automated index, using the gapi.client.blogger.posts.list() method.
However, it suddenly stopped working. The response now simply includes an error object with the message "We're sorry, but the requested resource could not be found.", as if the blog I'm trying to fetch info does not exist at all. I'm pretty sure my blog ID did not change during my sleep.
So I started digging around, and I found that the OAuth authentication token returned says I'm not actually logged in Google. In the status object from the token, the signed_in property is true but the google_logged_in one is false. Yet the Google login page showed up correctly upon execution and I allowed it, and the token does not have an error property. Why does it says I'm not logged in?
I'm under the assumption that Google blocked my application from making new requests due repeated use (even thought I never reached a dent of the daily quota) and now since the OAuth 2 ID authentication does not work, the API tries to use only the web browser API key, which does not work either since it's a private blog and user permission is required. But I really don't know.
I could not find a similar situation on the internet. I tried deleting both my API and OAuth keys and making new ones, deleting the project from the Google Console Developers and creating a new one under a different account, removing the application from the "allowed apps" in my account and adding it again, not using the browser API key. No game.
I would appreciate any inputs in solving this issue. And before anyone suggests: the "conventional" way of creating an automated index using the blog's feed does not work in my case, since the blog is private and has no feed.
Bellow is the returned access token (redacted a bit):
{
_aa: "1"
access_token: "[Redacted]"
client_id: "[Redacted]"
cookie_policy: undefined
expires_at: "1460999668"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1460996068"
response_type: "token"
scope: "https://www.googleapis.com/auth/blogger.readonly"
state: ""
status: {
google_logged_in: false
method: "AUTO"
signed_in: true
}
token_type: "Bearer"
}
And bellow an example code of my application:
var apiKey = "myWebBrowserApiKey";
var clientId = "myOAuth2ClientID";
var blogId = "myBlogID";
var bloggerReadOnly = "https://www.googleapis.com/auth/blogger.readonly";
var fetchParam = {
blogId: blogId,
fetchBodies: false,
fetchImages: false,
maxResults: 500,
status: "live",
view: "READER"
};
var authParamIm = {
client_id: clientId,
scope: bloggerReadOnly,
immediate: true
};
var authParam = {
client_id: clientId,
scope: bloggerReadOnly,
immediate: false
};
//Executed as soon the client:blogger.js loads
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
//Check if the user is already authenticated for immediate access
function checkAuth() {
gapi.auth.authorize( authParamIm, handleAuthResult );
}
function handleAuthResult(authResult) {
//If the user does already have authorization, proceed with the API call
if (authResult && !authResult.error) {
makeApiCall();
}
//If he does not, shows the authorization button
else {
authorizeButton.css('visibility', 'visible');
authorizeButton.click(handleAuthClick);
}
}
//The authorization button calls this function, that shows a Google login page
function handleAuthClick(event) {
gapi.auth.authorize( authParam, handleAuthResult );
return false;
}
//Loads the Blogger API, version 3, and runs the fetchPosts() function with a callback
function makeApiCall(){
gapi.client.load('blogger', 'v3', fetchPosts);
}
function fetchPosts(){
//Creates a request to get a list of posts from the blog, using the fetch parameters
var request = gapi.client.blogger.posts.list(fetchParam);
//Execute the request and treats the response with a callback function
request.execute(function(response){
//Do Stuff
}
}