I am implementing Facebook login in my Vue (nuxt) app with NodeJS backend.
I am able to receive my auth code, but am unable to get the access token using that. It keeps returning me the following error:
data: {
error: {
message: 'Missing redirect_uri parameter.',
type: 'OAuthException',
code: 191,
fbtrace_id: 'AEzwwqCBEvvkq9mDQPrvc-7'
}
}
This is the code I have:
My page containing the facebook login button:
facebookLoginUrl () {
const stringifiedParams = queryString.stringify({
client_id: process.env.facebook_app_id,
redirect_uri: process.env.base_url + this.$findPath('/authenticatie/facebook'),
scope: ['public_profile', 'email', 'user_birthday', 'user_location', 'user_hometown'].join(','), // comma seperated string
response_type: 'code',
auth_type: 'rerequest',
display: 'popup',
})
return `https://www.facebook.com/v4.0/dialog/oauth?${stringifiedParams}`
}
The page where my redirect_uri is going to only contains the following in the created hook:
created () {
let facebookAuthCode = this.$route.query.code
let redirectUri = process.env.base_url + this.$findPath('/authenticatie/facebook')
if (facebookAuthCode) {
this.$getFromBackend('/getFacebookAccessTokenFromCode', { authCode: facebookAuthCode, redirectUri })
}
}
and finally my backend code does this:
axios.get('https://graph.facebook.com/v4.0/oauth/access_token', {
client_id: process.env.facebook_app_id,
client_secret: process.env.facebook_app_secret,
redirect_uri: encodeURIComponent(redirectUri),
code: encodeURIComponent(authCode),
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error.response)
})
And it throws me the error mentioned above.
I can see in the error.response.config object the following:
config: {
url: 'https://graph.facebook.com/v4.0/oauth/access_token',
method: 'get',
headers: {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.19.2'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
client_id: 'xxxxxxxxxxxxxx',
client_secret: 'xxxxxxxxxxxxxxxxxx',
redirect_uri: 'http://localhost:3000/nl-be/authenticatie/facebook',
code: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
data: undefined
},
So the redirect_uri is set, whats going on?
UPDATE: I changed the axios code according to #CBroe answer:
axios.get('https://graph.facebook.com/v4.0/oauth/access_token', qs.stringify({
client_id: process.env.facebook_app_id,
client_secret: process.env.facebook_app_secret,
redirect_uri: encodeURIComponent(redirectUri),
code: encodeURIComponent(authCode),
}), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error.response)
})
But i am getting the exact same error.
Related
I'm trying to implement Google Oauth to get information on users who sign in with Google such as their name and profile photo.
I found this react library which seems like it can get me a code when using the flow: 'auth_code' param.
Additionally I've found these Google docs which outline how to exchange a code for an ID token which will have user information.
I can successfully retrieve a code using the react component as shown in this slightly redacted log message from my browser:
{
"code": "4/0AWtgzh4VK3QT85egP0m9Rsau_gND-FPDxpM-cPB7ocxTfNkZSCzFSbE86<redacted suffix>",
"scope": "email profile openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
"authuser": "0",
"prompt": "consent"
}
I then send this code to my backend which fires off the following request to the token endpoint as found in the Discovery document, mentioned in the OpenID Google docs:
try {
const resp = await axios.post("https://oauth2.googleapis.com/token", {
code: googleJWT,
client_id: getGoogleClientId(),
client_secret: googleSecret,
redirect_uri: "http://localhost:3000",
grant_type: "authorization_code"
}, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
})
console.log("################## posted", resp)
} catch (e) {
console.log("################## could not post")
console.log(e)
}
However, this results in an error being thrown:
error: 'unsupported_grant_type',
error_description: 'Invalid grant_type: '
even though a grant type of authorization_code is reported as supported by the Discovery document.
For completeness, here's the redacted axios config that gets printed with the above error message:
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'axios/0.26.1',
'Content-Length': 298
},
method: 'post',
url: 'https://oauth2.googleapis.com/token',
data: '{"code":"4/0AWtgzh4VK3QT85egP0m9Rsau_gND-FPDxpM-cPB7ocxTfNkZSCzFSbE86<redacted>","client_id":"221274282648-q9nrm8j61s5cba218qot926vnloua2hb.apps.googleusercontent.com","client_secret":"<redacted>","redirect_uri":"http://localhost:3000","grant_type":"authorization_code"}'
},
Why is google complaining that the provided grant type isn't supported and how can I swap this code for an ID token?
I'm working on adapting some React code of mine to use #testing-library/react, react-query, and msw to mock network calls that I make using axios.
So far (after some brainbending) I've got it working with this code! (yay!)
The test in question (simplified):
import React from 'react';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { screen, waitFor, waitForElementToBeRemoved } from '#testing-library/react';
import { queryCache } from 'react-query';
import userEvent from '#testing-library/user-event';
import Login from '../Login';
import { render, queryClient } from '~/testhelpers/helpers.integration';
// #endregion imports
const server = setupServer(
// ...default setup for successful request; not relevant here
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('rendering', () => {
describe('error', () => {
it.only('should render error message when request fails', async () => {
server.use(
rest.post(`${API_BASE_URL}api/auth/token`, (req, res, ctx) => {
return res(ctx.status(400), ctx.json('Invalid username or password.'));
})
);
render(<Login />);
const usernameInput = screen.getByLabelText('Username');
const passwordInput = screen.getByLabelText('Password');
const submitInput = screen.getByRole('button', { name: 'Login' });
userEvent.type(usernameInput, 'testUsername');
userEvent.type(passwordInput, 'testPassword');
userEvent.click(submitInput);
await waitFor(() => {
expect(screen.getByText('Invalid username or password.')).toBeInTheDocument();
});
});
});
});
Again, this test works! Hooray! But the issue is that I am making axios think (via msw) that it is receiving a 400 response for this query, and I get this big chunk of error message in my console when I run this test with Jest:
PASS src/display/views/login/__tests__/Login.spec.js
rendering
error
√ should hide loading indicator and render error message when request fails (522 ms)
console.error
Error: Request failed with status code 400
at createError (C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\axios\lib\core\createError.js:16:15)
at settle (C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\axios\lib\core\settle.js:18:12)
at XMLHttpRequestOverride.handleLoad [as onreadystatechange] (C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\axios\lib\adapters\xhr.js:59:7)
at XMLHttpRequestOverride.triggerReadyStateChange (C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\node-request-interceptor\src\interceptors\XMLHttpRequest\XMLHttpRequestOverride.ts:133:33)
at XMLHttpRequestOverride.trigger (C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\node-request-interceptor\src\interceptors\XMLHttpRequest\XMLHttpRequestOverride.ts:145:12)
at C:\Users\me\Documents\_Programming\GitHub\my-project\node_modules\node-request-interceptor\src\interceptors\XMLHttpRequest\XMLHttpRequestOverride.ts:306:18
at runNextTicks (internal/process/task_queues.js:62:5)
at processTimers (internal/timers.js:489:9) {
config: {
adapter: [Function: xhrAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8'
},
method: 'post',
url: 'http://baseurl/api/auth/token',
data: '{"username":"testUsername","password":"testPassword"}'
},
request: XMLHttpRequestOverride {
requestHeaders: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8'
},
responseHeaders: { 'x-powered-by': 'msw', 'content-type': 'application/json' },
_events: [],
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4,
onreadystatechange: [Function: handleLoad],
onabort: null,
onerror: [Function: handleError],
onload: null,
onloadend: null,
onloadstart: null,
onprogress: null,
ontimeout: [Function: handleTimeout],
url: 'http://baseurl/api/auth/token',
method: 'POST',
readyState: 4,
withCredentials: false,
status: 400,
statusText: 'Bad Request',
data: '{"username":"testUsername","password":"testPassword"}',
response: '"Invalid username or password."',
responseType: 'text',
responseText: '"Invalid username or password."',
responseXML: null,
responseURL: '',
upload: null,
timeout: 0,
async: true,
user: undefined,
password: undefined
},
response: {
data: 'Invalid username or password.',
status: 400,
statusText: 'Bad Request',
headers: { 'x-powered-by': 'msw', 'content-type': 'application/json' },
config: {
adapter: [Function: xhrAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'post',
url: 'http://baseurl/api/auth/token',
data: '{"username":"testUsername","password":"testPassword"}'
},
request: XMLHttpRequestOverride {
requestHeaders: [Object],
responseHeaders: [Object],
_events: [],
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4,
onreadystatechange: [Function: handleLoad],
onabort: null,
onerror: [Function: handleError],
onload: null,
onloadend: null,
onloadstart: null,
onprogress: null,
ontimeout: [Function: handleTimeout],
url: 'http://baseurl/api/auth/token',
method: 'POST',
readyState: 4,
withCredentials: false,
status: 400,
statusText: 'Bad Request',
data: '{"username":"testUsername","password":"testPassword"}',
response: '"Invalid username or password."',
responseType: 'text',
responseText: '"Invalid username or password."',
responseXML: null,
responseURL: '',
upload: null,
timeout: 0,
async: true,
user: undefined,
password: undefined
}
}
}
at CustomConsole.console.error (node_modules/#testing-library/react/dist/act-compat.js:52:34)
at node_modules/react-query/lib/core/mutation.js:115:32
I think, to a certain extent, this is expected, but it's REALLY unnecessary spam in my tests given that I am expecting and in fact wanting that request to fail. Is there a best practice way to remove this unnecessary log via config for Jest, RTL, msw, or Axios?
react-query logs to the console per default, you can override this with setLogger:
import { setLogger } from 'react-query'
setLogger({
log: printLog,
warn: printWarn,
error: printError,
})
see also: https://react-query.tanstack.com/reference/setLogger
I am using azure functions to serve as a secure api between my front end application and MS Graph.
I have a B2C AD for which I am able to get a single user and list all users, using my azure function.
I get 401 unauthorised when I try to create a user.
My API app is registered in B2C with permissions Directory.Read.All, Directory.ReadWrite.All, Application.ReadWrite.All, AuditLog.Read.All and User.ReadWrite.All - all of them indicate they have admin consent.
Client secret is in order and is used as part of the token request.
I get the access token from the https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/token end point. This is how I obtain the token:
const APP_ID = 'XXXXXXX';
const APP_SECERET = 'XXXXXXXX';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/token';
const MS_GRAPH_SCOPE = 'https://graph.microsoft.com/.default';
const MS_GRAPH_USER = "https://graph.microsoft.com/v1.0/users"
const axios = require('axios');
const qs = require('qs');
const postData = {
client_id: APP_ID,
scope: MS_GRAPH_SCOPE,
client_secret: APP_SECERET,
grant_type: 'client_credentials'
};
let token = null
await axios
.post(TOKEN_ENDPOINT, qs.stringify(postData))
.then(async (response) => {
token = response.data.access_token}) etc etc etc...
Error response is simply a 401 Unauthorised. I can post the full message if required.
Where am I going wrong?
Thanks
Error response looks like this:
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
'cache-control': 'private',
'content-type': 'application/json',
'request-id': '93918a37-f0ed-44c9-85ad-d22157e88239',
'client-request-id': '93918a37-f0ed-44c9-85ad-d22157e88239',
'x-ms-ags-diagnostic': '{"ServerInfo":{"DataCenter":"UK South","Slice":"SliceC","Ring":"4","ScaleUnit":"002","RoleInstance":"AGSFE_IN_40"}}',
'www-authenticate': 'Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id="00000003-0000-0000-c000-000000000000"',
'strict-transport-security': 'max-age=31536000',
date: 'Tue, 15 Dec 2020 15:20:10 GMT',
connection: 'close',
'content-length': '302'
},
config: {
url: 'https://graph.microsoft.com/v1.0/users',
method: 'post',
data: 'accountEnabled=true&mailNickname=TT&passwordProfile%5BforceChangePasswordNextSignIn%5D=true&passwordProfile%5Bpassword%5D=XXX',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
'content-type': 'application/json',
Authorization: 'Bearer XXX (removed)'
},
request: ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [TLSSocket],
connection: [TLSSocket],
_header: 'POST /v1.0/users HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/x-www-form-urlencoded\r\n' +
'User-Agent: axios/0.21.0\r\n' +
'Content-Length: 127\r\n' +
'Host: graph.microsoft.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'POST',
insecureHTTPParser: undefined,
path: '/v1.0/users',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: { error: [Object] }
},
isAxiosError: true,
toJSON: [Function: toJSON]
}
EDIT:
Tried graph explorer and despite having consent for each of the permissions I get a 403 forbidden
I've asked my global admin to confirm but I think I have the application administrator role.
Also, as my registered app has granted admin permissions I thought this would have worked as it does not rely on my permissions...?
Thanks
Please use the below code for getting the user list and Create a new user
const request = require('request');
let tokentest='';
const endpoint = "https://login.microsoftonline.com/tenantid/oauth2/v2.0/token";
const requestParams = {
grant_type: "client_credentials",
client_id : "clientid",
client_secret : "secret",
scope: "https://graph.microsoft.com/.default"
};
user = {
accountEnabled: true,
displayName: "Adele Vance",
mailNickname: "AdeleV",
userPrincipalName: "AdeleghhhVgghhh#xxx",
"passwordProfile" : {
forceChangePasswordNextSignIn: true,
password: "xWwvJ]6NMw+bWH-d"
}
};
request.post({ url:endpoint, form: requestParams }, function (err, response, body) {
if (err) {
console.log("error");
}
else {
console.log("Body=" + body);
let parsedBody = JSON.parse(body);
if (parsedBody.error_description) {
console.log("Error=" + parsedBody.error_description);
}
else {
console.log("Access Token=" + parsedBody.access_token);
createGraphAPI(parsedBody.access_token);
testGraphAPI(parsedBody.access_token)
}
}
});
function testGraphAPI(accessToken) {
request.get({
url:"https://graph.microsoft.com/v1.0/users",
headers: {
"Authorization": "Bearer " + accessToken
}
}, function(err, response, body) {
console.log(body);
});
}
function createGraphAPI(accessToken) {
request.post({
url:"https://graph.microsoft.com/v1.0/users",
headers: {
"Authorization": "Bearer " + accessToken,
'content-type': 'application/json'
},json:user
}, function(err, response, body) {
console.log(body);
});
}
I've written my code some month ago, and now without changing something there is an error message and I don't know why.
My Code in a Google Cloud Function loads a picture from an AWS S3 bucket to google cloud and saves it in a bucket, which was actually working some month ago!
My code is running node.js 8:
const tmpdir = os.tmpdir();
const filePath = path.join(tmpdir, fileName);
console.log(filePath);
console.log(data);
await writeFile(filePath, data.Body);
const bucketName = "test";
console.log('test1');
let storage;
if(!storage){
storage = new Storage();
}
await storage.bucket(bucketName).upload(filePath, {
gzip: false,
metadata: {
cacheControl: "public, max-age=31536000"
}
});
console.log('test2');
console.log(`${filePath} uploaded to ${bucketName}.`);
"test1" is printed in the logs, but "test2" not.
The error I get:
function-138m28l2agh39{ Error: Could not refresh access token: Unsuccessful response status code. Request failed with status code 500
at Gaxios._request (/srv/node_modules/gaxios/build/src/gaxios.js:85:23)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
response:
{ config:
{ url: 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fiam%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control',
headers: [Object],
retryConfig: [Object],
params: [Object],
responseType: 'text',
timeout: 0,
paramsSerializer: [Function: paramsSerializer],
validateStatus: [Function: validateStatus],
method: 'GET' },
data: 'Could not fetch URI /computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fiam%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control\n',
headers:
{ connection: 'close',
'content-length': '260',
'content-type': 'text/plain; charset=utf-8',
date: 'Thu, 16 Jul 2020 14:25:37 GMT',
'x-content-type-options': 'nosniff' },
status: 500,
statusText: 'Internal Server Error',
request:
{ responseURL: 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fiam%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control' } },
config:
{ url: 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fiam%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%2Chttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control',
headers: { 'Metadata-Flavor': 'Google' },
retryConfig:
{ noResponseRetries: 3,
currentRetryAttempt: 3,
retry: 3,
httpMethodsToRetry: [Array],
statusCodesToRetry: [Array] },
params:
{ scopes: 'https://www.googleapis.com/auth/iam,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/devstorage.full_control' },
responseType: 'text',
timeout: 0,
paramsSerializer: [Function: paramsSerializer],
validateStatus: [Function: validateStatus],
method: 'GET' },
code: '500' }
Did somebody face a similar issue or can somebody help? :)
Im trying to make a POST request to a FacePlusPlus API.
At the moment I'm just running app.js with node, but I keep getting an error .
My axios request inside express app:
axios({
method: "post",
url: "https://api-us.faceplusplus.com/facepp/v3/face/analyze",
headers: {
'Content-Type': "application/json",
},
data: {
'api_key': '<My api key goes here>',
'api_secret': '<My api secret goes here>',
'image_url': 'https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?w=1260&h=750&auto=compress&cs=tinysrgb'
}
})
.then(res => console.log(res))
.catch(err => console.log(err))
The Response error:
{ Error: Request failed with status code 400
at createError (/Users/Matt/webdev/test/node_modules/axios/lib/core/createError.js:16:15)
at settle (/Users/Matt/webdev/test/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/Users/Matt/webdev/test/node_modules/axios/lib/adapters/http.js:192:11)
at emitNone (events.js:110:20)
at IncomingMessage.emit (events.js:207:7)
at endReadableNT (_stream_readable.js:1047:12)
at _combinedTickCallback (internal/process/next_tick.js:102:11)
at process._tickCallback (internal/process/next_tick.js:161:9)
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'User-Agent': 'axios/0.17.1',
'Content-Length': 212 },
method: 'post',
url: 'https://api-us.faceplusplus.com/facepp/v3/face/analyze',
data: '{"api_key":"*** i removed this for question ***","api_secret":"*** i removed this for question ***","image_url":"https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?w=1260&h=750&auto=compress&cs=tinysrgb"}' }
...
...
data: { error_message: 'MISSING_ARGUMENTS: api_key' } } }
The data is obviously getting, sent, but why does it say missing argument?
Try console logging the api-key through the response and see what you get, since it is telling you it is a missing argument. Your data is a string and not an object should it be like that, just out of curiosity?