Docs - Ejemplos de API
Ejemplos completos y funcionales para las tareas más comunes de la API. Todos los ejemplos usan JavaScript puro y la API Web Crypto integrada, por lo que no se necesitan bibliotecas externas. Consulte la Documentación API para la referencia completa de los puntos de conexión.
Crear secreto
Un ejemplo de cómo cifrar un secreto y enviarlo a la API de password.link usando JavaScript. Guárdelo como archivo HTML, establezca la clave API y ábralo en un navegador.
Un script como este puede usarse en un ordenador local o en una red local para crear secretos cómodamente en nuestro servicio; sin embargo, nunca lo coloque en una ubicación pública sin la autenticación adecuada, porque contiene su clave API privada.
<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>
Crear secreto con adjunto
Este ejemplo muestra las partes específicas del adjunto. Asume que ya ha creado el texto cifrado del secreto y las partes de la contraseña como se muestra en el ejemplo Crear secreto anterior.
// 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;
}
Ver Secreto
Un ejemplo de cómo obtener, descifrar y mostrar un secreto usando JavaScript. Un script como este puede usarse para crear una página autoalojada para ver secretos.
El texto cifrado puede ser Web Crypto o SJCL heredado. El ejemplo siguiente gestiona ambos casos: las cargas SJCL tienen una clave ct. Para descifrar con SJCL, incluya sjcl.js en la página.
<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>