All files / src/wallet-sdk/auth index.ts

75.67% Statements 28/37
39.53% Branches 17/43
83.33% Functions 5/6
75% Lines 27/36

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          1x 1x   1x 2x     1x 1x     1x 1x 1x     1x 31x 31x 31x                                   1x                     1x 1x                       1x   1x 1x   1x     1x 1x 1x 1x                                   1x                           1x 1x    
import {
  encryptECIES,
  getPublicKey,
  publicKeyToStxAddress,
  TokenSigner,
} from 'micro-stacks/crypto';
import { bytesToHex, hexToBytes, utf8ToBytes } from 'micro-stacks/common';
 
export function makeDIDFromAddress(address: string) {
  return `did:btc-addr:${address}`;
}
 
export function nextMonth() {
  return new Date(new Date().setMonth(new Date().getMonth() + 1));
}
 
export function makeUUID4() {
  let d = new Date().getTime();
  Iif (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    d += performance.now(); // use high-precision timer if available
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = (d + Math.random() * 16) % 16 | 0;
    d = Math.floor(d / 16);
    return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
  });
}
 
interface MakeAuthResponseParams {
  privateKey: string;
  profile?: any;
  username?: string;
  metadata?: any;
  coreToken?: string;
  appPrivateKey?: string;
  expiresAt?: number;
  transitPublicKey?: string;
  hubUrl?: string;
  blockstackAPIUrl?: string;
  associationToken?: string;
}
 
export async function encryptPrivateKey(publicKey: string, privateKey: string): Promise<string> {
  const encryptedObj = await encryptECIES({
    content: hexToBytes(privateKey),
    publicKey,
    cipherTextEncoding: 'hex',
    wasString: false,
  });
  const encryptedJSON = JSON.stringify(encryptedObj);
  return bytesToHex(utf8ToBytes(encryptedJSON));
}
 
export async function makeAuthResponse(params: MakeAuthResponseParams): Promise<string> {
  let { expiresAt } = params;
  const {
    privateKey,
    appPrivateKey,
    coreToken,
    transitPublicKey,
    metadata,
    hubUrl,
    blockstackAPIUrl,
    associationToken,
    username,
    profile,
  } = params;
  /* Convert the private key to a public key to an issuer */
  const publicKey = bytesToHex(getPublicKey(privateKey));
  const address = publicKeyToStxAddress(publicKey);
 
  if (!expiresAt) expiresAt = nextMonth().getTime() as number;
 
  /* See if we should encrypt with the transit key */
  let privateKeyPayload = appPrivateKey;
  let coreTokenPayload = coreToken;
  let additionalProperties = {};
  Iif (appPrivateKey !== undefined && appPrivateKey !== null) {
    Iif (transitPublicKey !== undefined && transitPublicKey !== null) {
      privateKeyPayload = await encryptPrivateKey(transitPublicKey, appPrivateKey);
      Iif (coreToken !== undefined && coreToken !== null) {
        coreTokenPayload = await encryptPrivateKey(transitPublicKey, coreToken);
      }
    }
    additionalProperties = {
      email: metadata?.email ? metadata.email : null,
      profile_url: metadata?.profileUrl ? metadata.profileUrl : null,
      hubUrl,
      blockstackAPIUrl,
      associationToken,
      version: 'undefined',
    };
  }
 
  /* Create the payload */
  const payload = {
    jti: makeUUID4(),
    iat: Math.floor(new Date().getTime() / 1000), // JWT times are in seconds
    exp: Math.floor(expiresAt / 1000), // JWT times are in seconds
    iss: makeDIDFromAddress(address),
    private_key: privateKeyPayload ?? null,
    public_keys: [publicKey],
    profile: profile ?? null,
    username: username ?? null,
    core_token: coreTokenPayload ?? null,
    ...additionalProperties,
  };
 
  /* Sign and return the token */
  const tokenSigner = new TokenSigner('ES256k', privateKey);
  return tokenSigner.sign(payload);
}