{"id":139,"date":"2018-08-02T14:27:32","date_gmt":"2018-08-02T14:27:32","guid":{"rendered":"http:\/\/www.visionmate.se\/en\/?p=139"},"modified":"2018-08-03T13:35:47","modified_gmt":"2018-08-03T13:35:47","slug":"using-react-with-jwt","status":"publish","type":"post","link":"https:\/\/www.visionmate.se\/en\/2018\/08\/02\/using-react-with-jwt\/","title":{"rendered":"Using React with JWT"},"content":{"rendered":"<p>Over the last few years there has been a huge change in how we use software. The focus has shifted from desktop to lightweight cloud-based\u00a0application. Companies like Facebook and Google have been at the forefront of advancing these technologies.<\/p>\n<p>Google has introduced Angular, while Facebook is responsible for React. At Visionmate we have chosen the latter to build our applictions.\u00a0In this article, I&#8217;m describing how we approach security.<\/p>\n<p>JSON Web Token (JWT) is a popular way to secure your application. It is described in detail in RFC 7519 standard.<\/p>\n<p>The main advantage of using a JWT token is that you can inject extra information, which can be only decoded by your back-end code.<\/p>\n<p>At Visionmate we employ Grails as a back-end framework and use Spring Security REST plugin to generate and handle JWT tokens. This is not covered in this article, but we are planning to write another one about it.<\/p>\n<p>In this example, I&#8217;m using Redux to handle application state, which includes authentication data and token information.<\/p>\n<p>The login function returns not only the token itself, but also assigned user roles. This tells the application, what kind of rights the user has \u2013 for example whether they can edit certain documents or have access to the user management panel.<\/p>\n<p><strong>The API login function (all calls are made using axios):<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function login(user) {\r\n        const config = {\r\n            headers: {\r\n                'Accept': 'application\/json',\r\n                'Content-Type': 'application\/json'\r\n            }\r\n        };\r\n        return axios.post(`${SERVER_URL}\/api\/login`, user, config)       \r\n    }<\/pre>\n<p><strong>The login action:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">export function login(username, password) {\r\n    const user = {\r\n        username,\r\n        password\r\n    };\r\n    return (dispatch) =&gt; {\r\n        ApiConnector.login(user)\r\n            .then(response =&gt; {\r\n                const data = response.data\r\n                const user = {\r\n                    access_token: data.access_token,\r\n                    refresh_token: data.refresh_token,\r\n                    roles: data.roles,\r\n                    username: data.username,\r\n                };\r\n                sessionStorage.setItem('user', JSON.stringify(user));\r\n                dispatch({\r\n                    type: LOGIN,\r\n                    user\r\n                });\r\n            })\r\n            .catch((error) =&gt; {\r\n                console.log(error)\r\n            })\r\n    }\r\n}<\/pre>\n<p>Since we are using an asynchronous request, redux-thunk middleware is used to handle it.\u00a0As you can see, the response includes not only access and refresh tokens, but also user roles, which can be later used to for example conditionally render additional panels on the dashboard.<\/p>\n<p>The token is then stored in either session storage or local storage \u2013 depending on the application requirements, so the user doesn\u2019t have to login each time a browser is refreshed.<\/p>\n<p><strong>\u00a0The authentication data is then stored in a reducer:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">export default function (state = {loggedIn: false}, action) {\r\n    switch(action.type) {        \r\n        case LOGIN:\r\n            return {...state,\r\n                user: action.user,                 \r\n                loggedIn: true                \r\n            };\r\n        case LOGOUT:\r\n            return {...state,\r\n                user: {},\r\n                loggedIn: false\r\n            };\r\n        default:\r\n            return state\r\n    }\r\n}<\/pre>\n<p>JWT token has to be included in every call. Therefore, it can be stored in a separate JavaScript file to be retrieved easily later on.<\/p>\n<p><strong>In this case, it&#8217;s stored in headers.js:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">export default () =&gt; {\r\n    return {\r\n        'Content-Type': 'application\/json',\r\n        'Authorization': `Bearer ${sessionStorage.getItem('user') ? JSON.parse(sessionStorage.getItem('user')).access_token : null}`\r\n    }\r\n}<\/pre>\n<p><strong>The headers() function is then added to every API call as shown below.<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">import headers from \"..\/security\/headers\";\r\n\r\nfunction get(target, params) {\r\n       const config = {\r\n           headers: headers(),\r\n           params\r\n       }\r\n       return axios.get(`${SERVER_URL}\/api\/${target}`, config)\r\n   }<\/pre>\n<p>When the access token expires and is invalid, the user does not need to log in again &#8211; it&#8217;s enough to use the refresh token to obtain a new valid access token.<\/p>\n<p><strong>Refresh token function:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function refreshToken() {\r\n        const config = {\r\n            headers: {\r\n                'Accept': 'application\/json',\r\n                'Content-Type': 'application\/x-www-form-urlencoded'\r\n            }\r\n        };\r\n        const body = {\r\n            grant_type: \"refresh_token\",\r\n            refresh_token: JSON.parse(sessionStorage.user).refresh_token\r\n        };\r\n        axios.post(`${SERVER_URL}\/oauth\/access_token`, qs.stringify(body), config)\r\n            .then(response =&gt; {\r\n                const data = response.data\r\n                const user = {\r\n                    access_token: data.access_token,\r\n                    refresh_token: data.refresh_token,\r\n                    roles: data.roles,\r\n                    username: data.username                    \r\n                };\r\n                sessionStorage.setItem('user', JSON.stringify(user));\r\n                store.dispatch({\r\n                    type: REFRESH_TOKEN,\r\n                    user\r\n                })\r\n            })\r\n    }<\/pre>\n<p>In order to log the user out, simply delete the token from session\/local storage as shown in the action below.<\/p>\n<p><strong>Logout action:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">export function logout() {\r\n    sessionStorage.removeItem('user');\r\n    return {\r\n        type: LOGOUT\r\n    }\r\n}<\/pre>\n<p><strong>Summary<\/strong><\/p>\n<p>As shown in this article, in order to use JWT with your application, certain steps need to be followed. This example can be used as a template to add more functionality and expand your application.<\/p>\n<p>Libraries used: redux, redux-thunk and axios.<\/p>\n<p>Feel free to ask questions and share your comments!<\/p>\n<p>Origin: <a href=\"https:\/\/www.linkedin.com\/pulse\/using-react-jwt-radek-kaczorowski\/\" target=\"_blank\" rel=\"noopener\">www.linkedin.com<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Over the last few years there has been a huge change in how we use&#8230;<\/p>\n","protected":false},"author":1,"featured_media":140,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[5,4],"class_list":["post-139","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-jwt","tag-react"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/posts\/139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/comments?post=139"}],"version-history":[{"count":5,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/posts\/139\/revisions"}],"predecessor-version":[{"id":161,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/posts\/139\/revisions\/161"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/media\/140"}],"wp:attachment":[{"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/media?parent=139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/categories?post=139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.visionmate.se\/en\/wp-json\/wp\/v2\/tags?post=139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}