insufficient_scope when trying to use Google admin.directory.group API with ADC - google-api

I am trying to use ADC to use Google Admin directory/groups API but getting insufficient_scope error. My email address attached to ADC is a super admin in Google workspace. I am not sure whats going wrong, here is the code:
const {google} = require('googleapis');
async function main () {
const auth = new google.auth.GoogleAuth({
scopes: [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/admin.directory.group',
'https://www.googleapis.com/auth/admin.directory.user'
],
});
const service = await google.admin({version: 'directory_v1', auth});
const res = await service.members.list({
groupKey: 'my-group#my-domain'
}).then(
res => console.log(res.data)
)
console.log(res.data.members);
}
main().catch(console.error);
And I get following error:
GaxiosError: Insufficient Permission
at Gaxios._request (/Users/kumar.gaurav/Documents/work/my-project/node_modules/gaxios/build/src/gaxios.js:130:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async UserRefreshClient.requestAsync (/Users/kumar.gaurav/Documents/work/my-project/node_modules/google-auth-library/build/src/auth/oauth2client.js:382:18)
at async main (/Users/kumar.gaurav/Documents/work/my-project/test-adc.js:28:17) {
response: {
config: {
url: 'https://admin.googleapis.com/admin/directory/v1/groups/my-group%40my-domain/members',
method: 'GET',
userAgentDirectives: [Array],
paramsSerializer: [Function (anonymous)],
headers: [Object],
params: {},
validateStatus: [Function (anonymous)],
retry: true,
responseType: 'json',
retryConfig: [Object]
},
data: { error: [Object] },
headers: {
'cache-control': 'private',
connection: 'close',
'content-encoding': 'gzip',
'content-type': 'application/json; charset=UTF-8',
date: 'Thu, 29 Dec 2022 12:35:44 GMT',
server: 'ESF',
'transfer-encoding': 'chunked',
vary: 'Origin, X-Origin, Referer',
'www-authenticate': 'Bearer realm="https://accounts.google.com/", error="insufficient_scope", scope="https://apps-apis.google.com/a/feeds/groups/ https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/directory.group https://www.googleapis.com/auth/admin.directory.group.member https://www.googleapis.com/auth/admin.directory.group.member.readonly https://www.googleapis.com/auth/apps.directory.group.member.readonly https://www.googleapis.com/auth/directory.group.member.readonly https://www.googleapis.com/auth/admin.directory.group.readonly https://www.googleapis.com/auth/apps.directory.group.readonly https://www.googleapis.com/auth/directory.group.readonly"',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '0'
},
status: 403,
statusText: 'Forbidden',
request: {
responseURL: 'https://admin.googleapis.com/admin/directory/v1/groups/my-group%40my-domain/members'
}
},
config: {
url: 'https://admin.googleapis.com/admin/directory/v1/groups/my-group%40my-domain/members',
method: 'GET',
userAgentDirectives: [ [Object] ],
paramsSerializer: [Function (anonymous)],
headers: {
'x-goog-api-client': 'gdcl/6.0.4 gl-node/18.4.0 auth/8.7.0',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/6.0.4 (gzip)',
Authorization: 'Bearer ya29.someToken',
Accept: 'application/json'
},
params: {},
validateStatus: [Function (anonymous)],
retry: true,
responseType: 'json',
retryConfig: {
currentRetryAttempt: 0,
retry: 3,
httpMethodsToRetry: [Array],
noResponseRetries: 2,
statusCodesToRetry: [Array]
}
},
code: 403,
errors: [
{
message: 'Insufficient Permission',
domain: 'global',
reason: 'insufficientPermissions'
}
]
}
Here is my application-default-credential.json file:
cat ~/.config/gcloud/application_default_credentials.json
{
"client_id": "someClientId.apps.googleusercontent.com",
"client_secret": "someClientSecret",
"refresh_token": "someRefreshToken",
"type": "authorized_user"
}
Interesting thing is that if I use admin.directory.user API, that works:
// works
const res = await service.users.list({
customer: 'my_customer',
maxResults: 10,
orderBy: 'email',
});
console.log(res.data.members);
I am workspace admin, so I am assuming my ADC would be sufficient to get token for admin.directory.group API. Can I get idea about what could be going wrong?
Thanks.

Members.list requires one of the following scooes
https://apps-apis.google.com/a/feeds/groups/
https://www.googleapis.com/auth/admin.directory.group
https://www.googleapis.com/auth/admin.directory.group.member
https://www.googleapis.com/auth/admin.directory.group.member.readonly
https://www.googleapis.com/auth/admin.directory.group.readonly
You appear to be using one of these. My guess its that you have already authorized the user once then changed the scopes but did not authorize the user again using the new scopes
You need to either remember access directly in the users google account or remove it in your code I can't see that you are storing it locally could it be a cookie or something sorry not the best at node.js

Related

How to POST a request with the urlencode mode?

I'm a beginner with cypress, and I'm trying to send a POST request with mode urlencode to get the access token. This is script in postman:
const postRequest = {
url: 'https://login.microsoftonline.com/967731f1/oauth2/v2.0/token',
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
mode: 'urlencoded',
urlencoded:
[
{key: "client_id",value: "459e40d6-e96a-4c4e-833f-f43bd5d71885"},
{key: "scope",value: "api://icy-beach-05c371d10.1.azurestaticapps.net/459e40d6-e96a-4c4e-833f-f43bd5d71885/access-as-user"},
{key: "grant_type",value: "password"},
{key: "username",value: "abc#gmail.com"},
{key: "password",value: "xxxxxx"},
]
}
};
pm.sendRequest(postRequest, (error, response) => {
if (error) {
console.log(error);
}
pm.test('response should be okay to process', () => {
pm.environment.set("accesstoken_admin", response.json().access_token);
});
});
So, I do not know how to work on cypress. Does anyone help me? Many thanks!
/// <reference types="cypress"/>
describe('login and create a new client',()=>{
it('login by admin account',() => {
cy.request({
method: 'POST',
url:'https://login.microsoftonline.com/967731f1/oauth2/v2.0/token',
form: true,
body: {
client_id: "459e40d6-e96a-4c4e-833f-f43bd5d71885",
scope: "api://icy-beach-05c371d10.1.azurestaticapps.net/459e40d6-e96a-4c4e-833f-f43bd5d71885/access-as-user",
grant_type: "password",
username: "abc#gmail.com",
password: "xxxxxx",
}
}).then((res) => {
cy.log(JSON.stringify(res))
})
})
})

fastify-http-proxy: Failure with "Promise may not be fulfilled with 'undefined'

I am using the fastify with fastify-http-proxy with the following configuration:
const proxy = require('fastify-http-proxy');
fastify.register(proxy, {
upstream: "https://myendpoint/occm/api/azure",
prefix: '/azure',
onResponse: (request, reply, res) => {
console.log('Inside onResponse')
reply.send('New body of different length');
},
onError: (reply, error) => {
console.log('Inside onError')
reply.send(`My Error: ${error}`);
},
replyOptions: {
rewriteRequestHeaders: (originalRequest, headers) => {
console.log('Inside rewriteRequestHeaders')
return {
...headers,
};
},
},
});
I have configured a fastify route as this:
fastify.route({
method: 'GET',
url: `/azure/vsa/working-environments`,
schema: {
description: 'Trying out',
tags: ['notifications'],
security: [{
authorization: []
}],
params:{
title: "Working Env GET",
description:'Trying out...',
type: 'object',
example: 'No input',
properties: {
'accountId': {
description: "Account ID",
type: 'string'
},
},
}
},
handler: async (request, reply) => {
}
I am calling it as:
GET http://localhost:xxxx/azure/vsa/working-environments
Basically I would like to proxy the request to this endpoint:
https://myendpoint/occm/api/azure/vsa/working-environments
Now whenever I am running it, getting this error:
{"level":30,"time":1644249539394,"pid":12100,"hostname":"pradipm02-PC","requestId":"Q3T9RjI11N","req":{"method":"GET","url":"/azure/vsa/working-environments","hostname":"localhost:8092","remoteAddress":"127.0.0.1","remotePort":63308},"msg":"incoming request"}
{"level":50,"time":1644249540657,"pid":12100,"hostname":"pradipm02-PC","requestId":"Q3T9RjI11N","err":{"type":"FastifyError","message":"Promise may not be fulfilled with 'undefined' when statusCode is not 204","stack":"FastifyError: Promise may not be fulfilled with 'undefined' when statusCode is not 204\n at C:\\Users\\pradipm\\clients\\CloudManager\\cm_2\\occm\\service-infra\\notifications\\node_modules\\fastify\\lib\\wrapThenable.js:30:30\n at processTicksAndRejections (node:internal/process/task_queues:96:5)","name":"FastifyError","code":"FST_ERR_PROMISE_NOT_FULFILLED","statusCode":500},"msg":"Promise may not be fulfilled with 'undefined' when statusCode is not 204"}
Any of the console.log in the rewriteRequestHeaders, onReponse, onError are not showing up.
Any help is appreciated.
This is discussed in the following thread in fastify-http-proxy forum.
It is suggested to use fastify-reply-from instead.
That solves my issue.

How/Where does nuxtjs set the authorization header

I inherited a web project from another developer who's left the company. It is built with nuxtjs and laravel, neither of which I am strong with.
In the web project, after a user goes to a login screen, types in email and password, and presses save, I can see in my developer tools that the nuxtjs framework sends a {email: "user#email.com", password: "password"} to the laravel api at https://example.project.com/api/login. The laravel api then returns a 200 response with this payload { data: { message: "Login successful.", token: "50|Fs88RPU7HON1LnBskm35O9txG0OnXDiG3x4XWILJ" }}. Everything is great so far!
Immediately after the 200 response above, NuxtJS sends another request to https://example.project.com/api/auth/user in an attempt to get information about this user. Nginx gives a 401 response. My first suspicion for why this happens is because NuxtJS sent an Authorization: Bearer undefined as shown in this screenshot of my browser dev tools
I've seen other situations with Authorization: Bearer [object object].
I want to know how does NuxtJS decide what value to provide to the Authorization http header? This is my current nuxt.config.js
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'Blah',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.png' }],
},
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'#nuxtjs/axios',
'#nuxtjs/auth'
],
auth: {
redirect: {
login: '/login',
logout: '/',
callback: '/api/login',
home: '/'
},
strategies: {
local: {
endpoints: {
login: { url: '/api/login'},
user: { url: '/api/auth/user'}
},
}
},
localStorage: true
},
proxy: {
'/api': {
target: 'https://example.project.com/',
pathRewrite: { '^/api': '' },
},
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
baseURL: 'https://example.project.com/',
credentials: true,
headers : {
common: {
'Accept' : 'application/json'
}
}
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}
And also, this is the line of code in pages/login.vue that starts the login process:
await this.$auth.loginWith('local', { data: {email:"user#email.com",password:"password"} });
Simply add Authorization header as default header right after authorization request. Assuming that server sends access token in response the code might look like this:
const response = await this.$auth.loginWith('local', {
data: {
email: "user#email.com",
password:"password"
}
})
const { token } = response;
axios.defaults.headers.common = { Authorization: `Bearer ${token}` };
change your strategy as below to set property name of the token being returned in response.
strategies: {
local: {
token: {
property: 'token'
},
endpoints: {
login: { url: '/api/login'},
user: { url: '/api/auth/user'}
},
}
},
It will include the authorization header in all your requests using $axios.
also you might need to set propery of the user which you are returning from backend also.
This is done by a library in the background. #nuxtjs/auth or #nuxtjs/auth-next will set the token depending on your config in nuxt.config.ts.
You can find the token in the local storage in auth._token.<strategy>. To use a Bearer token in the subsequent userinfo request you might need to set:
nuxt.config.ts
auth: {
strategies: {
local: {
token: {
type: 'Bearer',
}
}
}

React Native getting response 400 when post to oauth/token using laravel passport

I am trying to login through oauth/tokens from my React Native project. I have tested with POSTMAN When i send the data from axios it works well. But when i try to send from my app it gives me Error: Request failed with status code 400
my code is :
axios({
method: 'post',
url: 'http://183.172.100.84:8000/oauth/token',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
},
data: {
client_id : '2',
client_secret : 'secret',
grant_type : 'password',
email : this.state.userEmail,
password : this.state.userPassword
}
})
.then((response) => {
console.log(response.data);
})
I've solved the problem in this way:
axios({
method: 'post',
url: 'http://183.172.100.84:8000/oauth/token',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
},
data: 'client_id=2&client_secret=secret&grant_type=password&email=' + this.state.userEmail+'&password='+this.state.userPassword
})
.then((response) => {
console.log(response.data);
})
Seems Axios have some problem to process the data in json format.
If you use Webpack you must also configure the proxy.

Parse.Cloud.httpRequest response gzip inflation / decompression

Is there something different in how Parse.Cloud.httpRequest is handling compression ?
On parse.com, I never had an issue with receiving a XML file, but using parse server on a different host (back4app), my httpResponse.text is a load of:
�E��ڇ�*q�������y���v^�����
Parse.Cloud.job("fetchData", function(request, status) {
Parse.Cloud.httpRequest({
method: 'GET',
url: 'http://example.com/test.xml',
headers: {
'Accept': 'application/xml',
'Accept-Encoding': 'gzip, deflate'
},
success: function (httpResponse) {
console.log("SUCCESS RECD FILE: " + httpResponse.text);
},
error: function (httpResponse) {
console.log('An error has occured with the http request.: ' + httpResponse);
}
});
}
Thanks for the great support at back4app, here is the solution
Basically, the option gzip:true is not documented anywhere but is needed.
I am not sure if the headers are needed, but I left them in anyway.
Parse.Cloud.httpRequest({
url: 'http://example.com/feed.xml',
headers: {
'Content-Type': 'application/xml',
'Accept-Encoding': 'gzip, deflate'
},
gzip:true,
success: function (httpResponse) {
...
},
error: function (httpResponse) {
...
}
}

Resources