Esempi di API documentazione
Esempi completi e funzionanti per le attività API più comuni. Tutti gli esempi utilizzano JavaScript puro e la Web Crypto API integrata, quindi non sono necessarie librerie esterne. Consultate la Documentazione API per il riferimento completo degli endpoint.
Creare un segreto
Un esempio di crittografia di un segreto e del suo invio all'API di password.link usando JavaScript. Salvatelo come file HTML, impostate la chiave API e apritelo in un browser.
Uno script come questo può essere utilizzato su un computer locale o in una rete locale per creare comodamente segreti sul nostro servizio; tuttavia non mettetelo mai in una posizione pubblica senza un'autenticazione adeguata, perché contiene la vostra chiave API privata.
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<script>
// Set the base URL for the generated link
const LINK_BASE_URL = "https://some.site/secret.html";
// Set the API key here - a private API key is required
const PRIVATE_API_KEY = "private_key_abcd...";
// ----------------------------------- //
// A function for sending the secret to password.link API
async function sendToPasswordlinkApi(secret) {
// Create the public and private password parts
const passwordPartPublic = generateString(18);
const passwordPartPrivate = generateString(18);
// Create a Web Crypto compatible Base64 encoded ciphertext
const ciphertext = await encryptSecret(passwordPartPublic, passwordPartPrivate, secret);
// Send a request to the password.link API and process the result
const response = await fetch("https://password.link/api/secrets", {
method: "POST",
headers: {
"Authorization": "ApiKey " + PRIVATE_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
"ciphertext": ciphertext,
"password_part_private": btoa(passwordPartPrivate)
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
const secretUrl = LINK_BASE_URL + "?" + data.data.id + "#" + btoa(passwordPartPublic);
const meta = data.metadata;
document.getElementById("secret").textContent = "URL to secret: " + secretUrl;
document.getElementById("secret-meta").textContent =
"Total secrets: " + meta.secrets_total +
" | Usage: " + meta.secrets_usage +
" | Allowance: " + meta.secrets_allowance;
}
// A function for encrypting a secret with the Web Crypto API (AES-GCM),
// returns a Base64 encoded ciphertext JSON string
async function encryptSecret(passwordPartPublic, passwordPartPrivate, secret) {
const iterations = 10000;
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
// The encryption key is derived from the concatenated password parts
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(passwordPartPrivate + passwordPartPublic),
"PBKDF2",
false,
["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt: salt, iterations: iterations, hash: "SHA-256" },
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt"]
);
const cipher = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
new TextEncoder().encode(secret)
);
return btoa(JSON.stringify({
"cipher": bufferToBase64(cipher),
"iv": bufferToBase64(iv),
"salt": bufferToBase64(salt),
"iter": iterations
}));
}
// Base64 encode the bytes of an ArrayBuffer
function bufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
// A function for generating a random string. Rejection sampling keeps
// the character distribution unbiased.
function generateString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$|/!_+,.-?()[]{}<>&#^*=@";
const maxAcceptable = Math.floor(256 / chars.length) * chars.length;
let str = "";
while (str.length < length) {
const randomValues = new Uint8Array(length - str.length);
crypto.getRandomValues(randomValues);
for (let i = 0; i < randomValues.length && str.length < length; i++) {
if (randomValues[i] < maxAcceptable) {
str += chars.charAt(randomValues[i] % chars.length);
}
}
}
return str;
}
// Execute the send function when the button is clicked
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("secret-button").addEventListener("click", function(e) {
e.preventDefault();
const secret = document.getElementById("secret-input").value;
sendToPasswordlinkApi(secret).catch(function(error) {
document.getElementById("secret").textContent = "Error: " + error.message;
});
});
});
</script>
<style>
body {
font: 12px Arial;
}
.container {
max-width: 960px;
margin: 0 auto;
text-align: center;
margin-top: 60px;
}
#secret {
margin-top: 30px;
font-size: 18px;
}
#secret-meta {
margin-top: 40px;
}
.secret-form {
display: flex;
flex-flow: row wrap;
align-items: center;
flex-direction: vertical;
justify-content: center;
}
.secret-form input {
vertical-align: middle;
margin: 5px 10px 5px 0;
padding: 10px;
border: 1px solid #ddd;
width: 200px;
}
.secret-form button {
padding: 10px 20px;
border: 1px solid #ddd;
}
.secret-form button:hover {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h2>Encrypt and create a secret on password.link</h2>
<form class="secret-form">
<input id="secret-input" type="text" name="secret">
<button id="secret-button">Encrypt and create link</button>
</form>
<!-- This div will contain the link to the secret (or error) -->
<div id="secret"></div>
<div id="secret-meta"></div>
</div>
</body>
</html>
Creare un segreto con allegato
Questo esempio mostra le parti specifiche dell'allegato. Presuppone che tu abbia già creato il testo cifrato del segreto e le parti della password come mostrato nell'esempio Creare un segreto sopra.
// Set these first
const PASSWORDLINK_BASE_URL = "https://password.link";
const PRIVATE_API_KEY = "private_key_abcd...";
// These values come from the Create Secret example above
const ciphertext = "...";
const passwordPartPrivate = "...";
const passwordPartPublic = "...";
const passwordPartPrivateBase64 = btoa(passwordPartPrivate);
async function createSecretWithAttachment(file) {
const createResponse = await fetch(`${PASSWORDLINK_BASE_URL}/api/secrets`, {
method: "POST",
headers: {
"Authorization": "ApiKey " + PRIVATE_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
ciphertext: ciphertext,
password_part_private: passwordPartPrivateBase64,
attachment: {
file_name: file.name,
file_type: file.type || "application/octet-stream",
file_size: file.size
}
})
});
const createData = await createResponse.json();
if (!createResponse.ok) {
throw new Error(createData.error?.message || "Failed to create secret");
}
const attachmentUpload = createData.data.attachment.upload;
const fileDataUrl = await fileToDataUrl(file);
const encryptedFile = await encryptAttachment(
fileDataUrl,
passwordPartPrivate + passwordPartPublic
);
const formData = new FormData();
appendFormFields(formData, attachmentUpload.metadata);
appendFormFields(formData, attachmentUpload.fields);
formData.append("file", new Blob([encryptedFile], { type: "text/plain" }), "file.txt");
const uploadUrl = new URL(attachmentUpload.url, PASSWORDLINK_BASE_URL);
const uploadOptions = {
method: "POST",
body: formData
};
if (uploadUrl.origin === new URL(PASSWORDLINK_BASE_URL).origin) {
uploadOptions.headers = {
"Authorization": "ApiKey " + PRIVATE_API_KEY
};
} else {
uploadOptions.mode = "no-cors";
}
const uploadResponse = await fetch(uploadUrl.toString(), uploadOptions);
if (uploadResponse.status !== 0 && !uploadResponse.ok) {
throw new Error("Failed to upload attachment");
}
return createData.data.id;
}
function appendFormFields(formData, fields) {
if (!fields) {
return;
}
Object.entries(fields).forEach(([key, value]) => {
formData.append(key, value);
});
}
function fileToDataUrl(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
async function encryptAttachment(plaintext, password) {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
"PBKDF2",
false,
["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt: salt, iterations: 10000, hash: "SHA-256" },
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt"]
);
const cipher = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
new TextEncoder().encode(plaintext)
);
return btoa(JSON.stringify({
cipher: bufferToString(cipher),
iv: bufferToString(iv),
salt: bufferToString(salt)
}));
}
function bufferToString(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return binary;
}
Visualizza il Segreto
Un esempio di recupero, decrittografia e visualizzazione di un segreto usando JavaScript. Uno script come questo può essere utilizzato per creare una pagina self-hosted di visualizzazione del segreto.
Il testo cifrato può essere Web Crypto o SJCL legacy. L'esempio seguente gestisce entrambi i casi: i payload SJCL hanno una chiave ct. Per la decrittografia SJCL, includere sjcl.js nella pagina.
<html>
<head>
<style>
body {
font: 12px Arial;
}
.container {
max-width: 960px;
margin: 0 auto;
text-align: center;
margin-top: 60px;
}
</style>
</head>
<body>
<div class="container">
<h2>Here's the secret</h2>
<!-- This div will contain the decrypted secret (or error) -->
<div id="secret"></div>
</div>
<script>
(function() {
// Set the API key here - a public API key is required
const PUBLIC_API_KEY = "public_key_abcd...";
// ----------------------------------- //
// Get the secret ID from the query string part of the URL
// E.g. https://some.site/secret.html?secret_id
const secretId = location.search.substr(1);
// Get the public password (encryption key) part from the hash part of the URL, in Base64 format
// E.g. https://some.site/secret.html?secret_id#password_part_public
const passwordPartPublic = location.hash.substr(1);
// Fetch the secret from the password.link API and decrypt it
async function fetchAndDecryptSecret() {
const response = await fetch("https://password.link/api/secrets/" + secretId, {
headers: {
"Authorization": "ApiKey " + PUBLIC_API_KEY
}
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
const secret = data.data;
const decryptedSecret = await decryptSecret(
passwordPartPublic,
secret.password_part_private,
secret.ciphertext
);
// Set the content of the element with id "secret" to the decrypted secret
// Use .textContent to avoid XSS
document.getElementById("secret").textContent = decryptedSecret;
}
// Decrypts a ciphertext received from the password.link API.
// All parameters are in Base64 format.
//
// Legacy SJCL payloads contain a "ct" key and need sjcl.js:
// https://github.com/bitwiseshiftleft/sjcl
async function decryptSecret(passwordPartPublic, passwordPartPrivate, ciphertext) {
const payload = JSON.parse(atob(ciphertext));
const password = atob(passwordPartPrivate) + atob(passwordPartPublic);
if (Object.prototype.hasOwnProperty.call(payload, "ct")) {
return sjcl.decrypt(password, atob(ciphertext));
}
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
"PBKDF2",
false,
["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: base64ToBytes(payload.salt),
iterations: payload.iter || 10000,
hash: "SHA-256"
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["decrypt"]
);
const plaintext = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: base64ToBytes(payload.iv) },
key,
base64ToBytes(payload.cipher)
);
return new TextDecoder().decode(plaintext);
}
// Decode a Base64 string into a byte array
function base64ToBytes(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
fetchAndDecryptSecret().catch(function(error) {
document.getElementById("secret").textContent = "Error: " + error.message;
});
})();
</script>
</body>
</html>