Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. 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

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

Comments

  • chadstoltzfuschadstoltzfus Member, Developers, csevp Posts: 144 PRO
    If you are using C# and are not using any imported libraries/examples from Onshape, you may have yet to set up a proper generator for request signatures. Check out the documentation on https://onshape-public.github.io/docs/apikeys/ to see what HTTP headers you'll need to set up to create a secure API call.  
    Applications Developer at Premier Custom Built
    chadstoltzfus@premiercb.com
  • billy2billy2 Member, OS Professional, Mentor, Developers, User Group Leader Posts: 2,071 PRO
    It sounds like a permission problem. Maybe you don't have rights to the document? With the api key method, you can only access your stuff, no one else. If you want to access other people's stuff you need to be using Oauth.

    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.


  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    If you are using C# and are not using any imported libraries/examples from Onshape, you may have yet to set up a proper generator for request signatures. Check out the documentation on https://onshape-public.github.io/docs/apikeys/ to see what HTTP headers you'll need to set up to create a secure API call.  
    Thanks for the reply, I will check it out.
    I have read this documentation, I will read it once again; see what I missed.
  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    If you are using C# and are not using any imported libraries/examples from Onshape, you may have yet to set up a proper generator for request signatures. Check out the documentation on https://onshape-public.github.io/docs/apikeys/ to see what HTTP headers you'll need to set up to create a secure API call.  
    Thanks for the reply, I will check it out.
    I have read this documentation, I will read it once again; see what I missed.
  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    billy2 said:
    It sounds like a permission problem. Maybe you don't have rights to the document? With the api key method, you can only access your stuff, no one else. If you want to access other people's stuff you need to be using Oauth.

    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.


    Thank you for the reply.
    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? 
  • billy2billy2 Member, OS Professional, Mentor, Developers, User Group Leader Posts: 2,071 PRO
    edited November 2021
    Yes, make sure you've established the communication protocol. Typically it's json. 

    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.
  • billy2billy2 Member, OS Professional, Mentor, Developers, User Group Leader Posts: 2,071 PRO
    edited November 2021
    I'm looking at my code and a have a function that builds headers for both post & get. It's the standard code that OS supplied.

    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?



  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    @billy2
    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.
  • billy2billy2 Member, OS Professional, Mentor, Developers, User Group Leader Posts: 2,071 PRO
    edited November 2021
    I think it insures your header has it. Turns out I decided not to inherit the header object and just created it.

    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.


  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    @billy2
    Thanks a lot, I will try this and tell you if there is any progress.


     
  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    @billy2
    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 
     




     
  • Pratik_BajracharyaPratik_Bajracharya Member Posts: 10
    This is my c# code:
       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;
            }
    




     
Sign In or Register to comment.