ACE + React (create-react-app)

TL;DR, how would you go about configuring ACE and React?

Does anyone uses atlassian-connect-express framework with create-react-app to create add-on’s? Single page add-on’s?
If so, how you deal with things such as expiration of JWT token for making requests to add-on service? (Or it should be done like in this post described)
Also how you deal with embeding some usefull context variables provided to hbs templates like title, addonKey etc?
How would you use webpack-dev-server? If use at all

What other problems you faced with?

3 Likes

I’ve considered using AtlasKit which is based on create-react-app and have found fullstackreact for the first step, but have not gone any further.

In my Angular based single page add-on, baked with ACE, so with hbs, I use authbeat to periodically refresh the token as long as user stays on the add-on page. Other context variables are passed via hbs too. E.g. layhout.hbs:

<head>
  <script type="text/javascript">
      window.atlToken = '{{token}}';
  </script>
</head>
<body>
  <ak-grid layout="fluid" id="content" ng-view>
    <context items='{"hostBaseUrl": "{{hostBaseUrl}}", "dashboardId":"{{dashboardId}}", "dashboardItemId":"{{dashboardItemId}}", "license": "{{license}}"' />
  </ak-grid>
</body>

However in pure static page, all this information can be retrieved with javascript from the window.location.search. And if you’d like to talk to backend you’d need the following too: https://bitbucket.org/atlassian/atlassian-connect-express/pull-requests/125/really-skip-qsh-verification-in/diff

Sorry, I have no experience with webpack-dev-server, though at first glance it looks more convenient than fullstackreact in dev mode.

1 Like

Hi @azhdanov, thanks for sharing :slight_smile:
fullstackreact is using webpack dev server with proxy to backend API, it works great, but i haven’t figured out how to use it with ACE, since it just serves html with js:C
Anyway thanks for your reply!

Hi @alexter_pr,

Ah, did not realize it’s the same thing, thanks for correction. So, you can use ACE in server/backend part of the fullstackreact, you might have got it, though.

Thank you.

Nah, looking at it now i think webpack-dev-server won’t work since static files serverd through it and we need to serve them through backend server with HBS files, so for development it seems like it’s not gonna work.
But i’m gonna try SSR with react soon, i think that might fit well, but i maybe wrong, never tried SSR

Also you previously mentioned authbeat, is it npm package or some library? Can you link it? I seem not to find it

authbeat is just periodic $http.get(’/authbeat’), with corresponding backend route:

  app.get('/authbeat', addon.checkValidToken(), function (req, res) {
    res.send();
  });

and updating auth header for subsequent requests.

In angular I do it like this:

    $provide.factory('authHttpInterceptor', ['$injector', '$q', function($injector, $q) {
        return {
            response: function(response) {
                var xAcpt = response.headers('X-acpt');
                if (xAcpt) {
                    window.atlToken = xAcpt;
                    // update authentication token
                    $httpProvider.defaults.headers.common['Authorization'] = "JWT " + window.atlToken;
                }
                return response;
            }
        }
    }]);

Also, unless my pull-request above is accepted, I have a hack in mind to make first JWT token pass checkValidToken by substituting req.url and req.query, like the following:

    // Use referer url for authentication first time when static pages
    var url = require('url');
    var rewriteUrl = function(req, res, next) {
      if (!req.headers.authorization) {
        var referer = url.parse(req.headers.referer, true); // TODO: param instead
        req.originalUrlBak = req.originalUrl;
        delete req.originalUrl;
        req.url = referer.pathname;
        req.query = referer.query;
      }
      next();
    };

    app.get('/api/configuration/:key', rewriteUrl, addon.checkValidToken(), function(req, res) {

Note, it’s better not to rely on Rererer header, but instead pass your own, say X-Location header.

While probably not the “react” way - I send the initial jwt token down in the body as in . Then outside of react I go old school and use the XMLHTTPrequest object to refresh the token every 10 minutes. Within the React items - I just retreive the jwt token from the body attribute whenever I need to refresh content. It’s not the most elegant - but it works :wink:

If you planning to develop for server and cloud . You might refer this example.
They give talk during atlas camp 2016.

Here another example.
https://www.atlassian.com/atlascamp/2017/archives/advanced-techniques/react-for-re-use-creating-ui-components-with-confluence-connect

Correct me if I am wrong, but if someone manages to sniff 1 jwt token, he can use it forever to get new tokens from /authbeat ?

@sanjeevan Yes, though isn’t sniffing once generally enough at all?

@azhdanov Well, I don’t think so. I try to image what the big sites such as google, fb, twitter etc does when they have a single page application. I don’t think any of the are vulnerable to replay-attacks. I mean if you leave your computer on with fb chat for 1 day. I think you have to refresh the entire page (this generating new tokens ) to continue chatting. They just have longer expiry time on their tokens than altassian which is few minutes!

Btw, since it may help others in this thread, my solution to this token expiry problem is as following:
The intial JWT token is sent to my web application, which verifies it, and then issues a new JWT token with 2 days expiry time.
The new JWT token, lets call it JWT_Internal, can then be used to call on my REST api. It can NOT be used to authorize or authenticate with anything else than my web application APIs, thus protecting the atlassian data and security mechanism.

Atlassian <—JWT—> Web application <----JWT_INTERNAL (2 days expiry)–> Browser

As far as I know many applications use pair token/refresh_token latter with longer lifetime and when token expired application send refresh_token to renew token.

Well, I have to leave this question for Atlassian-developers to answer because I know they are very strict about it after the recent hacking.

Hi @azhdanov
Can u tell me the same solution in react? For the Server side, I am using Spring boot there I added this Property.
#Adding expiration time for jwt
atlassian.connect.jwt-expiration-time: 15
Is this enough or I need to do something in react .
Suggestions…

It looks enough, and if you’d like to refresh it on clientside periodically before it expires, you need to work out similar authbeat (with setTimeout/clearTimeout at the lowest level), sorry can’t help you with the code.

1 Like

I am using atlassian-connect-spring-boot on the backend and react on the client side. I handled it by adding a rest end point for heartbeat which returns a new fresh token. On the client side before the expiration of token I trigger the /heartbeat and refresh the token.

Spring Controller

    @GetMapping({"/heartbeat"})
    @ResponseBody
    public Map<String, String> refreshJWT(Model model){
      return Collections.singletonMap("token", ""+model.getAttribute("atlassianConnectToken"));
    }

On Client Side

    const refreshJWT = () => {
      return fetch('/heartbeat',
        {
          headers: this.getAuthJWTHeader()
        })
        .then(res => {
          if (res.status === 200) {
            res.json().then(json => {
              const token = json['token'];
              Utils.setJWTToken(token);
            });
          };
        });
    }
    setInterval(refreshJWT, 30000);