One of the main issues with Net::HTTP stems from OpenSSL. When OpenSSL is initiated for the first time it can cause a significant lag in SU. Anything from half a minute to half and hour+. Seems to depend on how much memory the SU app have used that session.
Another issue would be if you needed not to block the main thread, since Ruby threads doesn’t work properly inside of SU. So it’s tricky to do async HTTP requests.
I’m using SendOwl, but I don’t contact it directly from my extension. I find it too risky because you need the secret key to their service to use their API.
I make requests to my own server, which in turn takes care of checking with SendOwl. (This is done via PHP in my case.)
This allows me to put additional checks in place, such as number of activations etc - so I can track if someone is sharing their serial. That allows me to deactivate a key if I want. It also decouples the extension from directly depending on SendOwl. If I replace SendOwl with another service, I don’t have to update my extension.
The HTTP request I make from my extension to my own server is done in JS, from within the webdialog. Because the user input their serial key into a webdialog - so I can just do it directly there. Saves me from pushing the logic into Ruby. Especially nice since Ruby’s Net::Http lib’s HTTPS connections can be a pain.
I find JSON to be easier to deal with than XML in Ruby. You can just require 'json' and get a Hash+Array structure that is easy to navigate. With XML you need additional library.
That SendOwl response is actually very helpful. Tells you exactly what you are missing. You need to add a header the declare how you want to accept the response.
But be very careful with your secret key, if anyone obtains it they can potentially create their own licenses, or list all known licenses. That’s which I put a layer between my extension and the SendOwl API. My layer in between allows no write access or enumerations.
This will happen if you use the standard library from the console.
But as Thomthom says, the loading of OpenSSL could take 30 minutes !
Does Send Owl insist upon using the HTTPS protocol ?
If not, … if it will work with just plain HTTP use that IF (and only if) you must use the standard Ruby Net::HTTP library classes (say with SketchUp versions earlier than 2017, where the newer API classes are not available.)
Plain text strings are very easy to see in Ruby’s memory, so loading your “secret” strings and having them sit in memory as plain text is not very secure.
So here is where your at now using just the API classes in SU2017+ …
(You’re actually not using URI so I leave this out of the depandancies.)
require 'json'
check_license()
# Setup variables
key = ''
secret = ''
productId = ''
licenseKeyToCheck = ''
# Perform check
url = "https://#{key}:#{secret}@www.sendowl.com/api/v1/products/"<<
"#{productId}/licenses/check_valid?key=#{licenseKeyToCheck}"
@request = Sketchup::Http::Request.new(url)
@request.headers = {
'Accept' => 'application/json'
}
@request.start do |request, response|
case response.headers['content-type'].split.first.chomp(';')
when 'application/json'
@@license_details = JSON.parse(response.body, symbolize_names: true)
# @@license_details will be a Ruby hash if successful
when 'application/xml'
@@license_details = parse_xml( response.body )
# Need to use some library like REXML or your choice ...
when 'text/html'
@dlg = UI::HtmlDialog.new(
dialog_title: "SendOwl Response: #{response.status_code}",
preferences_key: nil,
scrollable: true,
resizable: true,
width: 600,
height: 400,
)
@dlg.set_html( response.body )
@dlg.show
else
puts "body: #{response.body}"
end
end
end ###
Another note. Credentials embedded in URIs have been deprecated (at the least) and removed from the web specifications because of security concerns. Most web browsers will no longer allow their use.
Remember that Ruby is a very reflective and dynamic language. Once an encrypted RBE is loaded, the code is loaded into the interpreter and there are many ways to poke and inspect the various objects - like strings used, that might contain sensitive data. Any language with a garbage collector will make data stay alive for longer than the execution of the code.
Also, there are various tools meant to debug HTTP traffic, that also allow the administrator of a machine to also inspect HTTPS traffic. This means the secret key passed from the machine could be extracted from the owner of the machine.
I had some PHP experience from before. So I set up a new server for my commercial extensions using Laravel, a PHP framework. Then I used Laravel Forge to help me deploy the website easily by just pushing to a Git repository for the website.
That being said, if you only want to have a license server, you can go for a simpler solution. Find a host that offer any language you prefer, Ruby, PHP, Python… what ever suit your needs. If Ruby is your strong suit, then you might be better off picking a host that allows you to use Ruby. And then you should be able to set up a rather small license server. Just an API endpoint which you call from your extension, then your server makes the SendOwl request on you behalf. This could keep your secret key entirely secured on your own server and not stored in any form on your users’s machine.
I’m not that familiar with Ruby web applications, but Rack might be one solution to quickly set up a webserver: https://rack.github.io/
But this is just a suggestion. I merely wanted to point out potential gotchas in case you were not aware of them. How much effort you want to put into it is up to you.
I’m in favor of not spending too much time on license and anti-piracy measure. Just enough to keep honest people honest. You can ramp up the security if you see it actually being a problem. Otherwise it’s very easy to get lost in bolting on too much security measure without actually getting to the point of delivering a product.
SendOwl docs describe that the API key and secret key is passed via Basic Auth:
However, the CURL example they provide demonstrate authenticating via URL. Probably for convenience in for a short example. But as Dan mentions, it’s deprecated.
But going back to the SendOwl License API, it’s important to read its documentation: Licenses - API - SendOwl
IMPORTANT - Do not include calls to this API endpoint in your software. Whilst this will enable you to check license validity directly, it will expose your API key and secret to anyone examining your source code or watching wire data. With your API key and secret somebody could access your order history, remove your products… in short anything this API allows. Instead, to check license data in your software perform a call to your own server, which should in turn make a call to the SendOwl API to check license validity. This ensures only your server has details of your API key/secret. Order name/email can also be checked for additional security by checking the order associated with the license.
ALL of our examples are meant to be wrapped WITHIN your author/company namespace module, plugin sub-module (and/or custom classes if needed.)
It is obvious from this warning that the method should not be defined at the top level (in Ruby’s ObjectSpace.)
Ie, a module or class variable is for use within a module or class definition.
I know that being in your position right now can be frustrating. You have product and new ideas you want to get to, but you need some form of license activation. While looking for solutions it looks to quickly snowball into a much more complicated solution.
I think that for your initial start you should try to go for simplicity. Get a minimal setup going, which you can release your products with. Then you can slowly expand functionality as needed.
He’s my suggestion for a bare minimum:
Since you write Ruby extension, I’m going to suggest trying to set up a Ruby powered server. (Pick another language if you feel more comfortable with that.)
Get a host for your server. You can probably do with a simple shared hosting that support your chosen server language. This would be doable to obtain very cheaply.
You off course need a domain (you might already have this?)
Set up a very simple HTTP API endpoint on your server, where you accept a serial key (maybe email if you want to double check data) Something like example.com/api/v1/check_license?key=0123-456&email=john@example.com
(I added v1 to the URL because it makes it easier to deal with older clients that hit your old API)
Find a small framework to serve your responses. In Ruby it looks like Rack might be good starting point. I found this guide: Getting Started with Rack · GitHub It show you how to set up API end points (routes) and how to map them to various actions. With that you make your server call the SendOwl API and return your own custom response. Along that you can add logging if you want. Something you can add later. If you feel the need for it you can expand it to have your extension pass the MAC address of the machine its on, so you can from your server enforce an x number of activation.
If you plan to support SU versions older than SU2017 you need to evaluate whether you want to use Net::Http or do an AJAX call from the JavaScript in a WebDialog. (You might find less hassle with AJAX call from a webdialog.) But even with HTTP requests from JavaScript there are some gotchas, cross site scripting protection. You may have to do your request as a JSONP request to work around that. But all of that are details you have to judge depending on your own requirements.
Have a go at trying to set up a mini-server - keep it simple. Then let us know how it’s going.
A completely different aproach could be to use Extension Warehouse. While it’s quite far from perfect it’s possible to just plug into and get going with actually create new extensions.
I am currently selling some extensions on Extension Warehouse. You and other developers have helped me through that journey. It was not easy but after some persistence, I managed to get the SketchUp Licensing API working.
Now, I want to learn how to license my extensions with SendOwl API because if I want to also sell a software not related to SketchUp I can do so…For example a game.
Yes, it can be frustrating but I also think it is fun to learn how to code. SketchUp has been responsible for me learning how to code and I will keep making extensions. But, I also want to branch out and start learning to code other stuff like games and web apps.
Starting with a minimal viable product is the way to go indeed. I read a book by Eric Ries - The Lean Startup and it talks about the importance of not wasting time developing stuff.
Is Heroku viable? If so I have deployed a simple crud app made with rails already so I am not starting from scratch.
Yes, I have bought domains before with Google domains.
I am new to this but I have already started watching tutorials on Rest and how to make a request to an API.
I’m not familiar with Ruby web apps, I’ve been using PHP. If you have something related that you’ll comfortable with, then that’ll work just fine. It’ll probably get you up and running faster than learning a new framework.