Basic live service:
- Matchmaking
- Instance
- Presence (albeit empty atm)
Authentication fixes; differentiate between user and profile
Default auth timeout is now 3 hours
Add "operators" database key ("all users with operator permissions", or "developer" role set in token), add check in `Profile`
Fix default profile image filename reference when not set
account/me
Log hile reporting, do stuff with the report later ("Server" user for commands, operators can check reports)
Refresh login done by client automatically when token expires, requires extra work
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import { APIUtils, NoBody } from "../../apiutils.ts";
|
||||
import express from "express";
|
||||
import Profile from "../../data/profiles.ts";
|
||||
import { decode } from "@gz/jwt";
|
||||
import { Config } from "../../config.ts";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
|
||||
const config = Config.getConfig();
|
||||
|
||||
const log = new Logging("AuthConnectRoute");
|
||||
|
||||
export const route = APIUtils.createRouter("/connect");
|
||||
|
||||
interface TokenRequestBody {
|
||||
interface AuthBodyBase {
|
||||
grant_type: string;
|
||||
account_id: string;
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
platform: string;
|
||||
@@ -18,6 +24,16 @@ interface TokenRequestBody {
|
||||
asid: string;
|
||||
platform_auth: string;
|
||||
}
|
||||
interface TokenRequest extends AuthBodyBase {
|
||||
account_id: string;
|
||||
grant_type: "cached_login"
|
||||
}
|
||||
interface RefreshRequest extends AuthBodyBase {
|
||||
refresh_token: string,
|
||||
grant_type: "refresh_token"
|
||||
}
|
||||
|
||||
type TokenRequestBody = TokenRequest | RefreshRequest;
|
||||
|
||||
interface TokenResponseBody {
|
||||
error?: string;
|
||||
@@ -30,9 +46,9 @@ route.router.post("/token",
|
||||
|
||||
APIUtils.Authentication,
|
||||
express.urlencoded({ extended: true }),
|
||||
APIUtils.checkBodyTypes<TokenRequestBody>({
|
||||
APIUtils.logBody,
|
||||
APIUtils.checkBodyTypes<AuthBodyBase>({
|
||||
grant_type: "",
|
||||
account_id: "",
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
platform: "",
|
||||
@@ -42,25 +58,23 @@ route.router.post("/token",
|
||||
time: "",
|
||||
ver: "",
|
||||
asid: "",
|
||||
platform_auth: "",
|
||||
platform_auth: ""
|
||||
}),
|
||||
|
||||
async (
|
||||
rq: express.Request<NoBody, NoBody, TokenRequestBody>,
|
||||
rs: express.Response<TokenResponseBody>,
|
||||
) => {
|
||||
|
||||
|
||||
function requestFailed(msg: string = "invalid_request") {
|
||||
rs.json({
|
||||
error: msg,
|
||||
access_token: "",
|
||||
refresh_token: "",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const conditionsMet = ![
|
||||
rq.body.grant_type == "cached_login",
|
||||
rq.body.client_id == "recroom",
|
||||
rq.body.platform == "0",
|
||||
rq.body.ver == '20191120',
|
||||
@@ -71,24 +85,78 @@ route.router.post("/token",
|
||||
!(rq.body.asid.length > 32),
|
||||
].includes(false);
|
||||
|
||||
if (conditionsMet) {
|
||||
if (!conditionsMet) {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
if (rq.body.grant_type == 'cached_login') {
|
||||
const accounts = await rs.locals.user.getAssociatedProfiles();
|
||||
const targetAccount = parseInt(rq.body.account_id);
|
||||
|
||||
if (isNaN(targetAccount)) requestFailed();
|
||||
if (!accounts.has(targetAccount)) requestFailed("access_denied");
|
||||
if (isNaN(targetAccount)) {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
if (!accounts.has(targetAccount)) {
|
||||
requestFailed("access_denied");
|
||||
return;
|
||||
}
|
||||
|
||||
rs.locals.user.addAssociatedDeviceId(rq.body.device_id);
|
||||
rs.locals.user.addAssociatedPlatformId(rq.body.platform_id);
|
||||
|
||||
const profile = new Profile(targetAccount);
|
||||
if (!(await Profile.exists(profile.getId()))) requestFailed();
|
||||
if (!(await Profile.exists(profile.getId()))) {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await profile.getToken();
|
||||
rs.json({
|
||||
access_token: token,
|
||||
refresh_token: token,
|
||||
});
|
||||
} else requestFailed();
|
||||
} else {
|
||||
const refreshToken = rq.body.refresh_token;
|
||||
if (typeof refreshToken == 'undefined') {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
let decodedToken;
|
||||
try {
|
||||
decodedToken = await decode(rq.body.refresh_token, config.auth.secret, { algorithm: "HS512" });
|
||||
} catch (err) {
|
||||
log.w(`Refresh token decode failed: ${err}`);
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = await rs.locals.user.getAssociatedProfiles();
|
||||
const targetAccount = parseInt(decodedToken.sub ? decodedToken.sub : "NaN");
|
||||
|
||||
if (isNaN(targetAccount)) {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
if (!accounts.has(targetAccount)) {
|
||||
requestFailed("access_denied");
|
||||
return;
|
||||
}
|
||||
|
||||
rs.locals.user.addAssociatedDeviceId(rq.body.device_id);
|
||||
rs.locals.user.addAssociatedPlatformId(rq.body.platform_id);
|
||||
|
||||
const profile = new Profile(targetAccount);
|
||||
if (!(await Profile.exists(profile.getId()))) {
|
||||
requestFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await profile.getToken();
|
||||
rs.json({
|
||||
access_token: token,
|
||||
refresh_token: token,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user