Search code examples
hyperledger-composerpassport-jwt

JWT Authentication fails in Hyperledger Composer


I've been following Caroline's blog to setup a multi-user composer rest server. So, I have two servers viz. Admin Server and the User Server.

As mentioned in the tutorial I:

  1. Started the Admin Server with no authentication and single user mode. I started this server with Admin's card.
  2. Started the User Server with passport JWT authentication in multi-user mode. I started this server with Admin's card as well.
  3. Created a User participant and generated a card for user from the admin server.
  4. In this step I'm trying to exchange the JWT Token with the User Server(#2) and I'm able to get the token as well.
  5. Ping user server with JWT Token. This results in "Error: Authorization Required".

I've followed Chris Ganga's blog for implementing JWT. My COMPOSER_PROVIDERS is:

COMPOSER_PROVIDERS='{
  "jwt": {
      "provider": "jwt",
      "module": "/home/composer/node_modules/custom-jwt.js",
      "secretOrKey": "somesecretkey",
      "authScheme": "saml",
      "successRedirect": "/",
      "failureRedirect":"/"
  }
}'

I'm exchanging JWT token for the first time from a Java service. To create a bearer token, I've written the following code:

public static String getBearerToken(String username, String id) throws UnsupportedEncodingException {
    return Jwts.builder()
            .claim("timestamp", System.currentTimeMillis())
              .claim("username", username)
              .claim("id", id)
              .signWith(
                SignatureAlgorithm.HS256,
                "somesecretkey".getBytes("UTF-8")
              ).compact();
}

With this, I'm able to get the token. Next, I use this token to import the card into the wallet on User server:

RestTemplate restTemplate = new RestTemplate();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("X-Access-Token",getAccess_token(participantEmail));
headers.set("x-api-key", userServerKey);

LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("card", new FileSystemResource(card));
params.add("name", participantEmail);

HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(CARD_IMPORT_URL);


ResponseEntity<String> responseEntity = restTemplate.exchange(builder.build().encode().toUri(),
                HttpMethod.POST, requestEntity, String.class);

However, this results in:

Unhandled error for request POST /api/Wallet/import: Error: Authorization Required

Finding 1 Generally, when we exchange the JWT for the first time using auth bearer, a db named "test" is created in mongo. This db contains three collections: accessToken, user and userIdentity. However, in my case when I exchange the token, no db is created in mongo. Any suggestions on how I can debug this?

Note: This whole setup was working perfectly fine until I decided to prune and restart the network from scratch.


Solution

  • The issue here was I was using the same name for mongodb container across all the hosts. And since all the containers connect to the swarm network, there were 8 mongodb containers with the same name which is quite a silly mistake. It caused an issue when the docker container of composer-rest-server tried to connect to the mongodb container. This connection is defined in COMPOSER_DATASOURCES variable. Using the different name for each of the mongo containers across all the hosts solved this issue.