UNPKG

edge-core-js

Version:

Edge account & wallet management library

444 lines (377 loc) 12.6 kB
// @flow import type { Cleaner } from "cleaners"; import { asArray, asBoolean, asCodec, asDate, asEither, asNumber, asObject, asOptional, asString, asUnknown, asValue, uncleaner, } from "cleaners"; import { base16, base32, base64 } from "rfc4648"; import type { ChallengeErrorPayload, ChangeOtpPayload, ChangePasswordPayload, ChangePin2IdPayload, ChangePin2Payload, ChangeRecovery2IdPayload, ChangeRecovery2Payload, ChangeSecretPayload, ChangeUsernamePayload, ChangeVouchersPayload, CreateChallengePayload, CreateKeysPayload, CreateLoginPayload, EdgeBox, EdgeKeyBox, EdgeLobbyReply, EdgeLobbyRequest, EdgeSnrp, LobbyPayload, LoginPayload, LoginRequestBody, LoginResponseBody, MessagesPayload, OtpErrorPayload, OtpResetPayload, PasswordErrorPayload, Recovery2InfoPayload, UsernameInfoPayload, } from "./server-types"; import type { EdgePendingVoucher } from "./types"; /** * A string of hex-encoded binary data. */ export const asBase16: Cleaner<Uint8Array> = asCodec( (raw) => base16.parse(asString(raw)), (clean) => base16.stringify(clean).toLowerCase(), ); /** * A string of base32-encoded binary data. */ export const asBase32: Cleaner<Uint8Array> = asCodec( (raw) => base32.parse(asString(raw), { loose: true }), (clean) => base32.stringify(clean, { pad: false }), ); /** * A string of base64-encoded binary data. */ export const asBase64: Cleaner<Uint8Array> = asCodec( (raw) => base64.parse(asString(raw)), (clean) => base64.stringify(clean), ); // --------------------------------------------------------------------- // public Edge types // --------------------------------------------------------------------- export const asEdgePendingVoucher: Cleaner<EdgePendingVoucher> = asObject({ voucherId: asString, activates: asDate, created: asDate, ip: asString, ipDescription: asString, deviceDescription: asOptional(asString), }); // --------------------------------------------------------------------- // internal Edge types // --------------------------------------------------------------------- export const asEdgeBox: Cleaner<EdgeBox> = asObject({ encryptionType: asNumber, data_base64: asBase64, iv_hex: asBase16, }); export const asEdgeKeyBox: Cleaner<EdgeKeyBox> = asObject({ created: asOptional(asDate), data_base64: asBase64, encryptionType: asNumber, iv_hex: asBase16, }); export const asEdgeSnrp: Cleaner<EdgeSnrp> = asObject({ salt_hex: asBase16, n: asNumber, r: asNumber, p: asNumber, }); export const asEdgeLobbyRequest: Cleaner<EdgeLobbyRequest> = asObject({ loginRequest: asOptional(asObject({ appId: asString }).withRest), publicKey: asBase64, timeout: asOptional(asNumber), }).withRest; export const asEdgeLobbyReply: Cleaner<EdgeLobbyReply> = asObject({ publicKey: asBase64, box: asEdgeBox, }); /** * An array of base64-encoded hashed recovery answers. */ export const asRecovery2Auth: Cleaner<Uint8Array[]> = asArray(asBase64); // --------------------------------------------------------------------- // top-level request & response bodies // --------------------------------------------------------------------- export const asLoginRequestBody: Cleaner<LoginRequestBody> = asObject({ // The request payload: data: asUnknown, // Common fields for all login methods: challengeId: asOptional(asString), deviceDescription: asOptional(asString), otp: asOptional(asString), syncToken: asOptional(asString), voucherId: asOptional(asString), voucherAuth: asOptional(asBase64), // Secret-key login: loginId: asOptional(asBase64), loginAuth: asOptional(asBase64), // Password login: userId: asOptional(asBase64), passwordAuth: asOptional(asBase64), // PIN login: pin2Id: asOptional(asBase64), pin2Auth: asOptional(asBase64), // Recovery login: recovery2Id: asOptional(asBase64), recovery2Auth: asOptional(asRecovery2Auth), // Messages: loginIds: asOptional(asArray(asBase64)), // OTP reset: otpResetAuth: asOptional(asString), // Legacy: did: asOptional(asString), l1: asOptional(asBase64), lp1: asOptional(asBase64), lpin1: asOptional(asBase64), lra1: asOptional(asBase64), recoveryAuth: asOptional(asBase64), // lra1 }); export const asLoginResponseBody: Cleaner<LoginResponseBody> = asObject({ // The response payload: results: asOptional(asUnknown), // What type of response is this (success or failure)?: status_code: asNumber, message: asString, }); // --------------------------------------------------------------------- // request payloads // --------------------------------------------------------------------- export const asChangeOtpPayload: Cleaner<ChangeOtpPayload> = asObject({ otpTimeout: asOptional(asNumber, 7 * 24 * 60 * 60), // seconds otpKey: asBase32, }); export const asChangePasswordPayload: Cleaner<ChangePasswordPayload> = asObject( { passwordAuth: asBase64, passwordAuthBox: asEdgeBox, passwordAuthSnrp: asEdgeSnrp, passwordBox: asEdgeBox, passwordKeySnrp: asEdgeSnrp, }, ); export const asChangePin2IdPayload: Cleaner<ChangePin2IdPayload> = asObject({ pin2Id: asBase64, }); export const asChangePin2Payload: Cleaner<ChangePin2Payload> = asObject({ pin2Id: asOptional(asBase64), pin2Auth: asOptional(asBase64), pin2Box: asOptional(asEdgeBox), pin2KeyBox: asOptional(asEdgeBox), pin2TextBox: asEdgeBox, }); export const asChangeRecovery2IdPayload: Cleaner<ChangeRecovery2IdPayload> = asObject({ recovery2Id: asBase64, }); export const asChangeRecovery2Payload: Cleaner<ChangeRecovery2Payload> = asObject({ recovery2Id: asBase64, recovery2Auth: asRecovery2Auth, recovery2Box: asEdgeBox, recovery2KeyBox: asEdgeBox, question2Box: asEdgeBox, }); export const asChangeSecretPayload: Cleaner<ChangeSecretPayload> = asObject({ loginAuthBox: asEdgeBox, loginAuth: asBase64, }); export const asChangeUsernamePayload: Cleaner<ChangeUsernamePayload> = asObject( { userId: asBase64, userTextBox: asEdgeBox, }, ); export const asChangeVouchersPayload: Cleaner<ChangeVouchersPayload> = asObject( { approvedVouchers: asOptional(asArray(asString)), rejectedVouchers: asOptional(asArray(asString)), }, ); export const asCreateKeysPayload: Cleaner<CreateKeysPayload> = asObject({ keyBoxes: asArray(asEdgeBox), newSyncKeys: asOptional(asArray(asString), () => []), }); export const asCreateLoginPayload: Cleaner<CreateLoginPayload> = asObject({ appId: asString, loginId: asBase64, parentBox: asOptional(asEdgeBox), }); // --------------------------------------------------------------------- // response payloads // --------------------------------------------------------------------- export const asChallengeErrorPayload: Cleaner<ChallengeErrorPayload> = asObject( { challengeId: asString, challengeUri: asString, }, ); export const asCreateChallengePayload: Cleaner<CreateChallengePayload> = asObject({ challengeId: asString, challengeUri: asOptional(asString), }); export const asLobbyPayload: Cleaner<LobbyPayload> = asObject({ request: asEdgeLobbyRequest, replies: asArray(asEdgeLobbyReply), }); const asTrue = asValue(true); export const asLoginPayload: Cleaner<LoginPayload> = asObject({ // Identity: appId: asString, created: asDate, loginId: asBase64, syncToken: asOptional(asString), // Nested logins: children: asOptional(asArray((raw) => asLoginPayload(raw))), parentBox: asOptional(asEdgeBox), // 2-factor login: otpKey: asOptional(asEither(asTrue, asBase32)), otpResetDate: asOptional(asDate), otpTimeout: asOptional(asNumber), // Password login: passwordAuthBox: asOptional(asEdgeBox), passwordAuthSnrp: asOptional(asEdgeSnrp), passwordBox: asOptional(asEither(asTrue, asEdgeBox)), passwordKeySnrp: asOptional(asEdgeSnrp), // PIN v2 login: pin2Box: asOptional(asEither(asTrue, asEdgeBox)), pin2KeyBox: asOptional(asEdgeBox), pin2TextBox: asOptional(asEdgeBox), // Recovery v2 login: question2Box: asOptional(asEdgeBox), recovery2Box: asOptional(asEither(asTrue, asEdgeBox)), recovery2KeyBox: asOptional(asEdgeBox), // Secret-key login: loginAuthBox: asOptional(asEdgeBox), // Username: userId: asOptional(asBase64), userTextBox: asOptional(asEdgeBox), // Voucher login: pendingVouchers: asOptional(asArray(asEdgePendingVoucher), () => []), // Resources: keyBoxes: asOptional(asArray(asEdgeKeyBox)), mnemonicBox: asOptional(asEdgeBox), rootKeyBox: asOptional(asEdgeBox), syncKeyBox: asOptional(asEdgeBox), }); export const asMessagesPayload: Cleaner<MessagesPayload> = asArray( asObject({ loginId: asBase64, otpResetPending: asOptional(asBoolean, false), pendingVouchers: asOptional(asArray(asEdgePendingVoucher), () => []), recovery2Corrupt: asOptional(asBoolean, false), }), ); export const asOtpErrorPayload: Cleaner<OtpErrorPayload> = asObject({ login_id: asOptional(asBase64), otp_reset_auth: asOptional(asString), otp_timeout_date: asOptional(asDate), reason: asOptional(asValue("ip", "otp"), "otp"), voucher_activates: asOptional(asDate), voucher_auth: asOptional(asBase64), voucher_id: asOptional(asString), }); export const asOtpResetPayload: Cleaner<OtpResetPayload> = asObject({ otpResetDate: asDate, }); export const asPasswordErrorPayload: Cleaner<PasswordErrorPayload> = asObject({ wait_seconds: asOptional(asNumber), }); export const asRecovery2InfoPayload: Cleaner<Recovery2InfoPayload> = asObject({ question2Box: asEdgeBox, }); export const asUsernameInfoPayload: Cleaner<UsernameInfoPayload> = asObject({ loginId: asBase64, // Password login: passwordAuthSnrp: asOptional(asEdgeSnrp), // Recovery v1 login: questionBox: asOptional(asEdgeBox), questionKeySnrp: asOptional(asEdgeSnrp), recoveryAuthSnrp: asOptional(asEdgeSnrp), }); // --------------------------------------------------------------------- // uncleaners // --------------------------------------------------------------------- // Common types: export const wasEdgeBox = uncleaner<EdgeBox>(asEdgeBox); export const wasEdgeLobbyReply = uncleaner<EdgeLobbyReply>(asEdgeLobbyReply); export const wasEdgeLobbyRequest = uncleaner<EdgeLobbyRequest>(asEdgeLobbyRequest); // Top-level request / response bodies: export const wasLoginRequestBody = uncleaner<LoginRequestBody>(asLoginRequestBody); export const wasLoginResponseBody = uncleaner<LoginResponseBody>(asLoginResponseBody); // Request payloads: export const wasChangeOtpPayload = uncleaner<ChangeOtpPayload>(asChangeOtpPayload); export const wasChangePasswordPayload = uncleaner<ChangePasswordPayload>( asChangePasswordPayload, ); export const wasChangePin2IdPayload = uncleaner<ChangePin2IdPayload>( asChangePin2IdPayload, ); export const wasChangePin2Payload = uncleaner<ChangePin2Payload>(asChangePin2Payload); export const wasChangeRecovery2IdPayload = uncleaner<ChangeRecovery2IdPayload>( asChangeRecovery2IdPayload, ); export const wasChangeRecovery2Payload = uncleaner<ChangeRecovery2Payload>( asChangeRecovery2Payload, ); export const wasChangeSecretPayload = uncleaner<ChangeSecretPayload>( asChangeSecretPayload, ); export const wasChangeUsernamePayload = uncleaner<ChangeUsernamePayload>( asChangeUsernamePayload, ); export const wasChangeVouchersPayload = uncleaner<ChangeVouchersPayload>( asChangeVouchersPayload, ); export const wasCreateKeysPayload = uncleaner<CreateKeysPayload>(asCreateKeysPayload); export const wasCreateLoginPayload = uncleaner<CreateLoginPayload>(asCreateLoginPayload); // Response payloads: export const wasChallengeErrorPayload = uncleaner<ChallengeErrorPayload>( asChallengeErrorPayload, ); export const wasCreateChallengePayload = uncleaner<CreateChallengePayload>( asCreateChallengePayload, ); export const wasLobbyPayload = uncleaner<LobbyPayload>(asLobbyPayload); export const wasLoginPayload = uncleaner<LoginPayload>(asLoginPayload); export const wasMessagesPayload = uncleaner<MessagesPayload>(asMessagesPayload); export const wasOtpErrorPayload = uncleaner<OtpErrorPayload>(asOtpErrorPayload); export const wasOtpResetPayload = uncleaner<OtpResetPayload>(asOtpResetPayload); export const wasPasswordErrorPayload = uncleaner<PasswordErrorPayload>( asPasswordErrorPayload, ); export const wasRecovery2InfoPayload = uncleaner<Recovery2InfoPayload>( asRecovery2InfoPayload, ); export const wasUsernameInfoPayload = uncleaner<UsernameInfoPayload>( asUsernameInfoPayload, );