Last updatedNov 22, 2019

How to make REST API endpoints available for JWT authentication

This page covers JWT (JSON Web Token) authentication in Confluence Server and Data Center.  To learn more about JWT token authentication in Atlassian Cloud, see Understanding JWT for Connect apps.

Confluence exposes REST resources with the /rest/api/ prefix.  Some REST resources are consumed using token authentication, mainly JWT (JSON Web Token)

JWT authentication can be problematic for customers who use a reverse proxy to redirect requests to an authentication server (for example a single sign-on gateway), as requests with tokens (as a header or part of query parameters) get rejected at the proxy and don't make it to Confluence Server for authentication. 

For this reason, from Confluence 7.2, we expose REST resources with both of these prefixes:

  • /rest/token-auth/api/
  • /rest/api/

and ask customers to allow requests with the /rest/token-auth/api/ prefix to pass through their proxy and allow Confluence to perform the security checks. 

Because we don't want to make all resources with the /rest/api/ prefix available without authentication we block all these resources from being available by default and allow plugins to cherry pick the resources they need to be available.

How to make a /rest/token-auth/api/ resource available

To make a resource available you will need to write a new filter that sets the TokenAuthWhitelistOK attribute to true. Here's an example:

1
2
3
4
5
6
7
8
9
public class TokenAuthWhitelistFilter extends AbstractHttpFilter {

    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        request.setAttribute("TokenAuthWhitelistOK", Boolean.TRUE);
        filterChain.doFilter(request, response);
    }

}

You then need to configure it on the path you would like to expose with location="after-encoding" and weight="99". This is because it needs to run before any other filter that will reject all requests with the /rest/token-auth/api/ prefix. 

Here's an example that allows two resources:

1
2
3
4
5
6
7
8
<servlet-filter key="TokenAuthWhitelistFilter" class="com.atlassian.confluence.plugins.previews.jwt.TokenAuthWhitelistFilter"
                    location="after-encoding"
                    weight="99"
    >
        <url-pattern>/rest/token-auth/api/content/*/child/attachment</url-pattern>
        <url-pattern>/rest/token-auth/api/content/*/history</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </servlet-filter>

Best practices

  • Don’t whitelist resources which will be available to anonymous users. If you can't avoid this, you will need to handle the security yourself, in the filter.
  • Make sure you only whitelist requests signed by your plugin.  In the example below, we use the subject field in the JWT structure to indicate our plugin signed it. 
  • You don't need to validate the token, this will be done by the JwtAuthFilter filter later. If the request has a JWT token but it's invalid, it will be rejected by JwtAuthFilter.

Here's an example showing how to extract the JWT subject without validating the token. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private Optional<String> getJWTSubject(final HttpServletRequest request) {
        try {
            // do the work and extract the subject,
            // the JWT filter will handle verification later
            String jwtString = JwtUtil.extractJwt(request);
            if (jwtString == null) {
                return Optional.empty();
            }
            return Optional.of(jwtReaderFactory
                    .getReader(jwtString)
                    .readUnverified(jwtString)
                    .getSubject());
        } catch (Exception e) {
            log.error( "JwtTokenService.getJWTSubject: error extracting jwt from request. {}", e);
            return Optional.empty();
        }

    }