Microsoft-Rewards-Script
Microsoft-Rewards-Script copied to clipboard
Support for TOTP-based authentication
Can you add support for a totp property for the account data in accounts.json, so that we may put in TOTP secret and the script will be able to generate TOTP codes accordingly
import crypto from "crypto";
/**
* Decode Base32 (RFC 4648) to a Buffer.
* Accepts lowercase/uppercase, optional padding.
*/
function base32Decode(input: string): Buffer {
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const clean = input.toUpperCase().replace(/=+$/g, "").replace(/[^A-Z2-7]/g, "");
let bits = 0;
let value = 0;
const bytes: number[] = [];
for (const char of clean) {
value = (value << 5) | alphabet.indexOf(char);
bits += 5;
if (bits >= 8) {
bits -= 8;
bytes.push((value >>> bits) & 0xff);
}
}
return Buffer.from(bytes);
}
/**
* Generate an HMAC using Node's crypto and return Buffer.
*/
function hmac(algorithm: string, key: Buffer, data: Buffer): Buffer {
return crypto.createHmac(algorithm, key).update(data).digest();
}
/**
* Generate TOTP per RFC 6238.
* @param secretBase32 - shared secret in Base32
* @param time - Unix time in seconds (defaults to now)
* @param options - { digits, step, algorithm }
* @returns numeric TOTP as string (zero-padded)
*/
export function generateTOTP(
secretBase32: string,
time: number = Math.floor(Date.now() / 1000),
options?: { digits?: number; step?: number; algorithm?: "SHA1" | "SHA256" | "SHA512" }
): string {
const digits = options?.digits ?? 6;
const step = options?.step ?? 30;
const alg = (options?.algorithm ?? "SHA1").toUpperCase();
const key = base32Decode(secretBase32);
const counter = Math.floor(time / step);
// 8-byte big-endian counter
const counterBuffer = Buffer.alloc(8);
counterBuffer.writeBigUInt64BE(BigInt(counter), 0);
let hmacAlg: string;
if (alg === "SHA1") hmacAlg = "sha1";
else if (alg === "SHA256") hmacAlg = "sha256";
else if (alg === "SHA512") hmacAlg = "sha512";
else throw new Error("Unsupported algorithm. Use SHA1, SHA256 or SHA512.");
const hash = hmac(hmacAlg, key, counterBuffer);
// Dynamic truncation
const offset = hash[hash.length - 1] & 0x0f;
const code =
((hash[offset] & 0x7f) << 24) |
((hash[offset + 1] & 0xff) << 16) |
((hash[offset + 2] & 0xff) << 8) |
(hash[offset + 3] & 0xff);
const otp = (code % 10 ** digits).toString().padStart(digits, "0");
return otp;
}
Functional snippet, one may integrate this and he wont have to update it for a long time.
Add https://github.com/TheNetsky/Microsoft-Rewards-Script/pull/361 & https://github.com/LightZirconite/Microsoft-Rewards-Script-Private/tree/V2