@@ -49,9 +49,17 @@ export const JWT_SECRET = PEACOCK_DEV
4949 : randomBytes ( 32 ) . toString ( "hex" )
5050
5151export type OAuthTokenBody = {
52- grant_type : "external_steam" | "external_epic" | "refresh_token"
52+ grant_type :
53+ | "external_steam"
54+ | "external_epic"
55+ | "external_apple"
56+ | "refresh_token"
5357 steam_userid ?: string
5458 epic_userid ?: string
59+ apple_userid ?: string
60+ apple_refreshtoken ?: string
61+ device_os ?: string
62+ device_id ?: string
5563 access_token : string
5664 pId ?: string
5765 locale : string
@@ -85,78 +93,93 @@ export async function handleOAuthToken(
8593 notBefore : - 60000 ,
8694 expiresIn : 6000 ,
8795 issuer : "auth.hitman.io" ,
88- audience : isScpc ? "scpc-prod" : "pc_prod_8" ,
96+ audience : ( ( ) => {
97+ if ( isScpc ) return "scpc-prod"
98+ if ( req . body . grant_type === "external_apple" ) return "macos-prod"
99+ return "pc_prod_8"
100+ } ) ( ) ,
89101 noTimestamp : true ,
90102 }
91103
92- let external_platform : "steam" | "epic" ,
104+ let external_platform : "steam" | "epic" | "apple" ,
93105 external_userid : string ,
94- external_users_folder : "steamids" | "epicids" ,
106+ external_users_folder : "steamids" | "epicids" | "appleids" ,
95107 external_appid : string
96108
97- if ( req . body . grant_type === "external_steam" ) {
98- if ( ! / ^ \d { 1 , 20 } $ / . test ( req . body . steam_userid || "" ) ) {
99- return error400 // invalid steam user id
100- }
101-
102- external_platform = "steam"
103- external_userid = req . body . steam_userid || ""
104- external_users_folder = "steamids"
105- external_appid = req . body . steam_appid
106- } else if ( req . body . grant_type === "external_epic" ) {
107- if ( ! / ^ [ \d a - f ] { 32 } $ / . test ( req . body . epic_userid || "" ) ) {
108- return error400 // invalid epic user id
109- }
109+ switch ( req . body . grant_type ) {
110+ case "external_steam" :
111+ if ( ! / ^ \d { 1 , 20 } $ / . test ( req . body . steam_userid || "" ) ) {
112+ return error400 // invalid steam user id
113+ }
110114
111- const epic_token = decode (
112- req . body . access_token . replace ( / ^ e g 1 ~ / , "" ) ,
113- ) as {
114- appid : string
115- app : string
116- }
115+ external_platform = "steam"
116+ external_userid = req . body . steam_userid || ""
117+ external_users_folder = "steamids"
118+ external_appid = req . body . steam_appid
119+ break
120+ case "external_epic" : {
121+ if ( ! / ^ [ \d a - f ] { 32 } $ / . test ( req . body . epic_userid || "" ) ) {
122+ return error400 // invalid epic user id
123+ }
117124
118- if ( ! epic_token || ! ( epic_token . appid || epic_token . app ) ) {
119- return error400 // invalid epic access token
120- }
125+ const epic_token = decode (
126+ req . body . access_token . replace ( / ^ e g 1 ~ / , "" ) ,
127+ ) as {
128+ appid : string
129+ app : string
130+ }
121131
122- external_appid = epic_token . appid || epic_token . app
123- external_platform = "epic"
124- external_userid = req . body . epic_userid || ""
125- external_users_folder = "epicids"
126- } else if ( req . body . grant_type === "refresh_token" ) {
127- // send back the token from the request (re-signed so the timestamps update)
128- extractToken ( req ) // init req.jwt
129- // remove signOptions from existing jwt
130- // @ts -expect-error Non-optional, we're reassigning.
131- delete req . jwt . nbf // notBefore
132- // @ts -expect-error Non-optional, we're reassigning.
133- delete req . jwt . exp // expiresIn
134- // @ts -expect-error Non-optional, we're reassigning.
135- delete req . jwt . iss // issuer
136- // @ts -expect-error Non-optional, we're reassigning.
137- delete req . jwt . aud // audience
138-
139- if ( ! isScpc ) {
140- if ( userAuths . has ( req . jwt . unique_name ) ) {
141- userAuths
142- . get ( req . jwt . unique_name ) !
143- . _doRefresh ( )
144- . then ( ( ) => undefined )
145- . catch ( ( ) => {
146- log ( LogLevel . WARN , "Failed authentication refresh." )
147- userAuths . get ( req . jwt . unique_name ) ! . initialized = false
148- } )
132+ if ( ! epic_token || ! ( epic_token . appid || epic_token . app ) ) {
133+ return error400 // invalid epic access token
149134 }
150- }
151135
152- return {
153- access_token : sign ( req . jwt , JWT_SECRET , signOptions ) ,
154- token_type : "bearer" ,
155- expires_in : 5000 ,
156- refresh_token : randomUUID ( ) ,
136+ external_appid = epic_token . appid || epic_token . app
137+ external_platform = "epic"
138+ external_userid = req . body . epic_userid || ""
139+ external_users_folder = "epicids"
140+ break
157141 }
158- } else {
159- return error406 // unsupported auth method
142+ case "external_apple" :
143+ external_platform = "apple"
144+ external_userid = req . body . apple_userid || ""
145+ external_users_folder = "appleids"
146+ external_appid = "apple"
147+ break
148+ case "refresh_token" :
149+ // send back the token from the request (re-signed so the timestamps update)
150+ extractToken ( req ) // init req.jwt
151+ // remove signOptions from existing jwt
152+ // @ts -expect-error Non-optional, we're reassigning.
153+ delete req . jwt . nbf // notBefore
154+ // @ts -expect-error Non-optional, we're reassigning.
155+ delete req . jwt . exp // expiresIn
156+ // @ts -expect-error Non-optional, we're reassigning.
157+ delete req . jwt . iss // issuer
158+ // @ts -expect-error Non-optional, we're reassigning.
159+ delete req . jwt . aud // audience
160+
161+ if ( ! isScpc ) {
162+ if ( userAuths . has ( req . jwt . unique_name ) ) {
163+ userAuths
164+ . get ( req . jwt . unique_name ) !
165+ . _doRefresh ( )
166+ . then ( ( ) => undefined )
167+ . catch ( ( ) => {
168+ log ( LogLevel . WARN , "Failed authentication refresh." )
169+ userAuths . get ( req . jwt . unique_name ) ! . initialized =
170+ false
171+ } )
172+ }
173+ }
174+
175+ return {
176+ access_token : sign ( req . jwt , JWT_SECRET , signOptions ) ,
177+ token_type : "bearer" ,
178+ expires_in : 5000 ,
179+ refresh_token : randomUUID ( ) ,
180+ }
181+ default :
182+ return error406 // unsupported auth method
160183 }
161184
162185 if ( req . body . pId && ! uuidRegex . test ( req . body . pId ) ) {
@@ -165,7 +188,8 @@ export async function handleOAuthToken(
165188
166189 const isHitman3 =
167190 external_appid === "fghi4567xQOCheZIin0pazB47qGUvZw4" ||
168- external_appid === STEAM_NAMESPACE_2021
191+ external_appid === STEAM_NAMESPACE_2021 ||
192+ external_platform === "apple"
169193
170194 let gameVersion : GameVersion = "h1"
171195
@@ -253,6 +277,8 @@ export async function handleOAuthToken(
253277 userData . SteamId = req . body . steam_userid !
254278 } else if ( external_platform === "epic" ) {
255279 userData . EpicId = req . body . epic_userid !
280+ } else if ( external_platform === "apple" ) {
281+ userData . AppleId = req . body . apple_userid !
256282 }
257283
258284 if ( Object . hasOwn ( userData . Extensions , "inventory" ) ) {
@@ -292,6 +318,9 @@ export async function handleOAuthToken(
292318 gameVersion ,
293319 STEAM_NAMESPACE_2021 ,
294320 ) . get ( req . body . pId ! )
321+ } else if ( external_platform === "apple" ) {
322+ // TODO
323+ return [ ]
295324 } else {
296325 log ( LogLevel . ERROR , "Unsupported platform." )
297326 return [ ]
0 commit comments