Creating Wallet Address with coinbase API Using Axios Request - node.js

Hello I'm trying to use the coinase api using axios to make request. I have set up the neccessary api authentication using SHA256 HMAC. I have been able to make some GET Request and got response. I have been trying to make a POST Request but i have been getting 401 status code.
const name = "Test BTC Address";
const body = {
name: name
}
var encHash = {
baseUrl: 'https://api.coinbase.com',
method: 'POST',
path: '/v2/accounts/bbc2e3f7-a851-50ab-b4b3-a0f2a700846f/addresses',
body: body,
scopes: "wallet:addresses:create"
};
const sign = generateHashKey(encHash, key.APISECRET);
console.log(sign);
const config = {
headers: {
'CB-ACCESS-SIGN': sign.signature,
'CB-ACCESS-TIMESTAMP': sign.timestamp,
'CB-ACCESS-KEY': key.APIKEY,
'CB-VERSION': '2021-10-15'
}
}
const url = `${encHash.baseUrl}${encHash.path}`
console.log(url);
var options = await axios.post(url, body, config);
return res.send({data: options.data})
} catch (error) {
// console.error(error);
return res.send({error})
} ```

Related

Question about the Fastlane tool regarding authentication in App Store Connect Enterprise account using 2fa

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.

PUT request works in Postman but not a deno typescript file as a fetch request

I can do a put request just fine in Postman.
But when I try to do the same put request in a deno fresh app through a fetch command like this:
async function sendSignature() {
const signer = provider.getSigner();
const nonce = "asdf123492fd";
const signatureSigned = await signer.signMessage(nonce);
const headers = new Headers({
'Content-Type': 'application/json'
});
const opts = {
method: 'PUT',
headers: headers,
body: JSON.stringify({
key: props.singleUser,
wallet_address: walletAddrs,
signature: signatureSigned
})
}
console.log(opts.body);
const rawPosts = await fetch('http://localhost:4000/users/kythis1', opts);
console.log(rawPosts);
}
Btw all of the values are being populated in body. I can confirm that key, wallet_address, and signature are not null and are strings. It fails though... Here's what the browser's console looks like.
This is the entry point for the backend oak (deno's version of express) server.
import { Application } from "https://deno.land/x/oak/mod.ts";
import { APP_HOST, APP_PORT } from "./config.js";
import router from "./routes.js";
import _404 from "./controllers/404.js";
import errorHandler from "./controllers/errorHandler.js";
import { oakCors } from "https://deno.land/x/cors/mod.ts";
const app = new Application();
app.use(oakCors()); // Enable CORS for All Routes
app.use(errorHandler);
app.use(router.routes());
app.use(router.allowedMethods());
app.use(_404);
console.log(`Listening on port:${APP_PORT}...`);
await app.listen(`${APP_HOST}:${APP_PORT}`);
This is the function that is getting called by the put request:
import User from "../db/database.js";
export default async ({ request, response }) => {
if (!request.hasBody) {
response.status = 400;
response.body = { msg: "Invalid user data" };
return;
}
const body = request.body();
const {
key, wallet_address, signature
} = JSON.parse(await body.value);
console.log(signature);
if (!key) {
response.status = 422;
response.body = { msg: "Incorrect user data. Email and key are required" };
return;
}
const foundUser = await User.where('key', '=', key).first();
if (!foundUser) {
response.status = 404;
response.body = { msg: `User with key ${key} not found` };
return;
}
foundUser.wallet_address = wallet_address;
foundUser.updated_at = new Date();
const updatedResp = await foundUser.update();
response.body = { msg: "User updated", updatedResp };
};
Finally this is the backend routes:
import { Router } from "https://deno.land/x/oak/mod.ts";
import getUsers from "./controllers/getUsers.js";
import getUserDetails from "./controllers/getUserDetails.js";
import createUser from "./controllers/createUser.js";
import updateUser from "./controllers/updateUser.js";
//import deleteUser from "./controllers/deleteUser.js";
const router = new Router();
router
.get("/users", getUsers)
.get("/users/:key", getUserDetails)
.post("/users", createUser)
.put("/users/:key", updateUser);
//.delete("/users/:id", deleteUser);
export default router;
So why can I successfully call this function with a Postman put request, but I can't do a successful put request with fetch through a typescript file?
Your postman and fetch call aren't exactly the same.
Looking at postman it has 8 headers, and content-type seems to be set on Text.
While the fetch() is set on application/json.
When content type is application/json your request.body() is likely already parsed. Thus another JSON.parse will throw an error.
One easy fix with your current code would be to set your javascript headers to this:
const headers = new Headers({
'Content-Type': 'text/plain'
});
This will avoid 2 times parsing of the json file.
But the better fix would be to actually use application/json and see/log what request.body() returns.
Postman simply doesn’t care about CORS headers. So CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources.
You have to just specifies CORS (Access-Control-Allow-Origin) headers.

Authenticating FTX API SHA256 HMAC with Node

I am lost with using HMAC SHA256 for api authentication. This is my first time using it and I'm not sure what I am missing although I suspect it has to do with the timestamp. Can someone please help me identify what it is I am missing?
Everytime I try and make an API call I get a response stating
data: { success: false, error: 'Not logged in: Invalid signature' }
Here are the requirements for making the API call including the HMAC SHA256.
Here is the code I am using currently:
const axios = require('axios');
var forge = require('node-forge');
require('dotenv').config()
// get timestamp
var time = new Date().getTime();
// generate and return hash
function generateHash(plainText,secretKey)
{
var hmac = forge.hmac.create();
hmac.start('sha256', secretKey);
hmac.update(plainText);
var hashText = hmac.digest().toHex();
return hashText
}
// set axios config
var config = {
url:"https://ftx.us/api/wallet/all_balances",
method:"GET",
headers :{
"FTXUS-KEY":process.env.FTX_API_KEY,
"FTXUS-TS":time,
"FTXUS-SIGN":generateHash(`${new Date()}${"GET"}${"/wallet/all_balances"}`,process.env.FTX_API_SECRET)
}
}
axios(config)
.then(response => {
console.log(response.data)
}).catch(function (error) {
console.log(error);
})
I had to go through the same issue, so here goes my code.
import * as crypto from "crypto";
import fetch from "node-fetch";
// a function to call FTX (US)
async function callFtxAPIAsync(secrets, method, requestPath, body) {
const timestamp = Date.now();
const signaturePayload = timestamp + method.toUpperCase() + "/api" + requestPath + (method.toUpperCase() == "POST" ? JSON.stringify(body) : "");
const signature = crypto.createHmac('sha256', secrets.secret)
.update(signaturePayload)
.digest('hex');
const response = await fetch("https://ftx.us/api" + requestPath, {
method: method,
body: body != null ? JSON.stringify(body) : "",
headers: {
'FTXUS-KEY': secrets.key,
'FTXUS-TS': timestamp.toString(),
'FTXUS-SIGN': signature,
"Content-Type": "application/json",
"Accepts": "application/json"
}
});
return await response.json();
}
then call a post endpoint as for example:
let resultQuote = await callFtxAPIAsync(secrets, "post", "/otc/quotes",
{
"fromCoin": "USD",
"toCoin": "ETH",
"size": usd
});
or a get one:
let resultQuote = await callFtxAPIAsync(secrets, "get", "/otc/quotes/1234");
I hope it helps 😄
You need to add the full URL path, except the domain, in your case /api is missing. Try this:
"FTXUS-SIGN":generateHash(`${new Date()}${"GET"}${"/api/wallet/all_balances"}`,process.env.FTX_API_SECRET)

Nodejs - Axios not using Cookie for post request

I'm struggling with AXIOS: it seems that my post request is not using my Cookie.
First of all, I'm creating an Axios Instance as following:
const api = axios.create({
baseURL: 'http://mylocalserver:myport/api/',
header: {
'Content-type' : 'application/json',
},
withCredentials: true,
responseType: 'json'
});
The API I'm trying to interact with is requiring a password, thus I'm defining a variable containing my password:
const password = 'mybeautifulpassword';
First, I need to post a request to create a session, and get the cookie:
const createSession = async() => {
const response = await api.post('session', { password: password});
return response.headers['set-cookie'];
}
Now, by using the returned cookie (stored in cookieAuth variable), I can interact with the API.
I know there is an endpoint allowing me to retrieve informations:
const readInfo = async(cookieAuth) => {
return await api.get('endpoint/a', {
headers: {
Cookie: cookieAuth,
}
})
}
This is working properly.
It's another story when I want to launch a post request.
const createInfo = async(cookieAuth, infoName) => {
try {
const data = JSON.stringify({
name: infoName
})
return await api.post('endpoint/a', {
headers: {
Cookie: cookieAuth,
},
data: data,
})
} catch (error) {
console.log(error);
}
};
When I launch the createInfo method, I got a 401 status (Unauthorized). It looks like Axios is not using my cookieAuth for the post request...
If I'm using Postman to make the same request, it works...
What am I doing wrong in this code? Thanks a lot for your help
I finally found my mistake.
As written in the Axios Doc ( https://axios-http.com/docs/instance )
The specified config will be merged with the instance config.
after creating the instance, I must follow the following structure to perform a post requests:
axios#post(url[, data[, config]])
My requests is working now :
await api.post('endpoint/a', {data: data}, {
headers: {
'Cookie': cookiesAuth
}
});

AWS-Lambda 302 Not Redirecting after getting response from Axios (Frontend)

I'm trying to setup a Google-OAuth flow using serverless and AWS-Lambdas. To start, I have a button that kicks off the process by hitting a lambda endpoint. However, the page never actually redirects to the authentication page. Instead I get an error on the FE:
Request failed with status code 302
Frontend logic:
const redirectToGoogleOAuth = async (user) => {
try {
const endpoint = process.env.GOOGLE_PATH_ENDPOINT;
const response = await axios.get(endpoint, {
responseType: 'text',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${user}`,
},
});
// Expect redirect at this point
return response.data.data;
} catch (err) {
throw new Error(err.message);
}
};
Lambda Endpoint:
module.exports = async (event, context) => {
const responseType = 'code'
const googleAuthorizeURL = 'https://accounts.google.com/o/oauth2/v2/auth'
const scope = 'openid email https://www.googleapis.com/auth/contacts.readonly'
const accessType = 'offline'
try {
const params = [
`response_type=${responseType}`,
`client_id=${googleClientId}`,
`redirect_uri=${baseURL}`,
`scope=${scope}`,
`state="state"`,
`access_type=${accessType}`
]
const googleOAuthEndPath = `${googleAuthorizeURL}?${params.join('&')}`
const response = {
statusCode: 302,
body: '',
headers: {
location: googleOAuthEndPath
}
}
return response
} catch (err) {
return response(400, err.message)
}
}
In the lambda-response, I've added a header for location with the google-path. However, the frontend does not seem to consume the response correctly. The frontend interprets the 302 as in error instead of redirecting to the specific page. Any ideas on how I may resolve this so it actually redirects?
Axios uses XHR, which always follows redirects by itself and therefore Axios can't do anything about it (unless you rely on hacks, discussed in the same link).
You might have to use something other than Axios for this part, such as the Fetch API, which supports manual redirects.
GitHub user parties suggested the fetch() equivalent in the same Axios issue linked above:
fetch("/api/user", {
redirect: "manual"
}).then((res) => {
if (res.type === "opaqueredirect") {
window.location.href = res.url;
} else {
return res;
}
}).catch(handleFetchError);

Resources