Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/databaseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export async function setupFileStructure(joinFunc = join) {
"contracts",
joinFunc("userdata", "epicids"),
joinFunc("userdata", "steamids"),
joinFunc("userdata", "appleids"),
joinFunc("userdata", "users"),
joinFunc("userdata", "h1", "steamids"),
joinFunc("userdata", "h1", "epicids"),
Expand Down
2 changes: 2 additions & 0 deletions components/generatedPeacockRequireTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as commandService from "./commandService"
import * as configSwizzleManager from "./configSwizzleManager"
import * as controller from "./controller"
import * as databaseHandler from "./databaseHandler"
import * as delegation from "./delegation"
import * as entitlementStrategies from "./entitlementStrategies"
import * as eventHandler from "./eventHandler"
import * as evergreen from "./evergreen"
Expand Down Expand Up @@ -85,6 +86,7 @@ export default {
"@peacockproject/core/configSwizzleManager": configSwizzleManager,
"@peacockproject/core/controller": controller,
"@peacockproject/core/databaseHandler": databaseHandler,
"@peacockproject/core/delegation": delegation,
"@peacockproject/core/entitlementStrategies": entitlementStrategies,
"@peacockproject/core/eventHandler": eventHandler,
"@peacockproject/core/evergreen": evergreen,
Expand Down
47 changes: 32 additions & 15 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,16 @@ const app = express()
const baseDir = __dirname

app.use(function badPathRewritingMiddleware(req, _, next) {
req.url = req.url.replaceAll("//", "/")
// rewrite every `//` to `/` that occurs before the query string

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?issuer=https://appleid.apple.com, because that makes total sense

const qIdx = req.url.indexOf("?")

if (qIdx === -1) {
req.url = req.url.replaceAll("//", "/")
} else {
req.url =
req.url.slice(0, qIdx).replaceAll("//", "/") + req.url.slice(qIdx)
}

next()
})
app.use(loggingMiddleware)
Expand Down Expand Up @@ -211,31 +220,39 @@ app.get(
"pc-prod_6"
}

if (req.query.issuer === STEAM_NAMESPACE_2021) {
config.Versions[0].SERVER_VER.GlobalAuthentication.RequestedAudience =
"steam-prod_8"
switch (req.query.issuer) {
case STEAM_NAMESPACE_2021:
config.Versions[0].SERVER_VER.GlobalAuthentication.RequestedAudience =
"steam-prod_8"
break
case "https://appleid.apple.com":
config.Versions[0].SERVER_VER.GlobalAuthentication.RequestedAudience =
"apple-prod_8"
break
}

if (req.params.audience === "scpc-prod") {
switch (req.params.audience) {
// sniper challenge is a different game/audience
config.Versions[0].Name = "scpc-prod"
config.Versions[0].GAME_VER = "7.3.0"
config.Versions[0].SERVER_VER.GlobalAuthentication.RequestedAudience =
"scpc-prod"
case "scpc-prod":
config.Versions[0].Name = "scpc-prod"
config.Versions[0].GAME_VER = "7.3.0"
config.Versions[0].SERVER_VER.GlobalAuthentication.RequestedAudience =
"scpc-prod"
break
case "macos-prod":
config.Versions[0].Name = "macos-prod"
break
case "macossteam-prod":
config.Versions[0].Name = "macossteam-prod"
break
}

config.Versions[0].ISSUER_ID = req.query.issuer || "*"

config.Versions[0].SERVER_VER.Metrics.MetricsServerHost = `${proto}://${serverhost}`

config.Versions[0].SERVER_VER.Authentication.AuthenticationHost = `${proto}://${serverhost}`

config.Versions[0].SERVER_VER.Configuration.Url = `${proto}://${serverhost}/files/onlineconfig.json`

config.Versions[0].SERVER_VER.Configuration.AgreementUrl = `${proto}://${serverhost}/files/privacypolicy/hm3/privacypolicy.json`

config.Versions[0].SERVER_VER.Resources.ResourcesServicePath = `${proto}://${serverhost}/files`

config.Versions[0].SERVER_VER.GlobalAuthentication.AuthenticationHost = `${proto}://${serverhost}`

res.json(config)
Expand Down
153 changes: 91 additions & 62 deletions components/oauthToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,17 @@ export const JWT_SECRET = PEACOCK_DEV
: randomBytes(32).toString("hex")

export type OAuthTokenBody = {
grant_type: "external_steam" | "external_epic" | "refresh_token"
grant_type:
| "external_steam"
| "external_epic"
| "external_apple"
| "refresh_token"
steam_userid?: string
epic_userid?: string
apple_userid?: string
apple_refreshtoken?: string
device_os?: string
device_id?: string
access_token: string
pId?: string
locale: string
Expand Down Expand Up @@ -85,78 +93,93 @@ export async function handleOAuthToken(
notBefore: -60000,
expiresIn: 6000,
issuer: "auth.hitman.io",
audience: isScpc ? "scpc-prod" : "pc_prod_8",
audience: (() => {
if (isScpc) return "scpc-prod"
if (req.body.grant_type === "external_apple") return "macos-prod"
return "pc_prod_8"
})(),
noTimestamp: true,
}

let external_platform: "steam" | "epic",
let external_platform: "steam" | "epic" | "apple",
external_userid: string,
external_users_folder: "steamids" | "epicids",
external_users_folder: "steamids" | "epicids" | "appleids",
external_appid: string

if (req.body.grant_type === "external_steam") {
if (!/^\d{1,20}$/.test(req.body.steam_userid || "")) {
return error400 // invalid steam user id
}

external_platform = "steam"
external_userid = req.body.steam_userid || ""
external_users_folder = "steamids"
external_appid = req.body.steam_appid
} else if (req.body.grant_type === "external_epic") {
if (!/^[\da-f]{32}$/.test(req.body.epic_userid || "")) {
return error400 // invalid epic user id
}
switch (req.body.grant_type) {
case "external_steam":
if (!/^\d{1,20}$/.test(req.body.steam_userid || "")) {
return error400 // invalid steam user id
}

const epic_token = decode(
req.body.access_token.replace(/^eg1~/, ""),
) as {
appid: string
app: string
}
external_platform = "steam"
external_userid = req.body.steam_userid || ""
external_users_folder = "steamids"
external_appid = req.body.steam_appid
break
case "external_epic": {
if (!/^[\da-f]{32}$/.test(req.body.epic_userid || "")) {
return error400 // invalid epic user id
}

if (!epic_token || !(epic_token.appid || epic_token.app)) {
return error400 // invalid epic access token
}
const epic_token = decode(
req.body.access_token.replace(/^eg1~/, ""),
) as {
appid: string
app: string
}

external_appid = epic_token.appid || epic_token.app
external_platform = "epic"
external_userid = req.body.epic_userid || ""
external_users_folder = "epicids"
} else if (req.body.grant_type === "refresh_token") {
// send back the token from the request (re-signed so the timestamps update)
extractToken(req) // init req.jwt
// remove signOptions from existing jwt
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.nbf // notBefore
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.exp // expiresIn
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.iss // issuer
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.aud // audience

if (!isScpc) {
if (userAuths.has(req.jwt.unique_name)) {
userAuths
.get(req.jwt.unique_name)!
._doRefresh()
.then(() => undefined)
.catch(() => {
log(LogLevel.WARN, "Failed authentication refresh.")
userAuths.get(req.jwt.unique_name)!.initialized = false
})
if (!epic_token || !(epic_token.appid || epic_token.app)) {
return error400 // invalid epic access token
}
}

return {
access_token: sign(req.jwt, JWT_SECRET, signOptions),
token_type: "bearer",
expires_in: 5000,
refresh_token: randomUUID(),
external_appid = epic_token.appid || epic_token.app
external_platform = "epic"
external_userid = req.body.epic_userid || ""
external_users_folder = "epicids"
break
}
} else {
return error406 // unsupported auth method
case "external_apple":
external_platform = "apple"
external_userid = req.body.apple_userid || ""
external_users_folder = "appleids"
external_appid = "apple"
break
case "refresh_token":
// send back the token from the request (re-signed so the timestamps update)
extractToken(req) // init req.jwt
// remove signOptions from existing jwt
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.nbf // notBefore
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.exp // expiresIn
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.iss // issuer
// @ts-expect-error Non-optional, we're reassigning.
delete req.jwt.aud // audience

if (!isScpc) {
if (userAuths.has(req.jwt.unique_name)) {
userAuths
.get(req.jwt.unique_name)!
._doRefresh()
.then(() => undefined)
.catch(() => {
log(LogLevel.WARN, "Failed authentication refresh.")
userAuths.get(req.jwt.unique_name)!.initialized =
false
})
}
}

return {
access_token: sign(req.jwt, JWT_SECRET, signOptions),
token_type: "bearer",
expires_in: 5000,
refresh_token: randomUUID(),
}
default:
return error406 // unsupported auth method
}

if (req.body.pId && !uuidRegex.test(req.body.pId)) {
Expand All @@ -165,7 +188,8 @@ export async function handleOAuthToken(

const isHitman3 =
external_appid === "fghi4567xQOCheZIin0pazB47qGUvZw4" ||
external_appid === STEAM_NAMESPACE_2021
external_appid === STEAM_NAMESPACE_2021 ||
external_platform === "apple"

let gameVersion: GameVersion = "h1"

Expand Down Expand Up @@ -253,6 +277,8 @@ export async function handleOAuthToken(
userData.SteamId = req.body.steam_userid!
} else if (external_platform === "epic") {
userData.EpicId = req.body.epic_userid!
} else if (external_platform === "apple") {
userData.AppleId = req.body.apple_userid!
}

if (Object.hasOwn(userData.Extensions, "inventory")) {
Expand Down Expand Up @@ -292,6 +318,9 @@ export async function handleOAuthToken(
gameVersion,
STEAM_NAMESPACE_2021,
).get(req.body.pId!)
} else if (external_platform === "apple") {
// TODO
return []
} else {
log(LogLevel.ERROR, "Unsupported platform.")
return []
Expand Down
4 changes: 4 additions & 0 deletions components/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,10 @@ export type UserProfile = {
steam?: string
gog?: string
xbox?: string
/** @deprecated */
stadia?: string
apple?: string
nintendo?: string
}
Extensions: {
/**
Expand Down Expand Up @@ -574,6 +577,7 @@ export type UserProfile = {
DevId: string | null
SteamId: string | null
EpicId: string | null
AppleId?: string | null
NintendoId: string | null
XboxLiveId: string | null
PSNAccountId: string | null
Expand Down
2 changes: 1 addition & 1 deletion patcher-darwin/.idea/editor.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading