mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-11 22:17:14 +03:00
Better token generation
This commit is contained in:
@@ -185,6 +185,7 @@ addDefaults(config, {
|
|||||||
gumroad: {
|
gumroad: {
|
||||||
productPermalinks: ["sponsorblock"]
|
productPermalinks: ["sponsorblock"]
|
||||||
},
|
},
|
||||||
|
tokenSeed: "",
|
||||||
minUserIDLength: 30
|
minUserIDLength: 30
|
||||||
});
|
});
|
||||||
loadFromEnv(config);
|
loadFromEnv(config);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ interface GenerateTokenRequest extends Request {
|
|||||||
query: {
|
query: {
|
||||||
code: string;
|
code: string;
|
||||||
adminUserID?: string;
|
adminUserID?: string;
|
||||||
|
total?: string;
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
type: TokenType;
|
type: TokenType;
|
||||||
@@ -14,31 +15,41 @@ interface GenerateTokenRequest extends Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function generateTokenRequest(req: GenerateTokenRequest, res: Response): Promise<Response> {
|
export async function generateTokenRequest(req: GenerateTokenRequest, res: Response): Promise<Response> {
|
||||||
const { query: { code, adminUserID }, params: { type } } = req;
|
const { query: { code, adminUserID, total }, params: { type } } = req;
|
||||||
const adminUserIDHash = adminUserID ? (await getHashCache(adminUserID)) : null;
|
const adminUserIDHash = adminUserID ? (await getHashCache(adminUserID)) : null;
|
||||||
|
|
||||||
if (!code || !type) {
|
if (!type || (!code && type === TokenType.patreon)) {
|
||||||
return res.status(400).send("Invalid request");
|
return res.status(400).send("Invalid request");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === TokenType.patreon || (type === TokenType.local && adminUserIDHash === config.adminUserID)) {
|
if (type === TokenType.patreon
|
||||||
const licenseKey = await createAndSaveToken(type, code);
|
|| ([TokenType.local, TokenType.gift].includes(type) && adminUserIDHash === config.adminUserID)
|
||||||
|
|| type === TokenType.free) {
|
||||||
|
const licenseKey = await createAndSaveToken(type, code, adminUserIDHash === config.adminUserID ? parseInt(total) : 1);
|
||||||
|
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (licenseKey) {
|
if (licenseKey) {
|
||||||
return res.status(200).send(`
|
if (type === TokenType.patreon) {
|
||||||
<h1>
|
return res.status(200).send(`
|
||||||
Your license key:
|
<h1>
|
||||||
</h1>
|
Your license key:
|
||||||
<p>
|
</h1>
|
||||||
<b>
|
<p>
|
||||||
${licenseKey}
|
<b>
|
||||||
</b>
|
${licenseKey[0]}
|
||||||
</p>
|
</b>
|
||||||
<p>
|
</p>
|
||||||
Copy this into the textbox in the other tab
|
<p>
|
||||||
</p>
|
Copy this into the textbox in the other tab
|
||||||
`);
|
</p>
|
||||||
|
`);
|
||||||
|
} else if (type === TokenType.free) {
|
||||||
|
return res.status(200).send({
|
||||||
|
licenseKey: licenseKey[0]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return res.status(200).send(licenseKey.join("<br/>"));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return res.status(401).send(`
|
return res.status(401).send(`
|
||||||
<h1>
|
<h1>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { config } from "../config";
|
|||||||
import { privateDB } from "../databases/databases";
|
import { privateDB } from "../databases/databases";
|
||||||
import { Logger } from "../utils/logger";
|
import { Logger } from "../utils/logger";
|
||||||
import { getPatreonIdentity, PatronStatus, refreshToken, TokenType } from "../utils/tokenUtils";
|
import { getPatreonIdentity, PatronStatus, refreshToken, TokenType } from "../utils/tokenUtils";
|
||||||
|
import { getHash } from "../utils/getHash";
|
||||||
|
|
||||||
interface VerifyTokenRequest extends Request {
|
interface VerifyTokenRequest extends Request {
|
||||||
query: {
|
query: {
|
||||||
@@ -12,7 +13,9 @@ interface VerifyTokenRequest extends Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const validateLicenseKeyRegex = (token: string) =>
|
export const validateLicenseKeyRegex = (token: string) =>
|
||||||
new RegExp(/[A-Za-z0-9]{40}|[A-Za-z0-9-]{35}/).test(token);
|
new RegExp(/[A-Za-z0-9]{40}|[A-Za-z0-9-]{35}|[A-Za-z0-9-]{5}-[A-Za-z0-9-]{5}/).test(token);
|
||||||
|
|
||||||
|
const isLocalLicenseKey = (token: string) => /[A-Za-z0-9-]{5}-[A-Za-z0-9-]{5}/.test(token);
|
||||||
|
|
||||||
export async function verifyTokenRequest(req: VerifyTokenRequest, res: Response): Promise<Response> {
|
export async function verifyTokenRequest(req: VerifyTokenRequest, res: Response): Promise<Response> {
|
||||||
const { query: { licenseKey } } = req;
|
const { query: { licenseKey } } = req;
|
||||||
@@ -27,6 +30,18 @@ export async function verifyTokenRequest(req: VerifyTokenRequest, res: Response)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLocalLicenseKey(licenseKey)) {
|
||||||
|
const parts = licenseKey.split("-");
|
||||||
|
const code = parts[0];
|
||||||
|
const givenResult = parts[1];
|
||||||
|
|
||||||
|
if (getHash(config.tokenSeed + code, 1).startsWith(givenResult)) {
|
||||||
|
return res.status(200).send({
|
||||||
|
allowed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tokens = (await privateDB.prepare("get", `SELECT "accessToken", "refreshToken", "expiresIn" from "oauthLicenseKeys" WHERE "licenseKey" = ?`
|
const tokens = (await privateDB.prepare("get", `SELECT "accessToken", "refreshToken", "expiresIn" from "oauthLicenseKeys" WHERE "licenseKey" = ?`
|
||||||
, [licenseKey])) as {accessToken: string, refreshToken: string, expiresIn: number};
|
, [licenseKey])) as {accessToken: string, refreshToken: string, expiresIn: number};
|
||||||
if (tokens) {
|
if (tokens) {
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export interface SBSConfig {
|
|||||||
gumroad: {
|
gumroad: {
|
||||||
productPermalinks: string[],
|
productPermalinks: string[],
|
||||||
},
|
},
|
||||||
|
tokenSeed: string,
|
||||||
minUserIDLength: number
|
minUserIDLength: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import { privateDB } from "../databases/databases";
|
|||||||
import { Logger } from "./logger";
|
import { Logger } from "./logger";
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import { randomInt } from "node:crypto";
|
import { randomInt } from "node:crypto";
|
||||||
|
import { getHash } from "./getHash";
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
patreon = "patreon",
|
patreon = "patreon",
|
||||||
local = "local",
|
local = "local",
|
||||||
|
free = "free",
|
||||||
|
gift = "gift",
|
||||||
gumroad = "gumroad"
|
gumroad = "gumroad"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +31,7 @@ export interface PatreonIdentityData {
|
|||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAndSaveToken(type: TokenType, code?: string): Promise<string> {
|
export async function createAndSaveToken(type: TokenType, code?: string, total = 1): Promise<string[]> {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case TokenType.patreon: {
|
case TokenType.patreon: {
|
||||||
const domain = "https://www.patreon.com";
|
const domain = "https://www.patreon.com";
|
||||||
@@ -48,7 +51,7 @@ export async function createAndSaveToken(type: TokenType, code?: string): Promis
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.status === 200) {
|
if (result.status === 200) {
|
||||||
const licenseKey = generateToken();
|
const licenseKey = generateLicenseKey("P");
|
||||||
const time = Date.now();
|
const time = Date.now();
|
||||||
|
|
||||||
await privateDB.prepare("run", `INSERT INTO "licenseKeys"("licenseKey", "time", "type") VALUES(?, ?, ?)`, [licenseKey, time, type]);
|
await privateDB.prepare("run", `INSERT INTO "licenseKeys"("licenseKey", "time", "type") VALUES(?, ?, ?)`, [licenseKey, time, type]);
|
||||||
@@ -56,7 +59,7 @@ export async function createAndSaveToken(type: TokenType, code?: string): Promis
|
|||||||
, [licenseKey, result.data.access_token, result.data.refresh_token, result.data.expires_in]);
|
, [licenseKey, result.data.access_token, result.data.refresh_token, result.data.expires_in]);
|
||||||
|
|
||||||
|
|
||||||
return licenseKey;
|
return [licenseKey];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} catch (e) /* istanbul ignore next */ {
|
} catch (e) /* istanbul ignore next */ {
|
||||||
@@ -64,13 +67,16 @@ export async function createAndSaveToken(type: TokenType, code?: string): Promis
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case TokenType.free:
|
||||||
|
case TokenType.gift:
|
||||||
case TokenType.local: {
|
case TokenType.local: {
|
||||||
const licenseKey = generateToken();
|
const prefix = type === TokenType.free ? "F" : (type === TokenType.gift ? "G" : "A");
|
||||||
const time = Date.now();
|
const licenseKeys = [];
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
|
licenseKeys.push(generateLicenseKey(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
await privateDB.prepare("run", `INSERT INTO "licenseKeys"("licenseKey", "time", "type") VALUES(?, ?, ?)`, [licenseKey, time, type]);
|
return licenseKeys;
|
||||||
|
|
||||||
return licenseKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -109,7 +115,12 @@ export async function refreshToken(type: TokenType, licenseKey: string, refreshT
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateToken(length = 40): string {
|
function generateLicenseKey(prefix: string): string {
|
||||||
|
const code = `${prefix}${generateRandomCode()}`;
|
||||||
|
return `${code}-${getHash(config.tokenSeed + code, 1).slice(0, 5)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomCode(length = 4): string {
|
||||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
let result = "";
|
let result = "";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user