i've been trying to get some data from the non pro CB via their API.
I've been able to get data from coinbase pro, with the code below, adding the passphrase, but no dice with CB...
I keep getting invalid signature :(
Any idea what i could be missing?
const signedMessages = async (timestamp, meth, requestPath) => {
const secret = process.env.cb_all_read_secret;
const method = meth.toUpperCase();
const body = '';
const message = timestamp + method + requestPath + body;
const key = Buffer.from(secret, 'base64');
const hmac = crypto.createHmac('sha256', key);
const cb_access_sign = hmac.update(message).digest('base64');
return cb_access_sign;
};
const listAccounts = async () => {
let timestamp = Math.floor(Date.now() / 1000);
const signed = await signedMessages(timestamp, 'GET', '/v2/accounts');
const url = 'https://api.coinbase.com/v2/accounts';
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'CB-ACCESS-KEY': process.env.cb_all_read_key,
'CB-ACCESS-SIGN': signed,
'CB-ACCESS-TIMESTAMP': timestamp,
},
};
console.log(options);
try {
const data = await fetch(url, options);
const resultat = await data.json();
console.log(resultat);
} catch (error) {
console.log('error: ', error.message);
}
};
listAccounts();
First thing I'd check is that your computer's clock is up to date i.e. not > 30 seconds behind the API server.
coinbase documentation is frustrating at best.
In one place I saw the method you're using being documented.
var what = timestamp + method + requestPath + body;
// decode the base64 secret
var key = Buffer(secret, 'base64');
// create a sha256 hmac with the secret
var hmac = crypto.createHmac('sha256', key);
// sign the require message with the hmac
// and finally base64 encode the result
return hmac.update(what).digest('base64');
When I went to find it again I found the following: documentation which does the signing a little differently:
var signature = crypto.createHmac("sha256", apiSecret).update(message).digest("hex");
Notice the lack of a base64 buffer and the digest is Hex.
I modified your code and was able to get the wallets with view permissions. I would probably create a sign function using the code in the link provided including the body for any options you need to include.
const signedMessages = async (timestamp, method, path) => {
const apiSecret = '...';
const bodyStr = '';
let message = timestamp + method.toUpperCase() + '/v2/' + path + bodyStr;
return crypto.createHmac('sha256', apiSecret).update(message).digest('hex');
};
const listAccounts = async () => {
let timestamp = Math.floor(Date.now() / 1000);
const signed = await signedMessages(timestamp, 'GET', 'accounts');
const url = 'https://api.coinbase.com/v2/accounts/';
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'CB-ACCESS-KEY': '...',
'CB-ACCESS-SIGN': signed,
'CB-ACCESS-TIMESTAMP': timestamp,
},
};
console.log(options);
try {
const data = await fetch(url, options);
const resultat = await data.json();
console.log(resultat);
} catch (error) {
console.log('error: ', error.message);
}
};
listAccounts();
but guess what...
the "deprecated" node api does it the same way (that's where I found this signing method before I found the documentation) and it was last updated 4 years ago. Go figure.
Related
I have been trying to use the Spotify API in my expo app but every tutorial or wrapper I find doesn't seem to work.
I would specifically like to access the 30-second song previews and track/song searching features.
If anyone could provide some guidance or point me towards a working demo of any kind that would be awesome.
Thanks!
Found parts of the solution in https://docs.expo.dev/guides/authentication/#spotify
const discovery = {
authorizationEndpoint: 'https://accounts.spotify.com/authorize',
tokenEndpoint: 'https://accounts.spotify.com/api/token',
};
var client_id = ''; // Your client id
var client_secret = ''; // Your secret
export default function spotifyLogin(props) {
const [request, response, promptAsync] = useAuthRequest(
{
clientId: '',
scopes: ['user-read-email', 'user-read-playback-state', 'playlist-modify-public','playlist-modify-private','playlist-modify-public','playlist-read-private','user-read-recently-played'],
// In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
// this must be set to false
usePKCE: false,
redirectUri: makeRedirectUri({
//scheme: 'your.app'
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
const { code } = response.params;
//save code to local storage
props.saveLogin(code)
}
}, [response]);
return (
<Button
disabled={!request}
title="Login"
onPress={() => {
promptAsync();
}}
/>
);
}
export const getFirstTokenData = async (code) => {
var dataToSend = {
code: code,
redirect_uri: makeRedirectUri(),
grant_type: 'authorization_code'};
//making data to send on server
var formBody = [];
for (var key in dataToSend) {
var encodedKey = encodeURIComponent(key);
var encodedValue = encodeURIComponent(dataToSend[key]);
formBody.push(encodedKey + '=' + encodedValue);
}
formBody = formBody.join('&');
//POST request
var response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST', //Request Type
body: formBody, //post body
headers: {
//Header Defination
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')),
},
})
try{
return await response.json()
}catch (error){
console.log(error)
}
}
export const getRefreshTokenData = async (refreshToken) => {
console.log(refreshToken)
console.log(refreshToken + " going in for refresh")
var dataToSend = {
refresh_token : refreshToken,
grant_type: 'refresh_token'};
//making data to send on server
var formBody = [];
for (var key in dataToSend) {
var encodedKey = encodeURIComponent(key);
var encodedValue = encodeURIComponent(dataToSend[key]);
formBody.push(encodedKey + '=' + encodedValue);
}
formBody = formBody.join('&');
//POST request
var response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST', //Request Type
body: formBody, //post body
headers: {
//Header Defination
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')),
},
})
try{
return await response.json()
}catch (error){
console.log(error)
}
}
The above takes care of auth and getting refresh tokens, below takes care of searching for a track. To get 30 second previews there is a preview property in the return data for getTrack()
const apiPrefix = 'https://api.spotify.com/v1';
export default async ({
offset,
limit,
q,
token,
}) => {
const uri = `${apiPrefix}/search?type=track&limit=${limit}&offset=${offset}&q=${encodeURIComponent(q)}`;
console.log('search begin, uri =', uri, 'token =', token);
const res = await fetch(uri, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
}
});
const json = await res.json();
//console.log('search got json', json);
if (!res.ok) {
return [];
}
return json
// const {
// tracks: {
// items,
// }
// } = json;
// // const items = json.tracks.items;
// return items.map(item => ({
// id: item.id,
// title: item.name,
// imageUri: item.album.images
// ? item.album.images[0].url
// : undefined
// }));
console.log('search end');
};
export const getTrack = async(trackID, token) => {
const uri = `${apiPrefix}/tracks/${trackID}?market=ES`;
const res = await fetch(uri, {
method: 'GET',
headers: {
// Accept: `application/json`,
// Content-Type: `application/json`,
Authorization: `Bearer ${token}`,
}
});
const json = await res.json();
//console.log('search got json', json);
if (!res.ok) {
return [];
}
return json
}
Once upon a time, I worked on a similar application as a test. It's a bit outdated, but I believe Spotify has not changed its API much in the meantime.
Hope this caa help
https://github.com/kubanac95/spotify-test
I get a blob and I treat it like this:
const file = response.data;
var blob = new Blob([file], {
type: 'application/pdf',
});
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(blob);
fileReaderInstance.onload = async () => {
const fileUri = `${FileSystem.documentDirectory}file.pdf`;
await FileSystem.writeAsStringAsync(
fileUri,
fileReaderInstance.result.split(',')[1],
{
encoding: FileSystem.EncodingType.Base64,
}
);
console.log(fileUri);
Sharing.shareAsync(fileUri);
};
however when I generate and share the file, I can't access it and if I get its URI and search on the web it returns:
i solved my problem in this way:
This is a func who get other data to request, do the request (generate PDF()) and treat the data and generate by received blob the buffer on (fileReaderInstance.result) who is shared in Sharing.shareAsync()
const generatePDF = async () => {
setAnimating(true);
const companyReponse = await CompanyService.getCompany();
const peopleResponse = await PeopleService.getPerson(sale.customerId);
const company = companyReponse.response.company;
const people = peopleResponse.response;
const quote = false;
const json = await SaleService.generatePDF({
sale,
company,
people,
quote,
});
if (json && json.success) {
try {
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(json.data);
fileReaderInstance.onload = async () => {
const base64data = fileReaderInstance.result.split(',');
const pdfBuffer = base64data[1];
const path = `${FileSystem.documentDirectory}/${sale._id}.pdf`;
await FileSystem.writeAsStringAsync(`${path}`, pdfBuffer, {
encoding: FileSystem.EncodingType.Base64,
});
await Sharing.shareAsync(path, { mimeType: 'application/pdf' });
};
} catch (error) {
Alert.alert('Erro ao gerar o PDF', error.message);
}
}
setAnimating(false);
}
This is the func in SaleServicegeneratePDF who do the request to api and pass the parameters that return a blob of pdf using axios:
generatePDF: async ({ sale, company, people, quote }) => {
const token = await AsyncStorage.getItem('token');
const body = { sale, company, people, quote };
try {
const response = await axios(`${BASE_API}/generate-sale-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: token,
},
responseType: 'blob',
data: body,
});
return {
success: true,
data: response.data,
};
} catch (err) {
return err.error;
}
},
I have solved this problem by passing the blob string to WriteAsStringAsync method of FileSystem library from expo.
const blobDat = data.data[0].data; //blob data coming from an API call
const fileUri = FileSystem.documentDirectory + `testt.pdf`; //Directory Link of the file to be saved
await FileSystem.writeAsStringAsync(fileUri, blobDat, {
encoding: FileSystem.EncodingType.UTF8,
}) //This step writes the blob string to the pdf fileURI
await IntentLauncher.startActivityAsync("android.intent.action.VIEW", {
data: fileUri,
flags: 1,
type: "application/pdf",
});
//prompts user with available application to open the above created pdf.
I'm trying to verify a HMAC signature received from a WebHook. The details of the WebHook are https://cloudconvert.com/api/v2/webhooks#webhooks-events
This says that the HMAC is generated using hash_hmac (PHP) and is a SHA256 hash of the body - which is JSON. An example received is:
c4faebbfb4e81db293801604d0565cf9701d9e896cae588d73ddfef3671e97d7
This looks like lowercase hexits.
I'm trying to use Cloudflare Workers to process the request, however I can't verify the hash. My code is below:
const encoder = new TextEncoder()
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const contentType = request.headers.get('content-type') || ''
const signature = request.headers.get('CloudConvert-Signature')
let data
await S.put('HEADER', signature)
if (contentType.includes('application/json')) {
data = await request.json()
await S.put('EVENT', data.event)
await S.put('TAG', data.job.tag)
await S.put('JSON', JSON.stringify(data))
}
const key2 = await crypto.subtle.importKey(
'raw',
encoder.encode(CCSigningKey2),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signed2 = await crypto.subtle.sign(
'HMAC',
key2,
encoder.encode(JSON.stringify(data))
)
await S.put('V22', btoa(String.fromCharCode(...new Uint8Array(signed2))))
return new Response(null, {
status: 204,
headers: {
'Cache-Control': 'no-cache'
}
})
}
This will generate a hash of:
e52613e6ecebdf98bb085f04ca1f91bf9a5cf1dc085f89dcaa3e5fbf5ebf1b06
I've tried use the crypto.subtle.verify method, but that didn't work.
Can anyone see any issues with the code? Or have done this successfully using Cloudflare Workers?
Mark
I finally got this working using the verify method (I had previously tried the verify method, but it didn't work). The main problem seems to the use of request.json() wrapped in JSON.stringify. Changing this to request.text() resolved the issue. I can then use JSON.parse to access the data after verifying the signature. The code is as follows:
const encoder = new TextEncoder()
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const signature = request.headers.get('CloudConvert-Signature')
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(CCSigningKey2),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
)
const data = await request.text()
const verified = await crypto.subtle.verify(
'HMAC',
key,
hexStringToArrayBuffer(signature),
encoder.encode(data)
)
if (!verified) {
return new Response('Verification failed', {
status: 401,
headers: {
'Cache-Control': 'no-cache'
}
})
}
return new Response(null, {
status: 204,
headers: {
'Cache-Control': 'no-cache'
}
})
}
function hexStringToArrayBuffer(hexString) {
hexString = hexString.replace(/^0x/, '')
if (hexString.length % 2 != 0) {
return
}
if (hexString.match(/[G-Z\s]/i)) {
return
}
return new Uint8Array(
hexString.match(/[\dA-F]{2}/gi).map(function(s) {
return parseInt(s, 16)
})
).buffer
}
I am really battling to get API Key authentication working with Coinbase Pro using the Crypto-JS library in React Native.
I have followed the instructions here:
https://docs.pro.coinbase.com/#authentication
import axios from 'axios';
import { Buffer } from 'buffer';
import CryptoJS from 'crypto-js';
export const baseURL = 'https://api.pro.coinbase.com';
export const axiosTimeout = 10 * 1000; // 10 sec
export const addHeaders = (additional) => {
const headers = {};
return Object.assign(
headers,
{
Accept: 'application/json',
'User-Agent': 'rn-coinbasepro-app',
'Content-Type': 'application/json'
},
additional
);
};
componentDidMount() {
const auth = {
key: '',
secret: '',
passphrase: ''
};
const method = 'GET';
const url = `${baseURL}/orders?status=all`;
const timestamp = Date.now() / 1000;
const requestPath = '/orders?status=all'
const what = timestamp + method + requestPath;
const key = Buffer.from(auth.secret, 'base64').toString('ascii');
const hmac = CryptoJS.HmacSHA256(what, key);
const signature = CryptoJS.enc.Base64.stringify(hmac);
axios.interceptors.request.use((request) => {
console.log('Starting Request', request);
return request;
});
axios.interceptors.response.use((response) => {
console.log('Response:', response);
return response;
});
axios
.get(url, {
timeout: axiosTimeout,
headers: addHeaders({
method,
timeout: axiosTimeout,
'CB-ACCESS-KEY': auth.key,
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-PASSPHRASE': auth.passphrase
}),
debug: true
})
.then((response) => {
console.log(response);
if (response.status === 200) {
console.log(response.data);
} else {
console.log(response);
}
})
.catch((error) => {
console.log(error);
});
}
It keeps returning 401 (Unauthorized – Invalid API Key)
I have followed the instructions and I think I've done it all correctly. I just don't see what is wrong and the Coinbase Pro API returns very little to explain why.
After clicking an image through android mobile and extracting base64 from the image, I am sending it to the OCR space API to extract the text in the image.
getTextByURL is subjected to call the API after receiving the base64.
The format should be data:image/png;base64,iVBORw0KGgoAAAANS....
To obtain the format I am appending the base64 code to 'data:image/png;base64,' which should satisfy the required POST call conditions.
But the error still persists, I could not find whether the error is in the format or in the base64 code.
When I give a base64 code of an image in the POST call instead of base64 generated from the clicked image, the API call is working.
So no idea where is the glitch.
please help me!
getTextByUrl(base) {
var url = "https://api.ocr.space/parse/image";
var subscriptionKey = "xxxxxxxxxxxxxx";
var data = new FormData();
data.append("apikey", subscriptionKey);
data.append("language", "eng");
data.append("isOverlayRequired", "true");
data.append("Base64Image", base)
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
alert(this.responseText);
}
});
xhr.open("POST", "URL");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.send(data);
}
showCamera() {
this.setState({
showComponent: true,
uri: ""
})
}
The following function is to click the image and extract the base64 from the image.
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({ metadata: options })
.then((data) => {
let pathToImage = data.path;
CompressImage.createCompressedImage(pathToImage, 'compress/images').then(
ImgToBase64.getBase64String(pathToImage)
.then(base64String => {
**base64String = "data:image/jpg;base64," + base64String;**//This gives the base64 of the image
alert(base64String.substring(0, 50) + typeof (base64String));
this.setState({
baa: base64String.substring(0, 200)
})
this.getTextByUrl(base64String)
})
.catch(err => alert(err))
)
})
.catch(err => console.error(err));
}
Thanks in advance.
May be replace "Base64Image" with "base64Image". This works for me
const data = await this.camera.takePictureAsync(options);
const base64Str = 'data:image/jpg;base64,'+data.base64; var data = new FormData();
data.append('apikey', key)
data.append('base64Image', base64)
const headers = {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data;',
}
const config = {
method: 'POST',
headers,
body: data
};
const URL = 'https://api.ocr.space/parse/image';
fetch(URL, config )