39 public const MOJANG_AUDIENCE =
"api://auth-minecraft-services/multiplayer";
41 private const CLOCK_DRIFT_MAX = 60;
46 private static function checkExpiry(
JwtBodyRfc7519 $claims) :
void{
48 if(isset($claims->nbf) && $claims->nbf > $time + self::CLOCK_DRIFT_MAX){
49 throw new VerifyLoginException(
"JWT not yet valid", KnownTranslationFactory::pocketmine_disconnect_invalidSession_tooEarly());
52 if(isset($claims->exp) && $claims->exp < $time - self::CLOCK_DRIFT_MAX){
53 throw new VerifyLoginException(
"JWT expired", KnownTranslationFactory::pocketmine_disconnect_invalidSession_tooLate());
60 private static function validateAuthToken(
string $jwt,
string $signingKeyDer, ?
string $issuer,
string $audience,
XboxAuthJwtBody|
SelfSignedJwtBody $claims) :
void{
63 throw new VerifyLoginException(
"Invalid JWT signature", KnownTranslationFactory::pocketmine_disconnect_invalidSession_badSignature());
75 $mapper = new \JsonMapper();
76 $mapper->bExceptionOnUndefinedProperty =
false;
77 $mapper->bExceptionOnMissingData =
true;
78 $mapper->bStrictObjectTypeChecking =
true;
79 $mapper->bEnforceMapType =
false;
80 $mapper->bRemoveUndefinedAttributes =
true;
83 $mapper->map($claimsArray, $claims);
84 }
catch(\JsonMapper_Exception $e){
88 if($issuer !==
null && (!isset($claims->iss) || $claims->iss !== $issuer)){
92 if(!isset($claims->aud) || $claims->aud !== $audience){
96 self::checkExpiry($claims);
104 self::validateAuthToken($jwt, $signingKeyDer,
null, $audience, $claims);
113 self::validateAuthToken($jwt, $signingKeyDer, $issuer, $audience, $claims);
122 self::validateSelfSignedToken($jwt, $expectedKeyDer);
125 [, $claimsArray, ] = JwtUtils::parse($jwt);
127 $mapper = new \JsonMapper();
128 $mapper->bExceptionOnUndefinedProperty =
false;
129 $mapper->bExceptionOnMissingData =
true;
130 $mapper->bStrictObjectTypeChecking =
true;
131 $mapper->bEnforceMapType =
false;
132 $mapper->bRemoveUndefinedAttributes =
true;
136 }
catch(\JsonMapper_Exception $e){
140 self::checkExpiry($claims);
150 [$headersArray, ] =
JwtUtils::parse($jwt);
155 $mapper = new \JsonMapper();
156 $mapper->bExceptionOnMissingData =
true;
157 $mapper->bExceptionOnUndefinedProperty =
true;
158 $mapper->bStrictObjectTypeChecking =
true;
159 $mapper->bEnforceMapType =
false;
163 $headers = $mapper->map($headersArray,
new SelfSignedJwtHeader());
164 }
catch(\JsonMapper_Exception $e){
165 throw new VerifyLoginException(
"Invalid JWT header: " . $e->getMessage(),
null, 0, $e);
168 $headerDerKey = base64_decode($headers->x5u,
true);
169 if($headerDerKey ===
false){
170 throw new VerifyLoginException(
"Invalid JWT public key: base64 decoding error decoding x5u");
172 if($expectedKeyDer !==
null && $headerDerKey !== $expectedKeyDer){
174 throw new VerifyLoginException(
"Invalid JWT signature", KnownTranslationFactory::pocketmine_disconnect_invalidSession_badSignature());
178 if(!JwtUtils::verify($jwt, $headerDerKey, ec:
true)){
179 throw new VerifyLoginException(
"Invalid JWT signature", KnownTranslationFactory::pocketmine_disconnect_invalidSession_badSignature());
181 }
catch(JwtException $e){
182 throw new VerifyLoginException($e->getMessage(),
null, 0, $e);