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, }; } |