Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.
First time visiting? Here are some places to start:- Looking for a certain topic? Check out the categories filter or use Search (upper right).
- Need support? Ask a question to our Community Support category.
- Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
- Be respectful, on topic and if you see a problem, Flag it.
If you would like to contact our Community Manager personally, feel free to send a private message or an email.
Using API key gives me '401 Unauthenticated API request' message
Pratik_Bajracharya
Member Posts: 10 ✭
Hi everyone, I am using my API key to use the Onshape API. I read the documents and saw few example so i was able to get successful response in few API. The weird thing is when is use the User API (i.e. /users/settings or users/sessioninfo) I get the response, but when I use any other API like get parts, I get a '401 Unauthenticated API' request message.
I have checked my API key scope and I have all the scope, does anyone know what I am doing wrong?
P.S i am using c#
Thank you,
Pratik
I have checked my API key scope and I have all the scope, does anyone know what I am doing wrong?
P.S i am using c#
Thank you,
Pratik
0
Comments
chadstoltzfus@premiercb.com
I'd use your credentials and the api explorer working 1st, then get your server code working. The api explorer only works on your stuff.
I have read this documentation, I will read it once again; see what I missed.
I have read this documentation, I will read it once again; see what I missed.
I have successfully get a response by using API key. The previous problem was because of my nonce and header, once I fixed this I got proper response.
Sadly I have stumble upon another problem; while using the 'Upload file to new element' API (/blobelements/d/:did/w/:wid) I get the same 401 message. It is a document that I have created, so not sure if it is permission problem.
Does POST method require any extra steps to generate the signature, something to do with the request body maybe?
It sounds like it's a post header configuration problem.
I'll look at my post request and see if there are others. Post headers are more than get headers.
var buildHeaders = (method, path, query, accept) ... if (!('Content-Type' in headers)) { headers['Content-Type'] = 'application/json';}It looks like content type is always added to header for either post or get, that makes sense.
My example code has one header for both post & get.
Does this look familiar to you, is this the example code you're using?
Yes I have seen something similar, but isn't it only if the Content-Type in header is empty? the api i am using requires the content type to be multipart/form-data. I am currently looking at: https://github.com/onshape-public/apikey/blob/master/python/apikey/client.py#L187 ; but still haven't got any success.
Here's the entire function
var buildHeaders = (method, path, query, accept) => { var accessKey='***'; var secretKey='***'; //console.log(`buildHeaders method ${JSON.stringify(method, null, '\t')}`) //console.log(`buildHeaders path ${JSON.stringify(path, null, '\t')}`) //console.log(`buildHeaders query ${JSON.stringify(query, null, '\t')}`) //console.log(`buildHeaders accept ${JSON.stringify(accept, null, '\t')}`) //console.log(`buildHeaders inputHeaders ${JSON.stringify(inputHeaders, null, '\t')}`) //var headers = copyObject(inputHeaders); var headers = {}; // the Date header needs to be reasonably (5 minutes) close to the server time when the request is received var authDate = (new Date()).toUTCString(); //var authDate = 'Mon, 01 Oct 2018 02:09:31 GMT'; // the On-Nonce header is a random (unique) string that serves to identify the request var onNonce = buildNonce(); if (!('Content-Type' in headers)) { headers['Content-Type'] = 'application/json'; } // the Authorization header needs to have this very particular format, which the server uses to validate the request // the access key is provided for the server to retrieve the API key; the signature is encrypted with the secret key var hmacString = (method + '\n' + onNonce + '\n' + authDate + '\n' + headers['Content-Type'] + '\n' + path + '\n' + query + '\n').toLowerCase(); //console.log(`hmacString ${hmacString} \n`) var hmac = crypto.createHmac('sha256', secretKey); hmac.update(hmacString); var signature = hmac.digest('base64'); var asign = 'On ' + accessKey + ':HmacSHA256:' + signature; headers['On-Nonce'] = onNonce; headers['Date'] = authDate; headers['Authorization'] = asign; if (!('Accept' in headers)) { if (accept==='octet-stream') headers['Accept'] = 'application/vnd.onshape.v1+octet-stream'; else headers['Accept'] = 'application/vnd.onshape.v1+json'; } return headers; }<br>I remember going through this line by line.
I did debug this function and left the console output in the code.
This is nodejs or javascript.
Thanks a lot, I will try this and tell you if there is any progress.
Hey Billy I got an update,
1) I tired the upload Blob with API key using the nodeJs and it works fine!! the content type is kept as:
"multipart/form-data; boundary=<boundary_key>".
But when I try to replicate this with C# i get a 400 'bad request' error, I suspect my headers and signature are correct, its the matter of post body or the file I'm sending. I am sending a small STL file (same file is used for nodeJs method)
2) I tried the same Upload blob api; used the same code the only difference is that I used Oauth instead of the signature, this also works fine! So I am much more confused as to why I can't do the same thing with my API key method...
Thank you in advance
private static readonly Encoding encoding = Encoding.UTF8; public HttpWebResponse MultipartFormDataPost(string postUrl, Dictionary postParameters, string FileName) { string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid()); string contentType = "multipart/form-data; boundary=" + formDataBoundary; byte[] formData = GetMultipartFormData(postParameters, formDataBoundary); return PostForm(postUrl, contentType, formData, FileName); } public async System.Threading.Tasks.Task UploadDocument2(string DocumentId, string WorkspaceId, string FileName) { var fileName = Path.GetFileName(FileName); var path = Server.MapPath("~/Content/") + fileName; FileStream fs = new FileStream(path,FileMode.Open,FileAccess.Read); byte[] data = new byte[fs.Length]; fs.Read(data, 0,data.Length); fs.Close(); Dictionary postParameters = new Dictionary(); postParameters.Add("filename", fileName); postParameters.Add("file", new WinApiController.Models.FileParameter(data, fileName, "application/octet-stream")); string Url = "https://cad.onshape.com/api/blobelements/d/{0}/w/{1}"; string postURL = string.Format(Url, DocumentId, WorkspaceId); HttpWebResponse webResponse = MultipartFormDataPost(postURL, postParameters, fileName); return Json(webResponse,JsonRequestBehavior.AllowGet); } private HttpWebResponse PostForm(string postUrl, string contentType, byte[] formData, string FileName) { HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest; if (request == null) { throw new NullReferenceException("request is not a http request"); } var path = Server.MapPath("~/Content/") + FileName; var D = DateTime.UtcNow; var Date = String.Format("{0:r}", D); var nonce = GetNonce(); var sign = getSignature2(new Uri(postUrl), contentType, "post", Date, nonce); ///////////////////////////////////////////////////////////////////////////////////////// ///this method gives 400 no file //var client = new RestClient(postUrl); //client.Timeout = -1; //var request = new RestRequest(Method.POST); //request.AddHeader("Content-Type", contentType); //request.AddHeader("Date", Date); //request.AddHeader("On-Nonce", nonce); //request.AddHeader("Authorization", sign); //request.AddHeader("Accept", "application/vnd.onshape.v1+json"); //request.AddFileBytes("file", formData, FileName); //request.AddFile("file", System.IO.File.ReadAllBytes(path), Path.GetFileName(path)); //request.AlwaysMultipartFormData = true; //IRestResponse response = client.Execute(request); ///////////////////////////////////////////////////////////////////////////////////////// request.Method = "POST"; request.ContentType = contentType; request.CookieContainer = new CookieContainer(); request.ContentLength = formData.Length; request.Date = D; request.Accept = "application/vnd.onshape.v1+json"; request.Headers.Add("On-Nonce", nonce); request.Headers.Add("Authorization", sign); using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(formData, 0, formData.Length); requestStream.Close(); } return request.GetResponse() as HttpWebResponse; } private static byte[] GetMultipartFormData(Dictionary postParameters, string boundary) { Stream formDataStream = new System.IO.MemoryStream(); bool needsCLRF = false; foreach(var param in postParameters) { if (needsCLRF) formDataStream.Write(encoding.GetBytes(""), 0, encoding.GetByteCount("")); needsCLRF = true; if(param.Value is FileParameter) { FileParameter fileToUpload = (FileParameter)param.Value; string header = string.Format("--{0}Content-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"Content-Type: {3}", boundary, param.Key, fileToUpload.FileName ?? param.Key, fileToUpload.ContentType ?? "application/octet-stream"); formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header)); formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length); } else { string postData = string.Format("--{0}Content-Disposition: form-data; name=\"{1}\"{2}", boundary, param.Key, param.Value); formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData)); } string footer = "--" + boundary + "--"; formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer)); formDataStream.Position = 0; byte[] formData = new byte[formDataStream.Length]; formDataStream.Read(formData, 0, formData.Length); formDataStream.Close(); return formData; } return null; }