-
Notifications
You must be signed in to change notification settings - Fork 226
AuthenticationProviders
A new concept introduced in version 1.3.x of the SDK is authentication providers. The general idea is that we need to separate how the client stores and gets credentials from the rest of the client and transport code.
Up until version 1.2.x, the only way to pass credentials to the device client was through either the factory methods (fromSharedAccessSignature
and fromConnectionString
) or runtime methods (updateSharedAccessSignature
and setOptions
) to "update" the credentials used to authenticate with the Azure IoT Hub instance. This logic is limiting when thinking about secure storage of the
credentials, typically using a hardware security module (eg. a TPM) and puts the onus on the SDK user to figure out how to read or generate credentials using secure storage, and how and when to pass them to the device client.
The idea of an authentication provider is that it's an object that is given to the client by the user, and the client can query the authenticate provider for credentials whenever it needs them. In case of token-based authentication (as opposed to certificate-based authentication) the authentication provider can also tell the client when a new token is available for it to use.
Azure IoT Hub supports 2 types of authentication mechanism for devices: security tokens, and x509 certificates (to learn more, go read the about Azure IoT Hub security). The difference is that security tokens need to be refreshed before they expire and each transport has a different way of re-authenticating the connection with the Azure IoT Hub instance.
Note: X509 certificates can expire too but their renewal is usually not nearly as frequent as security tokens. By default, security tokens are renewed every hour, whereas certificates have days, weeks or even years before expiry.
The basic AuthenticationProvider
interface is defined here. It only has one method, getDeviceCredentials
which takes a callback and that callback is called either with an error or the device credentials (including deviceId, IoT hub host name, and either a security token or a certificate). Any authentication provider should implement at least this.
On top of that, we added a type
property that can either be AuthenticationType.Token
or AuthenticationType.X509
. We went that route instead of using different custom types to avoid a painful inheritance scheme.
Finally, authentication providers of type AuthenticationType.Token
are expected to inherit from EventEmitter
and emit the newTokenAvailable
event before the expiry time of the token so that the device client can re-authenticate before it gets an UnauthorizedError
from the Azure IoT Hub instance. You'll find below what the default authentication providers provided with the SDK do.
In the case of X509 authentication, the connection is authenticated only when established, so the device client only relies on the getDeviceCredentials
call.
Since we hate breaking changes, we are keeping the existing fromSharedAccessSignature
, fromConnectionString
, updateSharedAccessSignature
and setOptions
call. so your existing code will work and take advantage of this new architecture, since we "hide" one of the default authentication providers below those.
A shared access key is used to generate a security token either for an access policy, or a specific device. it's corresponds to the aptly named SharedAccessKey
section of a connection string. The SharedAccessKeyAuthenticationProvider will keep a copy of this key in memory and will generate security tokens on a regular basis. How regularly? every 45 minutes by default, and each token will have a 1 hour expiry time. This default behavior can be overridden by passing a tokenValidTimeInSeconds
and a tokenRenewalMarginInSeconds
arguments to the fromConnectionString
factory method. tokenValidTimeInSeconds
is the time the token should be valid for. tokenRenewalMarginInSeconds
is the number of seconds before expiry at which we should start the renewal process.
In the case of the SharedAccessSignatureAuthenticationProvider, the SDK is missing the shared access key to automatically renew the security token - therefore the user must call the SharedAccessSignatureAuthenticationProvider.updateSharedAccessSignature
method to update the token. calling this method will immediately trigger a newTokenAvailable
event for the client. No need to call updateSharedAccessSignature
on the client anymore.
The X509AuthenticationProvider only implements the getDeviceCredentials
method and calls the callback with the certificate and its private key that were passed when creating the X509AuthenticationProvider
. For compatibility purposes with the legacy setOptions
call of the client we also have a setX509Options
method on this authentication provider but we do not recommend using it. it will NOT fire the newTokenAvailable
event (it's not a token, and when using X509 the client does not support updating the certificates anyway).
In order to let SDK users create their own authentication providers as needed, or use the default ones, we've added a fromAuthenticationProvider
factory method in the device client object. it simply takes an authentication provider and a transport as arguments.
To be honest, the client does not really care about the authentication provider. the transports do. When the client instantiate the transport object, it'll pass the AuthenticationProvider
object to the transport who knows what to do with it.
The transport will check if it's a token-based authentication provider or not. if it is, it will subscribe to the newTokenAvailable
event.
When called upon to connect, the transport will call getCredentials
to retrieve the credentials and use them to connect. It's pretty simple really.