Google Calendar API returning "Daily Limit" - google-api

I'm using the nodejs googleapis module to authenticate the google calendar API. I've managed to get an access token. My code is below:
var tok = {
access_token: 'ya29.XXXXX-XXXXXXX',
refresh_token: '1/XXXXXXXXXXX',
scope: 'https://www.googleapis.com/auth/calendar',
token_type: 'Bearer',
expiry_date: 1569089181800
}
const calendar = google.calendar({version: 'v3', tok});
calendar.events.list({
calendarId: 'primary',
timeMin: (new Date()).toISOString(),
maxResults: 10000,
singleEvents: true,
orderBy: 'startTime',
}, (err, res) => {
});
Each time I run this command I get the following error: The API returned an error: Error: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.
Can't figure out why this is the case (seeing as it was working before and I've only used a handful of requests). I've enabled billing and enabled/disabled/re-enabled the calendar API in the Google Cloud console.

It appears that the token is expired since the epoch value corresponds to
Sep 21 2019 17:30:00 UTC/GMT
I think you need a fresh token

Related

How to use google reseller api using service account

I want to access Google reseller api to get customers and subscriptions using google service account key but not able to do it. Below is my code snippet:
async function runSample() {
const auth = new google.auth.GoogleAuth({
keyFile: "../server/credentials/serviceAccountKey.json",
scopes: ["https://www.googleapis.com/auth/apps.order",
"https://www.googleapis.com/auth/apps.order.readonly"
],
});
// Acquire an auth client, and bind it to all future calls
const authClient = await auth.getClient();
google.options({ auth: authClient });
// Do the magic
const res = await reseller.subscriptions.list();
console.log(res.data);
}
runSample().catch(console.error);
Here I want to get list of the subscription from google reseller console. I referenced above code from google documentation. Here I am getting the error 'Authenticated user is not authorized to perform this action.' and reason given is 'Insufficient permissions'.
errors: [
{
message: 'Authenticated user is not authorized to perform this action.',
domain: 'global',
reason: 'insufficientPermissions'
}
]
If I try to access cloud channel service api I can using the same service account key but it is giving error for reseller api.
I have given service account the owner, cloud workstation admin and service account admin role access.
I have also added scopes in domain wide delegation(dwd).
What else permission do I need?
In order to use a service account it must first be configured though your google workspace account Create a service account
You must also denote in your code the name of the user who your service account has been configured to impersonate.
const auth = new google.auth.GoogleAuth({
keyFile: "../server/credentials/serviceAccountKey.json",
clientOptions: {
subject: 'user#yourdomain.com'
},
scopes: ["https://www.googleapis.com/auth/apps.order"
],
});

Google Cloud Search Query via Node.js Error: This project doesn't have Cloud Search's Query API Enabled

I am working on Google Cloud Search API to search documents stored in Google Drive. I have Google Workspace account and few documents stored in Google Drive. I am able to search using Google Cloud search console but facing issue using below node.js code for searching using API.
Issue: I am able to generate the access token but get below error for search query:
Error: This project doesn't have Cloud Search's Query API Enabled,
and/or the Cloud Search Platform license has not been assigned to the
user account calling the Query API
var {google} = require("googleapis");
var serviceAccount = require('C:/nodejstest/key/serviceAccountKey.json');
// Specify the required scope.
var scopes = [
"https://www.googleapis.com/auth/cloud_search",
"https://www.googleapis.com/auth/cloud_search.query"
];
var jwtClient = new google.auth.JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: scopes,
subject: 'sample#example.com'
});
// Use the JWT client to generate an access token.
jwtClient.authorize(function(error, tokens) {
if (error) {
console.log("Error making request to generate access token:", error);
} else if (tokens.access_token === null) {
console.log("Provided service account does not have permission to generate access tokens");
} else {
var accessToken = tokens.access_token;
console.log('accessToken= ' + accessToken)
// Include the access token in the Authorization header.
}
});
const service = google.cloudsearch({version: 'v1'});
service.query.search({
auth: jwtClient,
requestBody: {
requestOptions: {
searchApplicationId: 'searchapplications/default',
debugOptions:{enableDebugging: true}
},
query: 'My query'
}
}).then((res) => {
console.log(JSON.stringify({results:res.results.length}));
console.log(JSON.stringify({resultsInfo:res.results[0]}));
}).catch((err) => {
console.error('Unexpected error with cloud search API.');
console.error(err.toString());
});
In above code I am passing workspace admins email id as subject.
I followed steps mentioned at below link
Configure access to the Google Cloud Search REST API
Perform Google Workspace domain-wide delegation of authority
Go to the google console then select the your project.
Then search for Cloud Search API then enable it.
Also make sure that your service account have access on Cloud Search Indexing API

Is there any problem of showing the api token in source? (Laravel + Vue.js)

My situation is that I want to do api request at the front-end (using axios)
The api request requires to add the api token in the header of the request.
And I want to store the api token securely, but I have no idea how to store the api token securely.
I just figure the way below and everything works nicely.
However, I am afraid if there is any security breach by doing so.
Let say I get the api token by regenerating and stored in a variable.
in
user-script.blade.php
var vm = new Vue({
el: "#api",
name: "api_token",
data: () => ({
api_token: '{!! \Illuminate\Support\Facades\Auth::check() ? \Illuminate\Support\Facades\Auth::user()->createToken('ApiToken')->accessToken : 'null' !!}',
devices: {}
}),
mounted() {
axios.get('/api/device',
{
headers: {
Authorization: "Bearer "+this.api_token
}
}
)
.then((response) => {
this.devices = response.data;
})
.catch((error) => {
console.log(error);
})
}
})
</script>
However, the api_token will be shown in devtool source.
devtool source
Is there any security breach if I show this to the users?
Is there any security breach if I store users' api token in the database?
Or how can I get the api token after I have already logged in?
On the client side you cant't prevent code modification.
Any user can view your source code of all your JavaScript files.
If you care about security read material related to web vulnerabilities such as XCC, CSRF.

Can't get oauth token from google smart home action sync intent in aws lambda

I am using aws lambda function for google smart home action. I used aws api gateway for fulfillment url to reach lambda. I can successfully handle google assistant's intents with below code:-
const {smarthome} = require('actions-on-google');
const app = smarthome();
app.onExecute((body, headers) => {
return {
requestId: 'ff36...',
payload: {
// ...
},
};
});
app.onQuery((body, headers) => {
return {
requestId: 'ff36...',
payload: {
// ...
},
};
});
app.onSync((body, headers) => {
console.log("body: "+JSON.stringify(body));
console.log("headers: "+JSON.stringify(headers));
return {
requestId: 'ff36...',
payload: {
// ...
},
};
});
exports.handler = app;
On hard coding device details in this function, It can successfully reflect in google home app. But to get actual devices of user I need to get oauth token from "SYNC" intent. But all I got from this code is this output:-
body: {"inputs":[{"intent":"action.devices.SYNC"}],"requestId":"5604033533610827657"}
headers: {}
Unlike "Discover Directive" of Alexa's skill, which contains token in request.directive.endpoint.scope.token, google's intent doesn't seems to carry it. For O Auth, I am using AWS Cognito which works fine with Alexa Account linking and for google home too it can successfully link the account and show devices which I hardcode in lambda function.
As per this answer, the token is in
headers.authorization.substr(7)
I've tried that and got nothing. It shows
"Cannot read property 'substr' of undefined".
The lambda handler in the Actions on Google client library assumes that the request headers are present at event.headers within the input event parameter of a Lambda Proxy Integration. If you have a custom Lambda integration or have otherwise modified the input mapping, you may need to edit your mapping template to ensure the headers are placed where the client library expects.

Invalid signature while validating Azure ad access token, but id token works

I am getting invalid signature while using jwt.io to validate my azure ad access token. My id token, however, validates just fine!
I have seen and tried the solutions suggested in
Invalid signature while validating Azure ad access token
and
https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx
but neither works for my access token.
The access and Id token is generated via Adal.js:
var endpoints = {
"https://graph.windows.net": "https://graph.windows.net"
};
var configOptions = {
tenant: "<ad>.onmicrosoft.com", // Optional by default, it sends common
clientId: "<app ID from azure portal>",
postLogoutRedirectUri: window.location.origin,
endpoints: endpoints,
}
window.authContext = new AuthenticationContext(configOptions);
Why can I validate my ID token, but not my access token?
Please refer to thread : https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609
but if look at the Jwt.Header you will see a 'nonce'. This means you need special processing. Normal processing will fail.
So if nonce includes in access token , validate signature with JWT.io or JwtSecurityToken won't success .
If anyone else has invalid signature errors, you should check this comment : https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/521#issuecomment-577400515
Solved the issue for my configuration.
Essentially, if you are getting access tokens to access your own resource server and not the Graph API, your scopes parameter should be [CLIENT_ID]/.default (and if you are using the access token to access the Graph API, you don't need to validate the token yourself)
Thanks to Nan Yu I managed to get token that can be validated by any public jwt validator like jwt.io
(couldn't put my comment in the comments section under Nan Yu's answer because its too long).
So as I understand the point from the discussion mentioned by Nan Yu that by default Azure AD generates tokens for Microsoft Graph and these tokens use special signing mechanism so that it is not possible to validate signature using public validators (except jwt.ms Microsoft's validator which most probably knows what mysterious special handling means :) ).
To get access token not for Microsoft Graph that can be validated using public validators I had to:
Remove any Microsoft Graph related scopes (by default I had only one scope configured User.Read so removed it in appConfig > API permissions)
create a custom scope for your application (appConfig > Expose an API > Add scope ...) this scope will look like api://{application-id}/scope-name
add just created scope in the application API permissions (appConfig > API permissions > Add api permission > My APIs > select your application > Delegated Permissions > Check your scope > Add permission)
then use this scope in your openid client scopes, in my case I have: openid offline_access {application-id}/scope-name
Note that in the openid client config newly created scope is used without api:// prefix (offline_access I have to enable refresh_token can be ignored if refresh token mechanism is not used)
Well thanks to #Antoine I fix my code. Here I will let my personal vue.js plugin that is working for everybody else reference:
import { PublicClientApplication } from '#azure/msal-browser'
import { Notify } from 'quasar'
export class MsalService {
_msal = null
_store = null
_loginRequest = null
constructor (appConfig, store) {
this._store = store
this._msal = new PublicClientApplication(
{
auth: {
clientId: appConfig.auth.clientId,
authority: appConfig.auth.authority
},
cache: {
cacheLocation: 'localStorage'
}
})
this._loginRequest = {
scopes: [`${appConfig.auth.clientId}/.default`]
}
}
async handleResponse (response) {
await this._store.dispatch('auth/setResponse', response)
const accounts = this._msal.getAllAccounts()
await this._store.dispatch('auth/setAccounts', accounts)
if (accounts.length > 0) {
this._msal.setActiveAccount(accounts[0])
this._msal.acquireTokenSilent(this._loginRequest).then(async (accessTokenResponse) => {
// Acquire token silent success
// Call API with token
// let accessToken = accessTokenResponse.accessToken;
await this._store.dispatch('auth/setResponse', accessTokenResponse)
}).catch((error) => {
Notify.create({
message: JSON.stringify(error),
color: 'red'
})
// Acquire token silent failure, and send an interactive request
if (error.errorMessage.indexOf('interaction_required') !== -1) {
this._msal.acquireTokenPopup(this._loginRequest).then(async (accessTokenResponse) => {
// Acquire token interactive success
await this._store.dispatch('auth/setResponse', accessTokenResponse)
}).catch((error) => {
// Acquire token interactive failure
Notify.create({
message: JSON.stringify(error),
color: 'red'
})
})
}
})
}
}
async login () {
// this._msal.handleRedirectPromise().then((res) => this.handleResponse(res))
// await this._msal.loginRedirect(this._loginRequest)
await this._msal.loginPopup(this._loginRequest).then((resp) => this.handleResponse(resp))
}
async logout () {
await this._store.dispatch('auth/setAccounts', [])
await this._msal.logout()
}
}
// "async" is optional;
// more info on params: https://quasar.dev/quasar-cli/boot-files
export default ({
app,
store,
Vue
}) => {
const msalInstance = new MsalService(
app.appConfig, store
)
Vue.prototype.$msal = msalInstance
app.msal = msalInstance
}
PD: using quasar framework
If you are using msal.js library with react, add this to your auth configuration.
scopes: [`${clientId}/.default`]
Editing scopes fixed issue for me

Resources