This little tutorial will show you how to manage Appmixer SDK authentication.
Introduction
Appmixer SDK provides a simple way to set an access token on the instance, so it can make authenticated requests to the API. Nevertheless, it is up to the implementing application to manage token persistence. The reason for this is to allow the implementing application to store the token wherever it wants - e.g. localStorage, sessionStorage, database, Amazon S3, etc.
Storing the token somewhere allows to reuse the token, instead of requesting a new one each time the Appmixer SDK instance is re-created, for example when the user reloads the page.
Token management example
For this example, we are going to use browser's sessionStorage to retrieve and store the access token. So let's imagine we have a simple login form with username and password fields and a submit button. We write a login function that is called when the login form is submitted:
// Method called when a login form is submitted
function login(username, password) {
// Request a token from the engine
appmixer.api.authenticate(username, password)
.then((data) => {
// We retrieve the token from the response data object
const { token } = data;
// We set the token onto our SDK instance
appmixer.set('accessToken', token);
// Now this is where we persist our token using the
// sessionStorage. Remember that you can store the token
// pretty much on any storage system you can access from
// your application
sessionStorage.setItem('myAccessToken', token);
// After this you can redirect to home page
})
.catch((err) => {
console.log('Something wrong happened');
console.log(err);
});
}
As you can see, we use Appmixer API authenticate method to retrieve a token from the engine. After we get it, we do two things:
Set it on our SDK instance
Store it on browser's sessionStorage under the key myAccessToken. This will allow us to persist the token even after the SDK instance is destroyed.
Now in order to use our stored token, every time our application loads, we need to check if there is a stored token. Depending on if there is a token or not, our application would behave differently:
If there isn't a stored token, we redirect to our login form
If there's a stored token, we set it on our SDK instance and redirect to our home page
So to achieve this, your application would need to call a function like this every time it loads:
// Appmixer is initialized somewhere when your application starts
const appmixer = new Appmixer();
function initialize() {
// We look into sessionStorage using the same key we use for
// storing the token when we authenticate into our application
const token = sessionStorage.getItem('myAccessToken');
if (token){
// If token exists, we set it on the Appmixer instance
// and redirect to home page
appmixer.set('accessToken', token);
// Redirect to home page
} else {
// There is no token stored, redirect to login page
}
}
This way, the cycle is closed and you are able to persist and reuse your access tokens after your SDK instances are lost.
Check token expiration
Appmixer uses expirable JWT tokens to authenticate their users. For this reason it is desirable to not only check if we have a stored token, but also if this token is still valid.
For this purpose, we need to decode the token in order to get the expiration date. You can use the jsonwebtoken library to decode JWT tokens and be able to access the information within. So now we expand the last example by also checking if the JWT token is valid as well:
// Appmixer is initialized somewhere when your application starts
const appmixer = new Appmixer();
// This is a helper function that returns if a token is expired or not
function tokenIsExpired(token){
// We assume jsonwebtoken has been imported/exposed somewhere
const decoded = jsonwebtoken.decode(token);
if (decoded && decoded.exp) {
const timeInSeconds = Math.ceil(new Date().getTime() / 1000);
return decoded.exp < timeInSeconds;
}
return true;
}
function initialize() {
// We look into sessionStorage using the same key we use for
// storing the token when we authenticate into our application
const token = sessionStorage.getItem('myAccessToken');
// Now in addition to checking if there's a stored token
// we also check if is still valid
if (token && !tokenIsExpired(token)){
// If token exists, we set it on the Appmixer instance
// and redirect to home page
appmixer.set('accessToken', token);
// Redirect to home page
} else {
// There is no token stored, redirect to login page
}
}
Appmixer SDK also provides a convenient way to handle invalid tokens, specifically allowing to execute custom code whenever an invalid token is used in a request made by the SDK. The SDK instance exposes onInvalidAccessToken(callbackFn) method, which receives a single function argument which is going to be called whenever the SDK is being used with an invalid token. Usually inside this callback you normally would provide the users a way to re-authenticate themselves, like redirecting them to a login form. Now we show how to use this method to achieve the same result as the previous example:
// Appmixer is initialized somewhere when your application starts
const appmixer = new Appmixer();
appmixer.onInvalidAccessToken(function(){
// Inside this function we could redirect users to the login form,
// or if you have what's neccessary you can generate new token by
// calling the proper function (appmixer.api.authenticate) which
// will return new fresh token.
});
function initialize() {
// We look into sessionStorage using the same key we use for
// storing the token when we authenticate into our application
const token = sessionStorage.getItem('myAccessToken');
// Now in addition to checking if there's a stored token
// we also check if is still valid
if (token){
// If token exists, we set it on the Appmixer instance
// and redirect to home page
appmixer.set('accessToken', token);
// Redirect to home page
} else {
// There is no token stored, redirect to login page
}
}
Multi-user management
In order to work with multiple SDK users at the same time, you can simply use more than one SDK instance at the time: