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:
The “unlockSecret” field is returned as part of the response payload when invoking the GET method on the endpoint:
GET https://jira.external-share.com/webapi/share/{ SHARE_UUID }
This is detailed in our API documentation: API - External Share for Jira
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:
Online JWT tool
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
.
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://jira.external-share.com/issue/{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);
}
}