Hey guys, I am back with another react native article. Today, we are going to discuss about most used use case in almost all the react native applications. That is handling Json Web Tokens in react native.
JWT tokens are very handy to use because of their “no need to store nature”. You don’t store JWT tokens in your server, because of their stateless nature, they are used widely.
Normally, most use one these two ways of handling JWT tokens.
- One single Long living JWT token.
- Access Token(Some people call it just Token, Short living) along with Refresh Token(Short living but has more living duration than Access Token)
Most of the applications use second one, they use two tokens to authenticate users, i.e token (Some say it as access token) and refresh token.
The access token will have short living duration compare to refresh tokens. Because of this nature, access tokens are refreshed using the refresh tokens.
For example, Say your server decides to have 5 minutes duration for access token, and 10 minutes duration for refresh token.
Then after 5 minutes duration of access token is over, we use refresh token to renew the access token for every 5 minutes till the expery of refresh token is over. In our case refresh token expiry is 10 minutes.
In this article, I discuss about handling Tokens in second way. I also assume that you know how to generate these two tokens immediately after user logged in.
Let’s see the important points in Authentication.
- User Sign in with Username and Password in a react native app. And gets Access and Refresh tokens in return.
- User will have to pass access token to do any CRUD operations later after signin.
- If access token is expired, uses refresh token to get new access token.
- If refresh token is expired, user will have to signin again.
- It is also possible to renew the refresh token at the time of renewing access token. That makes forever logged in authentication system.
This artcle does not discuss about way of generating JWT tokens from backend. It mainly discusses about automatically refreshing received JWT tokens once the user is logged in. We prepare a simple credentials.js file for react native that automatically refreshes tokens.
Before we dive into coding, I assume that you already have server that generates and returns Access and Refresh tokens in the below format.
{
"access":"xxxxxxxxxxxxxxxxxxx",
"refresh":"xxxxxxxxxxxxxxxxxx"
}
Let’s create credentials.js file in root directory of your existing react native project, or create a new project.
I am using AsyncStorage to store tokens in mobile. You can install it by reading the instructions here.
Now lets prepare setCredentials and getCredentials helper functions in credentials.js to read and write from AsyncStorage.
import AsyncStorage from '@react-native-community/async-storage'
const setCredentials = async keys => {
try {
await AsyncStorage.setItem('keys', JSON.stringify(keys))
} catch (e) {
console.log(e)
}
}
const getCredentials = async () => {
try {
let credentials = await AsyncStorage.getItem('keys')
if (credentials != null) {
return JSON.parse(credentials)
} else {
return null
}
} catch (e) {
console.log(e)
}
return null
}
As you can see in the above code, we have prepared two functions to read and write from AsyncStorage.
You can use setCredentials function to store the tokens as soon as your user is logged in.
Now the important thing is, we need to able to get the unexpired tokens from the getCredentials function, but currently it just reads from the AsyncStorage and returns them.
To automatically, read and check the expiry and return the valid tokens, we need to be able to read the exp value of the access token and refresh token.
To make this easier, we need to install jwt-decode library from npm, which decodes the jwt tokens. So that we can read the exp value of the tokens. To install jwt-decode use the command below.
npm install jwt-decode
And you can use it like below
var token = 'eyJ0eXAiO..';
var decoded = jwt_decode(token);
console.log(decoded);
/* prints:
* { foo: "bar",
* exp: 1393286893,
* iat: 1393268893 }
*/
Now lets create new helper function called isTokenExpired that checks if the token is expired or not. Let’s add it to our credentials.js file.
.....
var jwt_decode = require('jwt-decode');
.....
function isTokenExpired (token) {
var decoded = jwt_decode(token)
if (decoded.exp < Date.now() / 1000) {
return true
} else {
return false
}
}
Above simple function returns true if the token is expired or false if not expired.
To make getCredentials function return unexpired tokens, we need to create another function called getVerifiedKeys that checks the expiry of access token and refresh token and act accordingly.
Lets add the getVerifiedKeys function in credentials.js file.
......
async function getVerifiedKeys (keys) {
console.log('Loading keys from storage')
if (keys) {
console.log('checking access')
if (!isTokenExpired(keys.access)) {
console.log('returning access')
return keys
} else {
console.log('access expired')
console.log('checking refresh expiry')
if (!isTokenExpired(keys.refresh)) {
console.log('fetching access using refresh')
//TODO: write code to get refreshed tokens from server and store with AsyncStorage.
return null
} else {
console.log('refresh expired, please login')
return null
}
}
} else {
console.log('access not available please login')
return null
}
}
......
The above code checks if the Access token is expired, if not expired it will return the keys.
if expired, it will check for refresh token expiry, if refresh token is also expired sends null. And user have to login again.
there is another case where access is expired but not refresh, in such case, we need to use the refresh token to get the renewed access token.
To do that, we need to create a function called getAccessUsingRefresh, lets create and add that function to our credentials.js file.
async function getAccessUsingRefresh (refreshToken) {
return fetch(URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(refreshToken)
}).then(res => res.json())
}
The above code simply takes the refresh token and returns object consisting of both renewed refresh and access tokens.
Remember, you need to make necessary arrangements to return renewed access token using refresh token from the server side.
Let’s add this function to our getVerifiedKeys function.
......
async function getVerifiedKeys (keys) {
console.log('Loading keys from storage')
if (keys) {
console.log('checking access')
if (!isTokenExpired(keys.access)) {
console.log('returning access')
return keys
} else {
console.log('access expired')
console.log('checking refresh expiry')
if (!isTokenExpired(keys.refresh)) {
console.log('fetching access using refresh')
const response = await getAccessUsingRefresh(keys.refresh)
await AsyncStorage.setItem('keys', JSON.stringify(response))
console.log('UPDATED ONE')
return response
} else {
console.log('refresh expired, please login')
return null
}
}
} else {
console.log('access not available please login')
return null
}
}
......
And the last step is to make our getCredentials function to return renewed keys. Let’s do that..
const getCredentials = async () => {
try {
let credentials = await AsyncStorage.getItem('keys')
let cred = await getVerifiedKeys(JSON.parse(credentials))
if (credentials != null && cred != null) {
return cred
} else {
return null
}
} catch (e) {
console.log(e)
}
return null
}
Let’s put everything together, the complete credentials.js looks like below.
import AsyncStorage from '@react-native-community/async-storage'
var jwt_decode = require('jwt-decode')
async function getAccessUsingRefresh (refreshToken) {
return fetch(URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(refreshToken)
}).then(res => res.json())
}
async function getVerifiedKeys (keys) {
console.log('Loading keys from storage')
if (keys) {
console.log('checking access')
if (!isTokenExpired(keys.access)) {
console.log('returning access')
return keys
} else {
console.log('access expired')
console.log('checking refresh expiry')
if (!isTokenExpired(keys.refresh)) {
console.log('fetching access using refresh')
const response = await getAccessUsingRefresh(keys.refresh)
await AsyncStorage.setItem('keys', JSON.stringify(response))
console.log('UPDATED ONE')
return response
} else {
console.log('refresh expired, please login')
return null
}
}
} else {
console.log('access not available please login')
return null
}
}
function isTokenExpired (token) {
var decoded = jwt_decode(token)
if (decoded.exp < Date.now() / 1000) {
return true
} else {
return false
}
}
const setCredentials = async keys => {
try {
await AsyncStorage.setItem('keys', JSON.stringify(keys))
} catch (e) {
console.log(e)
}
}
const getCredentials = async () => {
try {
let credentials = await AsyncStorage.getItem('keys')
let cred = await getVerifiedKeys(JSON.parse(credentials))
if (credentials != null && cred != null) {
return cred
} else {
return null
}
} catch (e) {
console.log(e)
}
return null
}
That’s it friends, I hope you have followed this simple tutorial to automatically refresh tokens in React native. For more details, use the comment section below.
Thank you.
i m using this solution now but i found that the isValidTOken doesn’t check the referesh token and invoke an exception InvalidToken,
can u provide a solution to that i assume that she worked fine to u so any information can help cuz i’m really stuck
Where exactly you are stuck? any github link?
Thanks for blog bro.. It’s been weeks I’m searching for it finally found the refresh token part.
awesome bro