I've written some client-side app and tried to test it. How it turned out only I can use it. Anyone else will get such error.
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}
],
"code": 403,
"message": "Forbidden"
}
}
What does it mean? How to solve this?
There is my code. There i'm getting Email, name, surname and user photo. I want to get the number of youtube channel subscribers and work with youtube later. For example I want to rate some videos directly from the site.
function resultFindUserByEmail()
{
if (ajaxRet['isUserFinded'])
{
cf_JSON.clear();
cf_JSON.addItem( 'email',email );
var jsonstr = cf_JSON.make();
ajax_post('doyoutubelogin','loginres','index.php',jsonstr,c_dologin);
}else{
gapi.client.init({
discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/people/v1/rest"],
clientId: OAUTH2_CLIENT_ID,
scope: OAUTH2_SCOPES
}).then(function () {
var request = gapi.client.people.people.get({
'resourceName': 'people/me'
}).then(function(response) {
var parsedResponse = JSON.parse(response.body).names;
surname = parsedResponse[0].familyName;
name = parsedResponse[0].givenName;
photo = JSON.parse(response.body).photos[0].url;
addYoutubeUser();
});
});
}
}
function addYoutubeUser() {
cf_JSON.clear();
cf_JSON.addItem( 'Email',email );
cf_JSON.addItem( 'Firstname',name );
cf_JSON.addItem( 'Lastname',surname );
cf_JSON.addItem( 'Image',photo );
var jsonstr = cf_JSON.make();
ajax_post('addyoutubeuser','loginres','index.php',jsonstr,c_dologin);
}
var API_KEY = '<Key removed for posting>';
var API_KEY1='<Key removed for posting>';
var OAUTH2_CLIENT_ID = '<Key removed for posting>';
var OAUTH2_CLIENT_ID1 = '<Key removed for posting>';
var OAUTH2_SCOPES = 'https://www.googleapis.com/auth/youtube.force-ssl';
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];
var GoogleAuth;
function handleClientLoad() {
// Load the API's client and auth2 modules.
// Call the initClient function after the modules load.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Retrieve the discovery document for version 3 of YouTube Data API.
// In practice, your app can retrieve one or more discovery documents.
var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest';
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': API_KEY,
'discoveryDocs': [discoveryUrl,"https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"],
'clientId': OAUTH2_CLIENT_ID,
'scope': OAUTH2_SCOPES
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
//GoogleAuth.grant(OAUTH2_SCOPES);
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
setSigninStatus();
// Call handleAuthClick function when user clicks on
// "Sign In/Authorize" button.
$('#sign-in-or-out-button').click(function() {
handleAuthClick();
});
$('#revoke-access-button').click(function() {
revokeAccess();
});
});
}
function handleAuthClick() {
if (GoogleAuth.isSignedIn.get()) {
// User is authorized and has clicked 'Sign out' button.
GoogleAuth.signOut();
} else {
// User is not signed in. Start Google auth flow.
GoogleAuth.signIn();
}
}
function revokeAccess() {
GoogleAuth.disconnect();
}
function setSigninStatus(isSignedIn) {
var user = GoogleAuth.currentUser.get();
var isAuthorized = user.hasGrantedScopes(OAUTH2_SCOPES);
if (isAuthorized) {
$('#sign-in-or-out-button').html('Sign out');
$('#revoke-access-button').css('display', 'inline-block');
$('#auth-status').html('You are currently signed in and have granted ' +
'access to this app.');
//// get gmail Email
gapi.client.init({
'apiKey': API_KEY,
'discoveryDocs': ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"],
'clientId': OAUTH2_CLIENT_ID,
'scope': OAUTH2_SCOPES
}).then(function () {
var request = gapi.client.gmail.users.getProfile({
'userId': 'me'
}).then(function(response) {
email = JSON.parse(response.body).emailAddress;
cf_JSON.clear();
cf_JSON.addItem( 'email',email );
var jsonstr = cf_JSON.make();
tryFindUserByEmail(jsonstr);
});
});
// try to find email
} else {
$('#sign-in-or-out-button').html('Вход через Youtube');
$('#revoke-access-button').css('display', 'none');
$('#auth-status').html('You have not authorized this app or you are ' +
'signed out.');
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
How to manage permissions:
When you authenticate a user you are given access to that users account data and only that user. So if you are trying to access data on someone else's account they are not going to have permissions to access it and you are going to get the 403 forbidden error.
Without seeing your code its hard to know what you are doing, but I can guess.
You are using Oauth2 to authenticate users.
You are trying to access something with a hard coded id belonging to your personal account which the user does not have access.
How to fix it will depend on what it is you are trying to do.
You need to check some authentication in the API url like
username , ipaddress , token etc.
Based on the parameter you can control the permission on your API request.for example
http://some/thing?username="testuser"&ipaddress="323.2323.232.32"
You can find the parameters value using the function below
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
And then make you check and implement your error and redirection for specific users.
I guess it will help full for you , Thanks !
Related
I'm trying to implement Google Fit, in a school project.
I am using the documentation released by Google but it does not work.
I am getting 403 bad request error
Is it possible because I am using XAMPP to try to connect, it gives me an error?
I have entered the request domains on the Google project.
Can you help me?
Also I have a question:
Does it cost to use Google Fit Api rest?
Thanks everyone for your answers
var CLIENT_SECRET;
var CLIENT_ID;
$.post('healthDataFunc.php', { functionname: 'get_app_credentials' }, function(data){
var result = JSON.parse(data);
if (result){
CLIENT_SECRET = result[0];
CLIENT_ID = result[1];
}
});
var CLIENT_REDIRECT = "https://localdefault.com:4433";
//End-Configuration
var SCOPES_FITNESS = 'https://www.googleapis.com/auth/fitness.activity.read+https://www.googleapis.com/auth/fitness.body.read+https://www.googleapis.com/auth/fitness.location.read+https://www.googleapis.com/auth/fitness.blood_pressure.read+https://www.googleapis.com/auth/fitness.sleep.read';
var GoogleAuth;
/*
Initial request for Google authentication code
Opens google auth window
returns Google Auth token
*/
function requestGoogleoAuthCode() {
// Load the API's client and auth2 modules.
// Call the initClient function after the modules load.
gapi.load('client:auth2', initClient);
console.log(gapi);
}
function initClient() {
// In practice, your app can retrieve one or more discovery documents.
var discoveryUrl = "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest";
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': CLIENT_SECRET,
'clientId': CLIENT_ID,
'discoveryDocs': [discoveryUrl],
'scope': SCOPES_FITNESS
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
GoogleAuth.signIn();
console.log("test--------->", GoogleAuth, user);
});
}
/*
Uses Google Auth code to get Access Token and Refresh Token
returns object with access token, refresh token and access token expiration
*/
function getAccessToken(google_auth_code) {
var retVal = null;
jQuery.ajax({
url: "https://www.googleapis.com/oauth2/v3/token?code=" + google_auth_code + "&redirect_uri=" + CLIENT_REDIRECT + "&client_id=" + CLIENT_ID + "&client_secret=" + CLIENT_SECRET + "&scope=&grant_type=authorization_code",
type: "post",
success: function (result) {
console.log("Got Access Token And Refresh Token");
retVal = result;
console.log(result);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Error during getAccessToken");
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
retVal = null;
},
async: false
});
return retVal;
}
/*
Uses Refresh token to obtain new access token
returns new access token with expiration
*/
function refreshAccessToken(refresh_token) {
var retVal = null;
jQuery.ajax({
url: "https://www.googleapis.com/oauth2/v3/token?client_secret=" + CLIENT_SECRET + "&grant_type=refresh_token&refresh_token=" + refresh_token + "&client_id=" + CLIENT_ID,
type: "post",
success: function (result) {
console.log("Refreshed Access Token");
retVal = result;
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Error during refreshAccessToken");
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
retVal = null;
},
async: false
});
return retVal;
}
function revokeAccess(accessToken) {
GoogleAuth.disconnect();
}
var isAuthorized;
var currentApiRequest;
/**
* Store the request details. Then check to determine whether the user
* has authorized the application.
* - If the user has granted access, make the API request.
* - If the user has not granted access, initiate the sign-in flow.
*/
function sendAuthorizedApiRequest(requestDetails) {
currentApiRequest = requestDetails;
if (isAuthorized) {
// Make API request
// gapi.client.request(requestDetails)
// Reset currentApiRequest variable.
currentApiRequest = {};
} else {
GoogleAuth.signIn();
}
}
/**
* Listener called when user completes auth flow. If the currentApiRequest
* variable is set, then the user was prompted to authorize the application
* before the request executed. In that case, proceed with that API request.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
isAuthorized = true;
if (currentApiRequest) {
sendAuthorizedApiRequest(currentApiRequest);
}
} else {
isAuthorized = false;
}
}
I am working on a react-redux application where the user can submit their feedback/complaint to respective organization. The idea is to send emails on user's consent for which I am using Gmail API. I have integrated a feedback form.
Now the user has to be logged in before submission of the message. I do not want to add Gmail send (https://www.googleapis.com/auth/gmail.send) scope with login. Login(on the header) only works with the profile and read-only scopes. Send email permission should only be asked separately when submit is clicked and then take further action on that consent. How can I achieve this send email scope separately? I am writing down my login code as I am not sure what has to be done on submit.
var clientId = 'xxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
var API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxx';
var scopes = 'https://www.googleapis.com/auth/gmail.readonly '+'https://www.googleapis.com/auth/userinfo.profile';
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];
componentDidMount() {
this.handleClientLoad();
}
handleClientLoad = () => {
const script = document.createElement("script");
script.src = "https://apis.google.com/js/client.js";
script.async = true
script.onload = () => {
window.gapi.load('client:auth2', () => {
window.gapi.client.setApiKey(API_KEY);
window.setTimeout(this.initClient(), 1);
});
}
document.body.appendChild(script)
}
initClient = () => {
window.gapi.client.init({
apiKey: API_KEY,
discoveryDocs: DISCOVERY_DOCS,
clientId: clientId,
scope: scopes,
}).then(() => {
window.gapi.auth2.getAuthInstance().isSignedIn.listen(this.updateSigninStatus());
this.updateSigninStatus(window.gapi.auth2.getAuthInstance().isSignedIn.get());
});
}
updateSigninStatus = (isSignedIn) => {
if (isSignedIn) {
window.gapi.auth2.getAuthInstance().currentUser.listen((user) => {
console.log(user);
});
} else {
console.log("error");
}
}
authClick = () => {
window.gapi.auth2.getAuthInstance().signIn().then((user) =>
console.log(user)
)}
Following is the order:
User logs in
Fill out the feedback form
Hit submit (ask user's consent using Gmail send scope)
Perform actions on the basis of consent.
I am stuck on the 3rd step. How to add scope separately to get user consent?
you cant your going to need to request consent at the time you authenticate your user. You cant split that up.
You will need to add the Gmail.send scope here.
var scopes = 'https://www.googleapis.com/auth/gmail.readonly '+'https://www.googleapis.com/auth/userinfo.profile';
I am releasing access to pages using connect-roles and loopback but I have a pertinent question about how I can collect the customer's role and through the connect-roles to read the session and respond to a route.
Example, when the client logs in I load a string containing the client's role and access it in a function that controls access to pages.
I have this doubt because I'm finalizing a large scale service that usually there are multiple client sessions that are accessed instantly using a same storage and check function.
It would be efficient to store the customer's role using app.set() and app.get()?
app.get('/session-details', function (req, res) {
var AccessToken = app.models.AccessToken;
AccessToken.findForRequest(req, {}, function (aux, accesstoken) {
// console.log(aux, accesstoken);
if (accesstoken == undefined) {
res.status(401);
res.send({
'Error': 'Unauthorized',
'Message': 'You need to be authenticated to access this endpoint'
});
} else {
var UserModel = app.models.user;
UserModel.findById(accesstoken.userId, function (err, user) {
// console.log(user);
res.status(200);
res.json(user);
// storage employee role
app.set('employeeRole', user.accessLevel);
});
}
});
});
Until that moment everything happens as desired I collect the string loaded with the role of the client and soon after I create a connect-roles function to validate all this.
var dsConfig = require('../datasources.json');
var path = require('path');
module.exports = function (app) {
var User = app.models.user;
var ConnectRoles = require('connect-roles');
const employeeFunction = 'Developer';
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('ejs')) {
res.send('Access Denied - You don\'t have permission to: ' + action);
} else {
res.render('access-denied', {action: action});
// here
console.log(app.get('employeeRole'));
}
}
});
user.use('authorize access private page', function (req) {
if (employeeFunction === 'Manager') {
return true;
}
});
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
app.use(user.middleware());
};
Look especially at this moment, when I use the
console.log(app.get('employeeRole')); will not I have problems with simultaneous connections?
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
Example client x and y connect at the same time and use the same function to store data about your session?
Being more specific when I print the string in the console.log(app.get('employeeRole')); if correct my doubt, that I have no problem with simultaneous connections I will load a new variable var employeeFunction = app.get('employeeRole'); so yes my function can use the object containing the role of my client in if (employeeFunction === 'Any Role') if the role that is loaded in the string contain the required role the route it frees the page otherwise it uses the callback of failureHandler.
My test environment is limited to this type of test so I hope you help me on this xD
Instead of using app.set you can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('employeeRole', testObj);
hashmap.auth.get('employeeRole');
hashmap.auth.all();
hashmap.auth.delete('employeeRole');
I'm trying to understand the flow how to authenticate user on WEB client (JS), and then use Google API on my back-end server (ASP.NET MVC application), on behalf of authenticated user for retrieving users contacts list.
Here the current flow that I use:
1.In HTML I use google JS client: https://apis.google.com/js/client.js:
function auth(callback) {
var config = {
'client_id': '***********',
'scope': 'https://www.googleapis.com/auth/contacts.readonly'
};
config.immediate = true;
gapi.auth.authorize(config, function (authResult) {
if (authResult && !authResult.error) {
callback();
}
else {
config.immediate = false;
gapi.auth.authorize(config, function (response) {
//Here I send access_token to back-end using HTTPS
});
}
});
}
2.Then I use gapi.auth.getToken() and send it to back-end server (Using a HTTPS AJAX call)
3.Then on server I have the following code in controller:
public JsonResult Get(TokenModel model)
{
//Custom store for access_token
var myStore = new MyStore(NewtonsoftJsonSerializer.Instance.Serialize(new TokenResponse() { Issued = DateTime.Now, ExpiresInSeconds = 3600, TokenType = "Bearer", AccessToken = model.access_token }));
string[] Scopes = { PeopleService.Scope.ContactsReadonly };
ClientSecrets secrets = new ClientSecrets() { ClientId = "******", ClientSecret = "******" };
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
secrets,
Scopes,
"user",
CancellationToken.None,
myStore
).Result;
var service = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
List<string> result = GetPeople(service, null);
return Json(result);
}
Questions:
Is it the correct flow and does GoogleWebAuthorizationBroker is a correct class to use on server in my case?
Why and HOW GoogleWebAuthorizationBroker opens a new browser window for authentication, in case model.access_token = null?
Why when the token is not valid (ex: “dasdasdasdas”), AuthorizeAsync method returns me the UserCredential that looks absolutely valid, but then the exception occurs when make actual request to google api.
How from the above flow, I can get “refresh token” for later use (as I understand, I need somehow generate it myself, using access_token + secret key).
Thanks!
1.Used the code from this link: Sending email from gmail api not received but shown in sent folder
2. I'm using domino server locally with Domino Designer 9
3. Making sure that I'm able to authorize with Gmail api with my google client id (logout from gmail, run the code which is asking me login again)
The modified version of the above code is not working.
What is wrong in my code or setup.
Here is full code.
// Your Client ID can be retrieved from your project in the Google
// Developer Console, https://console.developers.google.com
// function assignval(cl,sc){
// var CLIENT_ID = '261056497849-8kj87m3pjmqko8iot7kpdee2htmaf29a.apps.googleusercontent.com';
// var CLIENT_ID = cl;
//var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
// var SCOPES = sc;
// }
var CLIENT_ID = '204856067483-0ib90ohcb1itdvho93cf33pc8g83t4lp.apps.googleusercontent.com';
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly','https://mail.google.com/','https://www.googleapis.com/auth/gmail.modify','https://www.googleapis.com/auth/gmail.compose','https://www.googleapis.com/auth/gmail.send'];
/**
* Check if current user has authorized this application.
*/
function auth() {
var config = {
'client_id': CLIENT_ID,
'scope': SCOPES
};
gapi.auth.authorize(config, function() {
console.log('login complete');
console.log(gapi.auth.getToken());
});
}
function checkAuth() {
gapi.auth.authorize(
{
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, handleAuthResult);
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
function handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
// Hide auth UI, then load client library.
authorizeDiv.style.display = 'none';
loadGmailApi();
} else {
// Show auth UI, allowing the user to initiate authorization by
// clicking authorize button.
authorizeDiv.style.display = 'inline';
}
}
/**
* Initiate auth flow in response to user clicking authorize button.
*
* #param {Event} event Button click event.
*/
function handleAuthClick(event) {
gapi.auth.authorize(
{client_id: CLIENT_ID, scope: SCOPES, immediate: false},
handleAuthResult);
return false;
}
/**
* Load Gmail API client library. List labels once client library
* is loaded.
*/
function loadGmailApi() {
gapi.client.load('gmail', 'v1',send());
}
function sendMessage(email, callback) {
//auth();
gapi.client.load('gmail', 'v1',function(){
var base64EncodedEmail = btoa(email).replace(/\//g,'_').replace(/\+/g,'-');
alert("Message sending" + base64EncodedEmail);
var request = gapi.client.gmail.users.messages.send({
'userId': 'me',
'message': {
'raw': base64EncodedEmail
}
});
request.execute(callback);
});
}
function send() {
var to = 'mohan_gangan#yahoo.com',
subject = 'Gmail API sendmessage test',
content = 'send a Gmail using domino server'
var email ="From: 'me'\r\n"+
"To: "+ to +"\r\n"+
"Subject: "+subject+"\r\n"+
"\r\n"+
content;
alert("Message sent to Gamil API");
sendMessage(email, function (response) {
//console.log("Handed to Gmail API for sending");
{console.log(response)}
});
alert("Message sent");
}