I have 2 functions in the same google cloud functions project (myfunction1 and myfunction2.
exports.myfunction1 = async (req, res) => {
await axios({
method: 'post',
url: 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2',
timeout: 15000,
headers: {
'Content-Type': 'application/json',
},
data: myjson
}).then(response => {
console.log(JSON.stringify(response.data));
}).catch(err => {
console.error("catch error");
console.error(err);
})
}
It is works fine, but only if I configure invokers permission for allUsers. If I remove this permission, e receive 403 code error. Not sounds good keep this permisson activate, because the function is exposed. I tried solve with this link and this link, but, no sucess.
Edit1:
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const targetAudience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2'
const url = '??????????';
async function request() {
console.info('request ${url} with target audience ${targetAudience}');
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({url});
console.info(res.data);
}
I'm trying using this code, but, who is const url?
You must perform service to service authentication. You can find a great tutorial in the Cloud Run page (ok you use Cloud Functions but the underlying infrastructure is the same and the doc is better).
You also have to be aware about the Functions identity and how to change them (or to grant the current service account the correct permission)
let audience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2';
let token_request_url = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=' + audience;
var token_response = await axios.get(token_request_url, { headers: {'Metadata-Flavor': 'Google'} });
let token_auth = token_response.data;
axios({
method: 'post',
url: audience,
timeout: 15000,
headers: {
'Authorization': "Bearer " + token_auth
},
data: myJSON
}).catch(err => {
console.error(err);
});
Related
I am trying to rewrite the implementation of the Fastlane authentication in the App Store Connect Enterprise account using 2fa with Node.js . So far, I have managed to get and input a one-time password from the SMS and to get the session out of it. The relevant code is presented below:
Node.js with Typescript rewritten code
let authServiceKey = "";
try {
//sending a get request in order to get the authService (This works as intended)
const authService = await axios.get("https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com");
authServiceKey = authService.data\["authServiceKey"\];
//This request tries to sign in the and gets expected 409 error status, with some useful response data
await axios({method: 'post', url: 'https://idmsa.apple.com/appleauth/auth/signin', headers: {
// eslint-disable-next-line #typescript-eslint/naming-convention
'Content-Type': 'application/json',
// eslint-disable-next-line #typescript-eslint/naming-convention
'X-Requested-With':'XMLHttpRequest',
// eslint-disable-next-line #typescript-eslint/naming-convention
'X-Apple-Widget-Key': authServiceKey
},
data: {
accountName: process.env.APP_STORE_CONNECT_ENTERPRISE_ACCOUNT_NAME,
password: process.env.APP_STORE_CONNECT_ENTERPRISE_PASSWORD,
rememberMe: true
}
});
} catch (e:any) {
try{
const response = e["response"];
const headers = response["headers"];
const xAppleIdSessionId:string = headers["x-apple-id-session-id"];
const scnt:string = headers["scnt"];
const authService = await axios.get("https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com");
const authKey = authService.data["authServiceKey"];
const authenticationHeaders = {
// eslint-disable-next-line #typescript-eslint/naming-convention
'Content-Type': 'application/json',
// eslint-disable-next-line #typescript-eslint/naming-convention
'X-Apple-Id-Session-Id': xAppleIdSessionId,
// eslint-disable-next-line #typescript-eslint/naming-convention
'X-Apple-Widget-Key': authKey,
// eslint-disable-next-line #typescript-eslint/naming-convention
"Accept": "application/json",
"scnt": scnt
}
const authenticationResult = await axios({method: "get", url: "https://idmsa.apple.com/appleauth/auth", headers:authenticationHeaders });
const phoneId= authenticationResult.data["trustedPhoneNumbers"][0]["id"];
const pushMode = authenticationResult.data["trustedPhoneNumbers"][0]["pushMode"];
const body = {
phoneNumber: {
id: phoneId,
},
mode: pushMode
}
await axios({
method: 'put', url: 'https://idmsa.apple.com/appleauth/auth/verify/phone',
headers: authenticationHeaders,
data: body
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("Input your code, received by the device ",async function (code: string) {
await axios({
method: "post", url:`https://idmsa.apple.com/appleauth/auth/verify/phone/securitycode`,
headers: authenticationHeaders, data: {
securityCode: {
code: code
},
phoneNumber:{
id: phoneId
},
mode: pushMode
}}
);
const finalRes = await axios({
method: "get",
url: "https://idmsa.apple.com/appleauth/auth/2sv/trust",
headers: authenticationHeaders
});
const sessionToCookie = finalRes["headers"]["x-apple-id-session-id"];
rl.close();
});
rl.on("close", function() {
process.exit(0);
});
}
catch (e)
{
console.error(e)
process.exit(1);
}
}
The problem occurs later since I need to use the session to create a cookie, as it is shown in the Fastlane project:
Original Ruby code:
def store_cookie(path: nil)
path ||= persistent_cookie_path
FileUtils.mkdir_p(File.expand_path("..", path))
# really important to specify the session to true
# otherwise myacinfo and more won't be stored
#cookie.save(path, :yaml, session: true)
return File.read(path)
end
def persistent_cookie_path
if ENV\["SPACESHIP_COOKIE_PATH"\]
path = File.expand_path(File.join(ENV\["SPACESHIP_COOKIE_PATH"\], "spaceship", self.user, "cookie"))
else
\[File.join(self.fastlane_user_dir, "spaceship"), "\~/.spaceship", "/var/tmp/spaceship", "#{Dir.tmpdir}/spaceship"\].each do |dir|
dir_parts = File.split(dir)
if directory_accessible?(File.expand_path(dir_parts.first))
path = File.expand_path(File.join(dir, self.user, "cookie"))
break
end
end
end
return path
end
I have no idea what to do with the received session to get the cookie based on the ruby code. Can anybody help me? And then, when I have a session, how do I access the App Store Connect
Enterprise account using cookies and avoiding the 2fa authentication based on the session?
I tried to use the tough-cookie NPM package for Node.js to build the cookie. It was not possible since I did not have options to mention YAML or session parameters.
I am trying to build an app using zapier cli.
I am having a problem with authorizing the request .
I am using an api key and a client id.
When I try to use the same credentials in the UI
Its working perfect , however in the cli it gives an error code 403.
I have listed the code below . What could be the issue ?
//test
"use strict";
const should = require("should");
const zapier = require("zapier-platform-core");
const App = require("../index");
const appTester = zapier.createAppTester(App);
describe("custom authentication authentication", () => {
// Put your test TEST_USERNAME and TEST_PASSWORD in a .env file.
// The inject method will load them and make them available to use in your
// tests.
zapier.tools.env.inject();
it("should authenticate", (done) => {
const bundle = {
authData: {
api_key: process.env.API_KEY,
client_id: process.env.CLIENT_ID,
},
};
appTester(App.authentication, bundle)
.then((response) => {
should.exist(response);
done();
})
.catch(done);
});
});
//authentication.js
"use strict";
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = currentDate.getMonth() + 1;
const day = currentDate.getDate();
const authentication = (z, bundle) => {
const options = {
url: "url",
method: "GET",
headers: {
ContentType: "application/json",
Accept: "application/json",
"x-api-key": bundle.authData["api_key"],
client_id: bundle.authData["client_id"],
},
params: {
year: year,
month: month,
day: day,
page_size: "1000",
},
};
return z.request(options).then((response) => {
response.throwForStatus();
});
};
module.exports = authentication;
Do you have values in a .env file at the application root? You can also double check that console.log(process.env.API_KEY) prints a value when the test is running.
As an aside, you can use the zapier convert CLI command to copy your integration from the UI to the CLI without having to re-write it. If it works in the UI, it'll work in the CLI too.
I am using strapi as backend and react in the front-end. So the use case is that the user will signup and that signup will be done using auth0. I have defined some roles for the users signing up as shown on auth0
Roles based on plan taken by user
const _ = require("lodash");
const axios = require("axios");
const jwt = require("../jwt");
module.exports = async (ctx, next) => {
let role;
if (ctx.state.user) {
// request is already authenticated in a different way
return next();
}
try {
const tokenInfo = await axios({ method: "post",
url: `${process.env.AUTH0_URL}/userinfo`,
headers: { Authorization: ctx.request.header.authorization,
},
});
let user_id = tokenInfo.data.sub;
var config = { method: "get",
url: `${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles`,
headers: {Authorization: `Bearer ${jwt.jwtSecret}`,
},
};
axios(config).then(function (response) {
ctx.state.roles = response.data[0].name; // This part does not work in the next policy as ctx.state.role gives undefined in route specific policy
}).catch(function (error) {
console.log(error);
});
// console.log(tokenInfo.data, "tokeninfo");
if (tokenInfo && tokenInfo.data) {
return await next();
}
} catch (err) {
console.log(err.message);
return handleErrors(ctx, err, "unauthorized");
}
Currently these will be managed here only. Now I have a collection which has some research articles which can only be accessed depending upon the plan user has been assigned. In order to protect the route and strapi access I have installed user-permissions plugin in strapi and managing userinfo using a global policy as shown
Project Structure
. So here is the code through which I am checking the user info on every route
Now there are two ways in which I tried solving my problem. First I read the tokenInfo data from userInfo route but unfortunately auth0 is not returning roles assigned. It is only returning standard data like
"name": "ansh5#gmail.com",
"nickname": "ansh5",
"picture": "https://s.gravatar.com/avatar/6fdb83f10321dd7712ac2457b11ea34e?
s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fan.png",
"updated_at": "2021-07-19T08:03:50.461Z",
"user_id": "auth0|60ec0b3721224b0078ac95f4",
So in order to get user role I used the other API and configured it with my auth0 account.
${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles
I am getting the correct response but when I am doing this assignment.
ctx.state.roles = response.data[0].name;
I am getting undefined in my ctx.state.roles in my route specific policy. Does anybody have idea how we manage strapi and auth0 together.
Yes, it's because the axios calls are asynchronous in nature. So as per your code, axios will try to get the user information over a network call, but strapi will not really wait for the response. Instead it will just move forward to the next policy, hence resulting in an undefined user role. To fix this, you need to await for the api response from axios. Try the code below:
const axios = require("axios");
const jwt = require("../jwt");
module.exports = async (ctx, next) => {
let role;
if (ctx.state.user) {
// request is already authenticated in a different way
return next();
}
try {
const tokenInfo = await axios({
method: "post",
url: `${process.env.AUTH0_URL}/userinfo`,
headers: {
Authorization: ctx.request.header.authorization,
},
});
let user_id = tokenInfo.data.sub;
var config = {
method: "get",
url: `${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles`,
headers: {
Authorization: `Bearer ${jwt.jwtSecret}`,
},
};
const resp = await axios(config);
ctx.state.roles = response.data[0].name;
console.log(ctx.state.roles);
// console.log(tokenInfo.data, "tokeninfo");
if (tokenInfo && tokenInfo.data) {
return await next();
}
} catch (err) {
console.log(err.message);
return handleErrors(ctx, err, "unauthorized");
}
}
I would try the Power BI API.
So I start by getting Embed Token ( I'm using application owns data scenario ).
1.access Token
var options = {
'method': 'POST',
'url': `https://login.microsoftonline.com/${process.env.TENANT_ID}/oauth2/token`,
'headers': {
'Content-Type': 'multipart/form-data'
},
formData: {
'grant_type': process.env.GRANT_TYPE,
'client_id': process.env.CLIENT_ID,
'client_secret': process.env.CLIENT_SECRET,
'resource': process.env.RESSOURCE,
'Scope': process.env.SCOPE
}
};
// get Access token from AAD to retrieve Embed Token in PBI API
let response;
try {
response = await new Promise((resolve, reject) => {
request(options, (error, response, data) => {
if (error) reject(error)
else resolve(data)
})
})
}
catch (error) {
context.error(error)
}
2.Fetch embed Token (Docs)
var data = '{accessLevel:"View"}';
var config = {
method: 'post',
url: `https://api.powerbi.com/v1.0/myorg/groups/${process.env.GROUP_ID}/dashboards/${process.env.DASHBOARD_ID}/GenerateToken`,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${JSON.parse(response).access_token}`
},
data: data
};
const embedtoken = await axios(config)
context.res = {
// status: 200, /* Defaults to 200 */
body: embedtoken.data
};
3. I have delegated permissions on azure
I'm getting an embed token.
4. fetch dashboard infos
I'm using postman to fetch dashboard infos from the same group id and dashboard id that I mentioned in the api to get embed token
( I add that token in authorization section )
The problem is that I'm getting 403 Forbidden error.
PS: In this post some limitations of service principal method are mentioned . is it the source of my problem ? Am I obliged to use master user method ?
You have a misunderstanding of the usage of Embed Token. It cannot be used to call https://api.powerbi.com/v1.0/myorg/groups/{group id}/dashboards/{dashboard id}/ directly. AAD token is needed here.
To use an Embed Token, you should call the Embed URL with it.
The format of Embed URL is like:
https://app.powerbi.com/reportEmbed?reportId=f6bfd646-b718-44dc-a378-b73e6b528204&groupId=be8908da-da25-452e-b220-163f52476cdd&config=eyJjbHVzdGVyVXJsIjoiaHR0cHM6Ly9XQUJJLVVTLU5PUlRILUNFTlRSQUwtcmVkaXJlY3QuYW5hbHlzaXMud2luZG93cy5uZXQiLCJlbWJlZEZlYXR1cmVzIjp7Im1vZGVybkVtYmVkIjp0cnVlfX0%3d
An .net example:
// You need to provide the workspaceId where the dashboard resides.
ODataResponseListReport reports = await client.Reports.GetReportsInGroupAsync(workspaceId);
// Get the first report in the group.
Report report = reports.Value.FirstOrDefault();
// Generate Embed Token.
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "view");
EmbedToken tokenResponse = client.Reports.GenerateTokenInGroup(workspaceId, report.Id, generateTokenRequestParameters);
// Generate Embed Configuration.
var embedConfig = new EmbedConfig()
{
EmbedToken = tokenResponse,
EmbedUrl = report.EmbedUrl,
Id = report.Id
};
Then you can call the EmbedUrl with EmbedToken.
Reference here.
My goal is to consume an API which has already been deployed with nopCommerce (I do not have dev access to the server - I am just a user). There is a sample client application here, but the code is in C#. I have a webapp deployed on an Azure server using node.js. The API uses the OAuth 2.0 Authorization Code grant type.
I did a bit of Googling and it appears that client_credentials is typically used for this type of server to server flow:
How does 2-legged oauth work in OAuth 2.0?
Using OAuth for server-to-server authentication?
There is also an answer here which suggests that I can manually retrieve a token and then store it on the server. This is what I'm currently doing while testing my code.
I also found this answer which appears to be asking the same question. Like the author of that post, I am able to get a token via Postman, but my node.js code fails.
OAuth2.0 for nopCommerce
I wrote a minimal example in node.js here.
import { config } from 'dotenv';
import * as path from 'path';
import fetch from 'cross-fetch';
const ENV_FILE = path.join(__dirname, '.env');
const loadFromEnv = config({ path: ENV_FILE });
export async function getCodeUrl() {
const params = {
client_id: <CLIENT_ID>,
redirect_uri: 'http://example.com',
response_type: 'code',
};
console.log(params);
const url = new URL(`http://example.com/OAuth/Authorize`);
Object.keys(params).forEach(( key ) => url.searchParams.append(key, params[key]));
const res = await fetch(url.href, { method: 'GET' });
return res;
}
export async function getToken(code: string) {
const url = new URL(`http://example.com/api/token`);
const options = {
form: {
client_id: <CLIENT_ID>,
client_secret: <CLIENT_SECRET>,
code,
grant_type: 'authorization_code',
redirect_ui: 'http://example.com',
},
headers: { 'content-type': 'application/x-www-form-urlencoded' },
method: 'POST',
};
console.log(options);
const res = await fetch(url.href, options);
console.log('res', res);
return res;
}
const test = async () => {
const codeUrlString = (await getCodeUrl()).url;
const code = (new URL(codeUrlString).searchParams.get('code'));
if (code) {
console.log('code', code);
const tokenResponse = await getToken(code);
console.log('token res', tokenResponse);
}
};
test();
I am successfully able to retrieve the authorization code, but when I use that in a POST request to get a token, I get this error:
{ error: 'invalid_client' }