galvanic corrosion rewrite
commit this before something goes horribly wrong
This commit is contained in:
@@ -1,18 +1,15 @@
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
import { authenticate } from "../../../util/api.ts";
|
||||
import { authenticate, galvanicError, GalvanicErrors, RateLimiter, recNetError } from "../../../util/api.ts";
|
||||
import Server from "../../../server/server.ts";
|
||||
import z from "zod";
|
||||
import { typedZValidator } from "../../../util/validators.ts";
|
||||
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
||||
import { PlatformType } from "../../../server/platforms/types.ts";
|
||||
import Steam from "../../../util/steam/steam.ts";
|
||||
|
||||
export const route = createHonoRoute('/account');
|
||||
|
||||
const transformNumber = (arg: string, ctx: z.RefinementCtx<string>) => {
|
||||
const int = parseInt(arg);
|
||||
if (isNaN(int) || !Number.isSafeInteger(int)) ctx.addIssue('Number is not valid');
|
||||
else return int;
|
||||
}
|
||||
const bulkAccountQuerySchema = z.object({
|
||||
id: z.union([ z.string().transform(transformNumber), z.array(z.string().transform(transformNumber)) ])
|
||||
id: z.union([ z.coerce.number(), z.array(z.coerce.number()) ])
|
||||
});
|
||||
route.app.get('/bulk', typedZValidator('query', bulkAccountQuerySchema), async c => {
|
||||
const { id } = c.req.valid('query');
|
||||
@@ -29,6 +26,51 @@ route.app.get('/bulk', typedZValidator('query', bulkAccountQuerySchema), async c
|
||||
);
|
||||
});
|
||||
|
||||
const postCreateRateLimiter = new RateLimiter(60, 3);
|
||||
const createAccountBodySchema = z.object({
|
||||
platform: z.string().transform(transformStringToEnum<PlatformType>(PlatformType)),
|
||||
platformId: z.string().min(14).max(20),
|
||||
deviceId: z.string().min(32).max(64)
|
||||
});
|
||||
route.app.post('/create', postCreateRateLimiter.middle(), typedZValidator('form', createAccountBodySchema), async c => {
|
||||
|
||||
const form = c.req.valid('form');
|
||||
|
||||
if (typeof form.platform == 'undefined')
|
||||
return c.json(galvanicError(GalvanicErrors.jex));
|
||||
else if (form.platform == PlatformType.Steam) {
|
||||
const steam = await Steam.GetPlayerSummaries([form.platformId]);
|
||||
if (steam.length == 0)
|
||||
return c.json(galvanicError(GalvanicErrors.sploot));
|
||||
|
||||
const cachedlogins = await Server.Platforms.getCachedLogins(form.platform, form.platformId, true);
|
||||
if (cachedlogins.length == 0) {
|
||||
|
||||
const profile = await Server.Profiles.create(form.platform, form.platformId, steam[0].realname ?? steam[0].personaname);
|
||||
if (!profile) return c.json(galvanicError(GalvanicErrors.sploot));
|
||||
|
||||
Server.Content.steamAvatarDownloadForProfile(profile, steam[0].avatarfull);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
value: profile.export()
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
const profile = await Server.Profiles.create(form.platform, form.platformId);
|
||||
if (!profile) return c.json(galvanicError(GalvanicErrors.sploot));
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
value: profile.export()
|
||||
});
|
||||
|
||||
}
|
||||
} else return c.json(recNetError("Not a Steam user"));
|
||||
|
||||
});
|
||||
|
||||
route.app.use(authenticate);
|
||||
|
||||
route.app.get('/me', c => {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { successResponse } from "../../../util/api.ts";
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
|
||||
export const route = createHonoRoute('/gamesight');
|
||||
|
||||
route.app.post('/event', successResponse(true, ""));
|
||||
7
src/routes/api/routes/undead.ts
Normal file
7
src/routes/api/routes/undead.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
|
||||
export const route = createHonoRoute('/undead');
|
||||
|
||||
route.app.get('/v1/emotes', c => {
|
||||
return c.json([]);
|
||||
});
|
||||
@@ -6,7 +6,7 @@ export const route = createHonoRoute("/versioncheck");
|
||||
|
||||
const versionCheckSchema = z.object({
|
||||
v: z.string(),
|
||||
p: z.string().transform(Number),
|
||||
p: z.coerce.number(),
|
||||
});
|
||||
|
||||
enum VersionStatus {
|
||||
@@ -15,7 +15,7 @@ enum VersionStatus {
|
||||
UpdateRequired
|
||||
}
|
||||
|
||||
export const gameVerString = '20220118';
|
||||
export const gameVerString = '20200306';
|
||||
|
||||
route.app.get('/v4', typedZValidator('query', versionCheckSchema), c => {
|
||||
const { v, p } = c.req.valid('query');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import z from "zod";
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
||||
import { PlatformType } from "../../../server/platforms/base.ts";
|
||||
import { PlatformType } from "../../../server/platforms/types.ts";
|
||||
import Server from "../../../server/server.ts";
|
||||
import { authenticate } from "../../../util/api.ts";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
@@ -28,6 +28,8 @@ const forPlatformIdsReqSchema = z.object({
|
||||
});
|
||||
route.app.post('/forplatformids', typedZValidator('form', forPlatformIdsReqSchema), async c => {
|
||||
const { id } = c.req.valid('form');
|
||||
log.d(`forplatformids: ${id}`);
|
||||
|
||||
const ids = await Server.Platforms.getCachedLogins(PlatformType.Steam, id, true);
|
||||
return c.json(ids || []);
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
import z from "zod";
|
||||
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
||||
import { DeviceClass, PlatformType, steamAuthTicketSchema } from "../../../server/platforms/base.ts";
|
||||
import { DeviceClass, PlatformType, TokenFormat, TokenType } from "../../../server/platforms/types.ts";
|
||||
import { steamAuthTicketSchema } from "../../../server/platforms/base.ts";
|
||||
import { gameVerString } from "../../api/routes/versioncheck.ts";
|
||||
import Steam from "../../../util/steam/steam.ts";
|
||||
import { SteamAuthResult } from "../../../util/steam/SteamAuthTypes.ts";
|
||||
@@ -22,12 +23,9 @@ const authBodyBaseSchema = z.object({
|
||||
platform_id: z.string().min(4),
|
||||
device_id: z.string().min(4),
|
||||
device_class: z.string().transform(transformStringToEnum<DeviceClass>(DeviceClass)),
|
||||
time: z.string().transform(Date),
|
||||
time: z.coerce.date(),
|
||||
ver: z.literal(gameVerString),
|
||||
build_key: z.string().min(4),
|
||||
asid: z.string().transform(Number),
|
||||
eac_challenge: z.literal("who said it"),
|
||||
eac_response: z.literal("who_said_it"),
|
||||
asid: z.coerce.number(),
|
||||
platform_auth: z.string().transform((arg, ctx) => {
|
||||
try {
|
||||
const parsed = steamAuthTicketSchema.safeParse(JSON.parse(arg))
|
||||
@@ -39,9 +37,6 @@ const authBodyBaseSchema = z.object({
|
||||
})
|
||||
});
|
||||
|
||||
const createAccountGrantSchema = authBodyBaseSchema.extend({
|
||||
grant_type: z.literal("create_account")
|
||||
});
|
||||
const cachedLoginGrantSchema = authBodyBaseSchema.extend({
|
||||
grant_type: z.literal('cached_login'),
|
||||
account_id: z.string().transform(Number),
|
||||
@@ -52,21 +47,29 @@ const refreshTokenGrantSchema = authBodyBaseSchema.extend({
|
||||
});
|
||||
|
||||
const tokenGrantSchema = z.discriminatedUnion('grant_type', [
|
||||
createAccountGrantSchema,
|
||||
cachedLoginGrantSchema,
|
||||
refreshTokenGrantSchema
|
||||
]);
|
||||
|
||||
enum TokenRequestError {
|
||||
InvalidRequest = "invalid_request",
|
||||
InvalidGrant = "invalid_grant",
|
||||
InvalidClient = "invalid_client",
|
||||
InvalidUsernameOrPassword = "invalid_username_or_password",
|
||||
InvalidTime = "invalid time",
|
||||
InvalidPlatform = "invalid platform",
|
||||
InvalidGrant = "invalid_grant",
|
||||
UnauthorizedClient = "unauthorized_client",
|
||||
UnsupportedGrantType = "unsupported_grant_type",
|
||||
UnsupportedResponseType = "unsupported_response_type",
|
||||
InvalidScope = "invalid_scope",
|
||||
AuthorizationPending = "authorization_pending",
|
||||
AccessDenied = "access_denied",
|
||||
SlowDown = "slow_down",
|
||||
PlatformVerificationFailed = "platform verification failed"
|
||||
ExpiredToken = "expired_token"
|
||||
}
|
||||
enum TokenRequestErrorDescriptions {
|
||||
InvalidUsernameOrPassword = "invalid_username_or_password",
|
||||
InvalidTime = "invalid time",
|
||||
PlatformVerificationFailed = "platform verification failed",
|
||||
InvalidPlatform = "invalid platform",
|
||||
InvalidDeviceClass = "invalid device class"
|
||||
}
|
||||
|
||||
route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => {
|
||||
@@ -75,55 +78,47 @@ route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => {
|
||||
}
|
||||
|
||||
const form = c.req.valid('form');
|
||||
if (typeof form.platform_auth == 'undefined' || typeof form.platform == 'undefined') return error(TokenRequestError.InvalidPlatform);
|
||||
if (typeof form.platform_auth == 'undefined' || typeof form.platform == 'undefined') return error(TokenRequestError.AccessDenied);
|
||||
|
||||
const { valid } = await Steam.AuthenticateUserTicket(form.platform_auth, form.platform_id);
|
||||
if (valid == SteamAuthResult.Failure) return error(TokenRequestError.PlatformVerificationFailed);
|
||||
if (valid == SteamAuthResult.Failure) return error(TokenRequestError.AccessDenied, TokenRequestErrorDescriptions.PlatformVerificationFailed);
|
||||
|
||||
if (Math.abs(Date.now() - new Date(form.time).getTime()) > 3_600_000) return error(TokenRequestError.InvalidTime);
|
||||
if (Math.abs(Date.now() - new Date(form.time).getTime()) > 3_600_000) return error(TokenRequestError.AccessDenied, TokenRequestErrorDescriptions.InvalidTime);
|
||||
|
||||
const logins = await Server.Platforms.getCachedLogins(form.platform, form.platform_id, false);
|
||||
if (form.grant_type == 'create_account' && logins && logins.length > 0) return error(TokenRequestError.InvalidRequest);
|
||||
else if (form.grant_type == 'create_account') {
|
||||
const profile = await Server.Profiles.create();
|
||||
if (!profile) return error(TokenRequestError.AccessDenied);
|
||||
await Server.Platforms.addCachedLogin(form.platform, form.platform_id, profile?.getId());
|
||||
|
||||
const token = await Server.Platforms.getToken(profile.getId(), await profile.getRole() || 'user');
|
||||
return c.json({
|
||||
access_token: token,
|
||||
refresh_token: token,
|
||||
key: "aHVo"
|
||||
});
|
||||
} else if (form.grant_type == 'refresh_token') {
|
||||
if (form.grant_type == 'refresh_token') {
|
||||
const secret = Deno.env.get('SECRET');
|
||||
if (!secret) {
|
||||
log.w(`Secret not set!`);
|
||||
return error(TokenRequestError.InvalidRequest);
|
||||
}
|
||||
try {
|
||||
await verify(form.refresh_token, secret);
|
||||
const token = JSON.parse(JSON.stringify(await verify(form.refresh_token, secret))) as TokenFormat;
|
||||
|
||||
const profile = await Server.Profiles.get(token.sub);
|
||||
if (!profile) return error(TokenRequestError.AccessDenied);
|
||||
const accessToken = await Server.Platforms.getToken(profile.getId(), TokenType.Access);
|
||||
|
||||
return c.json({
|
||||
access_token: form.refresh_token,
|
||||
access_token: accessToken,
|
||||
refresh_token: form.refresh_token,
|
||||
key: "aHVo"
|
||||
});
|
||||
} catch (err) {
|
||||
log.w(`Authentication error (token req): ${(err as Error).stack}`);
|
||||
return error(TokenRequestError.InvalidRequest);
|
||||
return error(TokenRequestError.InvalidClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (logins && logins.find(login => login.accountId === form.account_id)) {
|
||||
if (logins.find(login => login.accountId === form.account_id)) {
|
||||
const profile = await Server.Profiles.get(form.account_id);
|
||||
if (!profile) return error(TokenRequestError.InvalidRequest, "No such profile");
|
||||
await Server.Platforms.updateLastLoginTime(form.platform, form.platform_id, form.account_id);
|
||||
const token = await Server.Platforms.getToken(profile.getId(), await profile.getRole() || 'user');
|
||||
const accessToken = await Server.Platforms.getToken(profile.getId(), TokenType.Access);
|
||||
const refreshToken = await Server.Platforms.getToken(profile.getId(), TokenType.Refresh);
|
||||
|
||||
return c.json({
|
||||
access_token: token,
|
||||
refresh_token: token,
|
||||
key: "aHVo"
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
} else return error(TokenRequestError.InvalidRequest, "No such profile");
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
|
||||
export const route = createHonoRoute("/eac");
|
||||
|
||||
route.app.get('/challenge', c => {
|
||||
return c.text(`"who said it"`);
|
||||
});
|
||||
@@ -5,6 +5,6 @@ export const route = createHonoRoute("/player");
|
||||
|
||||
route.app.use(authenticate);
|
||||
|
||||
route.app.post('/login', async c => {
|
||||
return c.status(200);
|
||||
route.app.post('/login', _c => {
|
||||
return new Response("OK", { status: 200 });
|
||||
});
|
||||
@@ -1,31 +1,22 @@
|
||||
import { getNetConfig } from "../../net.ts";
|
||||
import { createHonoRoute } from "../../util/import.ts";
|
||||
|
||||
export const route = createHonoRoute('/');
|
||||
|
||||
const netConfig = getNetConfig();
|
||||
route.app.get('/', async (c, next) => {
|
||||
if (c.req.query('v') == '2') return c.json({
|
||||
Accounts: "https://wsi.proxnet.dev/accounts",
|
||||
API: "https://wsi.proxnet.dev/",
|
||||
Auth: "https://wsi.proxnet.dev/auth",
|
||||
BugReporting: "https://wsi.proxnet.dev/bugs",
|
||||
CDN: "https://wsi.proxnet.dev/cdn",
|
||||
Chat: "https://wsi.proxnet.dev/chat",
|
||||
Clubs: "https://wsi.proxnet.dev/clubs",
|
||||
Commerce: "https://wsi.proxnet.dev/commerce",
|
||||
DataCollection: "https://wsi.proxnet.dev/datacol",
|
||||
Discovery: "https://wsi.proxnet.dev/disc",
|
||||
Images: "https://wsi.proxnet.dev/img",
|
||||
Leaderboard: "https://wsi.proxnet.dev/leaderboard",
|
||||
Link: "https://wsi.proxnet.dev/link",
|
||||
Matchmaking: "https://wsi.proxnet.dev/match",
|
||||
Moderation: "https://wsi.proxnet.dev/mod",
|
||||
Notifications: "https://wsi.proxnet.dev/notify",
|
||||
PlatformNotifications: "https://wsi.proxnet.dev/platnotify",
|
||||
PlayerSettings: "https://wsi.proxnet.dev/plsettings",
|
||||
RoomComments: "https://wsi.proxnet.dev/roomcomments",
|
||||
Rooms: "https://wsi.proxnet.dev/rooms",
|
||||
Storage: "https://wsi.proxnet.dev/storage",
|
||||
WWW: "https://wsi.proxnet.dev/www",
|
||||
if (c.req.query('v') === '2') return c.json({
|
||||
Auth: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/auth`,
|
||||
API: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/`,
|
||||
Notifications: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/notify`,
|
||||
Images: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/img`,
|
||||
CDN: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/cdn`,
|
||||
Commerce: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/commerce`,
|
||||
Matchmaking: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/match`,
|
||||
Storage: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/storage`,
|
||||
Chat: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/chat`,
|
||||
Leaderboard: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/leaderboard`,
|
||||
Accounts: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/accounts`,
|
||||
});
|
||||
return await next();
|
||||
});
|
||||
Reference in New Issue
Block a user