Add tutorial documentation

This commit is contained in:
Maria Shodunke
2025-04-08 13:59:28 +01:00
parent 3fafeb5292
commit 0f9338c0b9
10 changed files with 597 additions and 260 deletions

View File

@@ -1,12 +1,11 @@
import {
strToHex,
decodeHex,
datetimeToRippleTime,
rippleTimeToDatetime,
} from "./utils.js";
import { isValidClassicAddress } from "xrpl";
isoTimeToRippleTime,
rippleTimeToISOTime,
isValidClassicAddress,
} from "xrpl";
import { stringToHex } from "@xrplf/isomorphic/dist/utils/index.js";
import { decodeHex } from "./decode_hex.js";
import { ValueError } from "./errors.js";
// Regex constants
@@ -15,7 +14,6 @@ const URI_REGEX = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]{1,256}$/;
/**
* Validate credential request.
*
* This function performs parameter validation. Validated fields:
* - subject (required): the subject of the credential, as a classic address
* - credential (required): the credential type, in human-readable (ASCII) chars
@@ -35,19 +33,16 @@ export function validateCredentialRequest({ subject, credential, uri, expiration
if (typeof credential !== "string") {
throw new ValueError("Must provide a string 'credential' field");
}
/*
Checks if the specified credential type is one that this service issues.
XRPL credential types can be any binary data; this service issues
any credential that can be encoded from the following ASCII chars:
alphanumeric characters, underscore, period, and dash.
(min length 1, max 64)
You might want to further limit the credential types, depending on your
use case; for example, you might only issue one specific credential type.
*/
if (!CREDENTIAL_REGEX.test(credential)) {
/**
* Checks if the specified credential type is one that this service issues.
* XRPL credential types can be any binary data; this service issues
* any credential that can be encoded from the following ASCII chars:
* alphanumeric characters, underscore, period, and dash. (min length 1, max 64)
*
* You might want to further limit the credential types, depending on your
* use case; for example, you might only issue one specific credential type.
*/
throw new ValueError(`credential not allowed: '${credential}'.`);
}
@@ -76,12 +71,10 @@ export function validateCredentialRequest({ subject, credential, uri, expiration
// Validate and parse expiration
let parsedExpiration;
if (expiration !== undefined) {
if (typeof expiration === "string") {
parsedExpiration = new Date(expiration);
} else {
if (typeof expiration !== "string") {
throw new ValueError(`Unsupported expiration format: ${typeof expiration}`);
}
parsedExpiration = new Date(expiration);
if (isNaN(parsedExpiration.getTime())) {
throw new ValueError(`Invalid expiration date: ${expiration}`);
}
@@ -95,20 +88,41 @@ export function validateCredentialRequest({ subject, credential, uri, expiration
};
}
/**
* As a credential issuer, you typically need to verify some information
* about someone before you issue them a credential. For this example,
* the user passes relevant information in a documents field of the API request.
* The documents are kept confidential, off-chain.
*/
export function verifyDocuments({documents}) {
/*
This is where you would check the user's documents to see if you
should issue the requested Credential to them.
Depending on the type of credentials your service needs, you might
need to implement different types of checks here.
// Convert an XRPL ledger entry into a usable credential object
export function credentialFromXrpl(entry) {
const { Subject, CredentialType, URI, Expiration, Flags } = entry;
return {
subject: Subject,
credential: decodeHex(CredentialType),
uri: URI ? decodeHex(URI) : undefined,
expiration: Expiration ? rippleTimeToISOTime(Expiration) : undefined,
accepted: Boolean(Flags & 0x00010000), // lsfAccepted
};
}
// Convert to an object in a format closer to the XRP Ledger representation
export function credentialToXrpl(cred) {
// Credential type and URI are hexadecimal;
// Expiration, if present, is in seconds since the Ripple Epoch.
return {
subject: cred.subject,
credential: stringToHex(cred.credential),
uri: cred.uri ? stringToHex(cred.uri) : undefined,
expiration: cred.expiration
? isoTimeToRippleTime(cred.expiration)
: undefined,
};
}
export function verifyDocuments({ documents }) {
/**
* This is where you would check the user's documents to see if you
* should issue the requested Credential to them.
* Depending on the type of credentials your service needs, you might
* need to implement different types of checks here.
*/
if (typeof documents !== 'object' || Object.keys(documents).length === 0) {
if (typeof documents !== "object" || Object.keys(documents).length === 0) {
throw new ValueError("you must provide a non-empty 'documents' field");
}
@@ -123,50 +137,3 @@ export function verifyDocuments({documents}) {
throw new ValueError("reason must include 'please'");
}
}
/**
* Convert to a Credential object in a format closer to the XRP Ledger representation.
* Credential type and URI are hexadecimal;
* Expiration, if present, is in seconds since the Ripple Epoch.
*/
export function credentialToXrpl(cred) {
return {
subject: cred.subject,
credential: strToHex(cred.credential),
uri: cred.uri ? strToHex(cred.uri) : undefined,
expiration: cred.expiration ? datetimeToRippleTime(cred.expiration) : undefined,
};
}
// Convert an XRPL ledger entry into a usable credential object
export function parseCredentialFromXrpl(entry) {
const { Subject, CredentialType, URI, Expiration, Flags } = entry;
if (!Subject || !CredentialType) {
throw new Error("Missing required fields from XRPL credential entry");
}
return {
subject: Subject,
credential: decodeHex(CredentialType),
uri: URI ? decodeHex(URI) : undefined,
expiration: Expiration ? rippleTimeToDatetime(Expiration) : undefined,
accepted: Boolean(Flags & 0x00010000), // lsfAccepted
};
}
/**
* Convert a credential object into API-friendly JSON
*/
export function serializeCredential(cred) {
const result = {
subject: cred.subject,
credential: cred.credential,
};
if (cred.uri) result.uri = cred.uri;
if (cred.expiration) result.expiration = cred.expiration.toISOString();
if (cred.accepted !== undefined) result.accepted = cred.accepted;
return result;
}