Now we can build the application, it’s time to start analyzing what’s really going on. My plan is to find out how exactly the authentication process works, so I might be able to change it.
In the following posts I assume you have at least some programming experience, and know a few things about object oriented programming. If things are a bit fuzzy, please read up on OOP and Java to be able to follow my exposé.
You can find some great documentation on the Oracle site.
But we have to start somewhere, and where better to start than the main class. We know that the application will instantiate a the main class upon startup.
The main class is situated in the AVSApp.java file under the com.amazon.alexa.avs package. You can see it because there’s a small green triangle by the file icon.
If you open the file you can see the class, and from the class definition we can indeed deduct that the main class is in fact the frame that’s displayed on the screen. A frame in Java is a window with controls on it, like buttons, text boxes, labels etc.
The class extends (inherits from) the “JFrame” class and implements a few interfaces in which we’ll probably be interested later on. They implement the multiple inheritance paradigm, and assuming by the name we’ll probably need AccessTokenListener, but I’ll see what’s up with that later on.
We can see that the class contains 3 overloaded constructors, all of which boil down to the last one where the constructor instantiates some stuff depending on what is presumably a container class called DeviceConfig. When executing the application in NetBeans it will use the constructor with any arguments, which apparently will read a configuration file using the DeviceConfigUtils class.
Let’s find out where the configuration file is, and what it contains. Below is a screenshot of part of the class definition of the DeviceConfigUtils class.
Above, we see that a parameterless method called readConfigFile() of the DeviceConfigUtils class uses a constant defined in the DeviceConfig class to open its default configuration.
Let’s check it out :
And there we have it. A number of static variables (constants) that are used by the application, one of which is the filename “config.json“. Let’s check out what’s in it. It can be found in the root of the Java Client directory.
Let’s see what’s in it using the “cat” command
Here, we can clearly see how the automated install script we used earlier to install all of this, changed this configuration file to include the ProductId fore example. What alarms me is for example the provisioningMethod property. It’s set to the companionService, which is exactly the thing we want to replace.
If we look into the container class called DeviceConfig, into which the settings are read, we can see that configuration setting of the provisioning method is an enumeration called ProvisioningMethod.
Interestingly enough, it is defined further into the file, we can see the definition.
The default setting as it is now is set to Companion Service, and there is an alternative called Companion App.
This rings a bell. If we go into the documentation of the Alexa Voice Service, we can find the following document:
In here, there is an explanation of how the configuration of the authentication of a companion app (instead of a companion service) should work, and it clearly states the following :
When to use this method?
Scenario: You want to authorize a headless device, like a smart speaker, to access AVS and associate it with a customer’s account using a companion app.
Let’s make things clear. The Java Client is not a Companion App. I learned this along the way, but it’s important to give this information right now.
If you read the documentation on the authentication for AVS carefully, you will see that there are 2 types of authentication :
- Implicit Grant — Send a client ID to get an access token. This is the one implemented using the companion service.
- Advantage: Easier to implement. Only relies on client-side scripting.
- Disadvantage: The access token expires.
- Authorization Code Grant — Send a client ID and a client secret to get an access token and a refresh token.
- Advantage: More secure. Indefinite access by using the refresh token.
- Disadvantage: Requires server-side scripting. More difficult to implement.
So far, we were able to access the service by using the sample companion service. Remember how the disadvantage here was that we had to re-authenticate every time using the browser. This is an implicit grant. The companion service sends the client ID to AVS, and an access code is received. The access code is passed to the Java Client, and with it, the requests are sent.
We’re on the lookout of implementing the second one. Study the below image carefully to fully understand the workings of the Authorization Code Grant.
In the example above the “Product” on the right hand side is our Raspberry Pi. It calls AVS with an access token. That’s also what it did using the Implicit Grand authorization, but the way it gets its Access Token is different.
As the product is “headless”, you can’t open a browser of some kind. Therefore you need another application to authorize for the product (in our case the Raspberry Pi). This can be done using a Companion App. You really need to see it as a mobile app (android or ios) that will authenticate to amazon on behalf of your product.
In the search through the code above in this article, we can find a setting to change from “Companion Service” to “Companion App” in the Java Client. Changing this setting does not all of a sudden make the Java Client a companion app. It changes the role of the Java Client to an application that can handle the authentication through an external Companion App. The Java client will be running on the product (in our case the Pi). The companion app on your mobile should request the product metadata from your product. We’ll find out how that works later on. Once this information is returned, the mobile app is responsible to get an authorization code from Amazon. It does this with a “User Consent”. Meaning : I allow this product to connect to Amazon using my credentials. This authorization code is then sent back to the Java Client on the Pi. Using that code it can request tokens. Tokens (multiple) because there are 2 kinds of tokens. Where as the companion service only used an Access Token (which expires), this authentication method also uses a refresh token. This refresh token is very important. It is generated once using the authorization code and sent back to the java client on the Pi. The Java Client is responsible for saving this token, as it is valid indefinitely. How it does that, we’ll come back to later on. Once the access token requires, the Java Client automatically requests another one using the refresh token.
The big question is : how does all this work?
Let’s continue our quest in the next article.