Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 3x 3x 2x 3x 2x 2x 2x 2x 2x 2x 3x 2x 2x 2x 2x 3x 3x 3x 2x 2x 2x 2x 2x 2x 2x 3x | import { getRandomBytes, TokenSigner, privateKeyToBase58Address, Json } from 'micro-stacks/crypto';
import { bytesToHex, fetchPrivate } from 'micro-stacks/common';
import { getPublicKey } from 'micro-stacks/crypto';
import type {
GaiaHubConfig,
GaiaAuthScope,
HubInfo,
ScopedGaiaTokenOptions,
GenerateGaiaHubConfigOptions,
} from './types';
/**
* Gaia takes the key 'domain' but 'path' makes more sense to someone implementing this
*/
function transformScopePath(scopes?: GaiaAuthScope[]) {
return scopes?.map(scope => ({ ...scope, domain: scope.path })) ?? null;
}
function makeDefaultScope(path: string): GaiaAuthScope {
return {
scope: 'putFilePrefix',
path,
};
}
export function makeScopedGaiaAuthTokenPayload(options: ScopedGaiaTokenOptions): Json {
const { hubInfo, privateKey, gaiaHubUrl, associationToken = null, scopes } = options;
const { challenge_text: gaiaChallenge } = hubInfo;
const iss = bytesToHex(getPublicKey(privateKey, true));
const salt = getRandomBytes(16).toString();
const payload: Json = {
gaiaChallenge,
hubUrl: gaiaHubUrl,
iss,
salt,
associationToken,
scopes: transformScopePath(scopes),
};
return payload;
}
/**
* Generate a scoped gaia auth token
*/
export async function makeScopedGaiaAuthToken(options: ScopedGaiaTokenOptions): Promise<string> {
const payload = makeScopedGaiaAuthTokenPayload(options);
const signer = new TokenSigner('ES256K', options.privateKey);
const token = await signer.sign(payload);
return `v1:${token}`;
}
export function makeScopedGaiaAuthTokenSync(options: ScopedGaiaTokenOptions): string {
const payload = makeScopedGaiaAuthTokenPayload(options);
const signer = new TokenSigner('ES256K', options.privateKey);
const token = signer.signSync(payload);
return `v1:${token}`;
}
const DEFAULT_PAYLOAD: HubInfo = {
challenge_text: '["gaiahub","0","storage2.blockstack.org","blockstack_storage_please_sign"]',
latest_auth_version: 'v1',
max_file_upload_size_megabytes: 20,
read_url_prefix: 'https://gaia.blockstack.org/hub/',
};
/**
* Generates a gaia hub config to share with someone so they can edit a file
*/
export async function generateGaiaHubConfig(
options: GenerateGaiaHubConfigOptions,
fetchHubInfo = false
): Promise<GaiaHubConfig> {
const { gaiaHubUrl, privateKey, associationToken, scopes } = options;
let hubInfo = DEFAULT_PAYLOAD;
Iif (fetchHubInfo) {
const response = await fetchPrivate(`${gaiaHubUrl}/hub_info`);
hubInfo = await response.json();
}
const { read_url_prefix: url_prefix, max_file_upload_size_megabytes } = hubInfo;
const token = await makeScopedGaiaAuthToken({
hubInfo,
privateKey,
gaiaHubUrl,
associationToken,
scopes,
});
const address = privateKeyToBase58Address(privateKey);
return {
address,
url_prefix,
token,
server: gaiaHubUrl,
max_file_upload_size_megabytes: max_file_upload_size_megabytes ?? 20,
};
}
// sync version
export function generateGaiaHubConfigSync(options: GenerateGaiaHubConfigOptions): GaiaHubConfig {
const { gaiaHubUrl, privateKey, associationToken, scopes } = options;
const hubInfo = DEFAULT_PAYLOAD;
const { read_url_prefix: url_prefix, max_file_upload_size_megabytes } = hubInfo;
const token = makeScopedGaiaAuthTokenSync({
hubInfo,
privateKey,
gaiaHubUrl,
associationToken,
scopes,
});
const address = privateKeyToBase58Address(privateKey);
return {
address,
url_prefix,
token,
server: gaiaHubUrl,
max_file_upload_size_megabytes: max_file_upload_size_megabytes ?? 20,
};
}
|