Access-Control-Allow-Origin responses in Standard OAuth2.0 App
AnsweredI'm having a persistent problem (400 Bad Request) using the simple file upload API from an AngularJS app running on localhost.
The Box side is configured to use Standard OAuth2.0 User Authentication, I get my bearer token (via another server configured at the Redirect URI) and it's working for other APIs like folder listing and creation. https://localhost:3000 is listed in the app's CORS domains Allowed Origins (folder listing and creation didn't work until this was done). The problem presents in every browser I have tried, Firefox seems to give the most helpful debug information in its Web Developer - Tools - Network - Headers tab. In there, I can see my folder listing operations doing preflight OPTIONS methods on their first pass, they go straight to GET methods on later passes. If the folder I'm looking for isn't present, I do a POST to create it. That all works...
I'm slowly uncovering more and more debug info. The failed transaction now goes like this:
Request headers:
Host: upload.box.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Authorization: Bearer EE3TEstgGYjUOchHVEPfJlyxI95jBLgl
Content-Type: multipart/form-data; boundary=---------------------------277529705
Content-Length: 352
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/maint?logs=true&auth=&user=&text=true&latest=true
No Cookies
Request Payload:
-----------------------------277529705
Content-Disposition: form-data; name="attributes"
{'name':'testFileName','parent':{'id':'0'}}
-----------------------------277529705
Content-Disposition: form-data; name="file"
mary had a little lamb.
-----------------------------277529705--
Response:
code | "bad_request" |
help_url | "http://developers.box.com/docs/#errors" |
status | 400 |
message | "API upload did not contain a file part" |
type | "error" |
Code that generated the above:
$scope.sendFile = function sendFile()
{ const formDataFoo = new FormData();
formDataFoo.append( 'attributes', "{'name':'testFileName','parent':{'id':'0'}}" );
formDataFoo.append( 'file', "mary had a little lamb.");
var req =
{ method: 'POST',
url: 'https://upload.box.com/api/2.0/files/content',
headers: { 'Authorization': 'Bearer $$boxAccessToken$$',
'Content-Type': undefined
},
data: formDataFoo
};
$http( req ).then( sendFile2, netFailHandler );
}
I think the following is a (common) wild goose chase due to the 400 response also throwing CORS errors, but here's the documentation anyway: The response headers for the transactions look like this:
OPTIONS (first step of a new folder listing)
HTTP/1.1 204 No Content
Date: Thu, 18 Jun 2020 17:14:25 GMT
Content-Type: text/plain charset=UTF-8
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: DELETE, HEAD, GET, OPTIONS, POST, PUT
Vary: Origin
BOX-REQUEST-ID: 0c37c643fa34041a68540bffc72429aac
Access-Control-Max-Age: 1800
Access-Control-Allow-Headers: authorization
GET (folder listing)
HTTP/1.1 200 OK
Date: Thu, 18 Jun 2020 17:14:26 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
Content-Encoding: gzip
Vary: Origin
BOX-REQUEST-ID: 0ba35f126d13cecb769a3e49fe0b2e8a4
POST (create folder)
HTTP/1.1 201 Created
Date: Thu, 18 Jun 2020 17:14:26 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
ETag: "0"
Vary: Origin
BOX-REQUEST-ID: 020737d69c311367a6a6d499bff57437e
Note that all three of those responses include Access-Control-Allow-Origin: *
The problem comes when I'm trying to use the Upload API, its preflight OPTIONS check succeeds with a response like this:
HTTP/1.1 200 OK
Date: Thu, 18 Jun 2020 17:33:47 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://localhost:3000
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
Access-Control-Allow-Methods: POST
Vary: Origin
Access-Control-Allow-Headers: authorization
Strict-Transport-Security: max-age=31536000
Successful, but note that the Access-Control-Allow-Origin is more restrictive. The subsequent POST attempting to create a small file fails with this response header:
HTTP/1.1 400 Bad Request
Date: Thu, 18 Jun 2020 17:33:47 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 179
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
I have tried many variations with the request API, their request headers typically look something like this:
Host: upload.box.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Authorization: Bearer 7g6Jj9d17e16DCdYpcNNhp8VqFFFFFFF
Content-Type: multipart/form-data
Content-Length: 93
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/maint?logs=true&auth=&user=&text=true&latest=true
and the response that AngularJS' $http returns to the error handler looks like this:
{ "data":null,
"status":-1,
"config":{"method":"POST",
"transformRequest":[null],
"transformResponse":[null],
"jsonpCallbackParam":"callback",
"headers":{"Authorization":"Bearer LXeQOLMIN4kzwxPMwZlwhUmTeY06qmgB",
"Content-Type":"multipart/form-data",
"Accept":"application/json, text/plain, */*"
},
"url":"https://upload.box.com/api/2.0/files/content",
"data":{"attributes":"{'name':'testFileName',
'parent':{'id':'0'}
}",
"file":"mary had a little lamb."
}
},
"statusText":"",
"xhrStatus":"error"
}
Based on other threads for similar problems, I've tried reauthorizing the App in the admin console after the CORS domains allowed origins were updated - no change. Initial tests with the chunked upload API seems like it might work, but not for files smaller than 20MB.
The code I've been using to try to upload a file is:
$scope.uploadFile = function uploadFile()
{ var req =
{ method: 'POST',
url: 'https://upload.box.com/api/2.0/files/upload',
headers: { 'Authorization': 'Bearer $$boxAccessToken$$',
'Content-Type': 'multipart/form-data'
},
data: { attributes: { "name":"testFileName",
"parent":{"id":"0"} },
file: 'mary had a little lamb.'
}
};
$http( req ).then( uploadFileStep2, netFailHandler );
}
triggered by a button click:
ng-click="uploadFile()" class='button' type='button' id='uploadButton'>Upload
Any ideas how I might be able to upload small files to Box, successfully?
Thanks,
P.S. - for what it's worth, the "try this API" function in the Upload files API docs also fails with the message "An unknown error has occured".
-
Working now... needed to wrap the file data in a Blob (the filename passed to the Blob creator is ignored):
$scope.sendFile = function sendFile()
{ const foo = new FormData();
foo.append( 'attributes', '{"name":"testFileName","parent":{"id":"0"}}' );
foo.append( 'file', new Blob( ["mary had\na little lamb"],{type:"text/plain"}) );
var req =
{ method: 'POST',
url: 'https://upload.box.com/api/2.0/files/content',
headers: { 'Authorization': 'Bearer $$boxAccessToken$$',
'Content-Type': undefined
},
data: foo
};
$http( req ).then( sendFile2, netFailHandler );
}The working request body looks like this, seems like having the Content-Type specified on the file part of the body is what was missing:
-----------------------------420sep
Content-Disposition: form-data; name="attributes"{"name":"testFileName","parent":{"id":"0"}}
-----------------------------420sep
Content-Disposition: form-data; name="file"; filename="blob"
Content-Type: text/plainmary had
a little lamb
-----------------------------420sep--
Please sign in to leave a comment.
Comments
1 comment