Programmatical Share Access token

To ensure a seamless experience for our customers, we introduce the ability to programmatically auto-login to Password Protected External Share links. This will eliminate the need to manually enter the password and restrict access to the Confluence page exclusively through our system via iFrame to for example create an online user guide. To achieve that, we use JWT (JSON Web Token).

How to Use It?

1. Obtaining the Unlock Secret

Every share comes equipped with an Unlock Secret, which is essential for creating a share access token.
The Unlock Secret is a hex-encoded string, crafted from 32 randomly assorted bytes.

Accessing the Unlock Secret

  • Via the user interface:

    • Go to the share’s configurations page

    • Ensure that the ‘Protect link with password’ option is enabled under the security tab

    • Click on the (Show advanced) button

    • Here, you can locate and access the Unlock Secret

      Unlock secret is a read only value which can be copied, or regenerated.
  • Via the API:


2. Creating JWT

Once you have obtained the Unlock Secret, you can proceed to create a JWT to access shares without manually entering a password.

JWT stands for JSON Web Token. It's a compact and self-contained string that represents information between two parties. The token is composed of a header, a payload, and a signature. It's commonly used for authentication and secure data exchange.

JWT can be created in a programming language or using a website like:
https://dinochiesa.github.io/jwt/


JWT unlocking share: Required components

  • Standard header:

    { "alg": "HS256", "typ": "JWT" }
  • Payload:

    { "iss": "a8b63c1d-3a37-428b-c807-2ffeabbaa647", // UUID of the share "nbf": 1698133085, // Token valid from (Unix time in seconds) "exp": 1698133175 // Token expiration (Unix time in seconds) }

Unix time, also known as Epoch time, is a system for tracking time that counts the seconds that have passed since January 1, 1970, at 00:00:00 Coordinated Universal Time (UTC).

It's like a big stopwatch that started on January 1, 1970, and has been ticking every second since. This single number is easy for computers to read and work with.
https://www.unixtimestamp.com

Requirement:
The token’s expiration time must be within 90 seconds of its start validity time: exp - nbf <= 90

  • Signature:
    The Unlock Secret value, converted to bytes. In UI and API it is presented in hex format.
    For instance, when using a linked JWT creator website, select ‘Key encoding: HEX’ for this conversion.

 

Example of creating JWT using linked website:
With Java example of creating nbf and exp.

After filling data you have to click left arrow to generate JWT.


3. Using JWT Share Access token

Now, as you already have the JWT token, for a password-protected link, append ?unlock parameter to the URL as follows:

https://confluence.external-share.com/content/{UUID}?unlock={jwt}

On successful JWT validation, we are adding the same cookie as the page would have been accessed using password, but for security reasons, only for one hour. After that, Share Access Token will need to be regenerated.


Example of java code that generates JWT:

import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.awt.*; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.StringJoiner; class Main { public static void main(String[] args) throws Exception { // input var secretHex = "D90B5B3529ECCCDB67EF991E3C8CE079379EAF49803A5A88E257CBD31B8AD03D"; var shareUuid = "972faf56-7abf-4a15-bd1b-be70f6f8148d"; // main logic var secret = hexStringToByteArray(secretHex); var jwt = createJWT(secret, shareUuid); // output System.out.println(jwt); var url = "https://confluence.external-share.com/content/" + shareUuid + "?unlock=" + jwt; Desktop.getDesktop().browse(new URI(url)); } private static String createJWT(byte[] secret, String shareUUID) throws Exception { Map<String, Object> header = new HashMap<>(); header.put("alg", "HS256"); header.put("typ", "JWT"); var nbf = Instant.now(); var exp = nbf.plusSeconds(60); Map<String, Object> payload = new HashMap<>(); payload.put("nbf", nbf.getEpochSecond()); payload.put("exp", exp.getEpochSecond()); payload.put("iss", shareUUID); var headerBase64 = base64UrlEncode(toJson(header)); var payloadBase64 = base64UrlEncode(toJson(payload)); var signature = hmacSha256(secret, headerBase64 + "." + payloadBase64); return headerBase64 + "." + payloadBase64 + "." + signature; } private static String hmacSha256(byte[] secret, String data) throws Exception { var mac = Mac.getInstance("HmacSHA256"); var secretKeySpec = new SecretKeySpec(secret, "HmacSHA256"); mac.init(secretKeySpec); var hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); return base64UrlEncode(hash); } private static byte[] hexStringToByteArray(String s) { var len = s.length(); var data = new byte[len / 2]; for (var i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } private static String toJson(Map<String, Object> map) { var joiner = new StringJoiner(",", "{", "}"); for (var entry : map.entrySet()) { joiner.add("\"" + entry.getKey() + "\":\"" + entry.getValue().toString() + "\""); } return joiner.toString(); } private static String base64UrlEncode(String str) { return Base64.getUrlEncoder().withoutPadding().encodeToString(str.getBytes(StandardCharsets.UTF_8)); } private static String base64UrlEncode(byte[] bytes) { return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } }