yushijinhun / authlib-injector

Build your own Minecraft authentication system.
https://authlib-injector.yushi.moe
GNU Affero General Public License v3.0
744 stars 67 forks source link

What addresses are used for skins? #118

Closed Shark-vil closed 3 years ago

Shark-vil commented 3 years ago

I wanted to implement my own authentication server and made the main API routes.

get /
post /signout
post /authenticate
post /refresh
post /validate
post /invalidate
post /sessionserver/session/minecraft/join
post /sessionserver/session/minecraft/hasJoined
get /sessionserver/session/minecraft/profile/{profile_id}

All links work as expected. However, the last link only works for the client. As I understand it, using this link you cannot get skins of other players during the game. Could you tell me in which direction I need to study the API in order to load skins of other players for the client?

yushijinhun commented 3 years ago

Please refer to the API specification (unfortunately it's in Chinese, but Google Translate should work).

If you can't get other players' skins displayed in the game, please turn on -Dauthlibinjector.debug option and upload the MC client console log.

Shark-vil commented 3 years ago

Please refer to the API specification (unfortunately it's in Chinese, but Google Translate should work).

If you can't get other players' skins displayed in the game, please turn on -Dauthlibinjector.debug option and upload the MC client console log.

I found the reason. For himself, the client requests a skin with the parameter "unsigned=true". But for other players, a similar request is sent with the "unsigned=false" parameter. After that I get the message:

Signature is missing from textures payload

Is it possible to always use a parameter in the false state? I don't quite understand how signatures work, I can't find good documentation on how to create them and how to associate them with textures. Therefore, I would like to always ignore this parameter.

WooMai commented 3 years ago

Please refer to the API specification (unfortunately it's in Chinese, but Google Translate should work). If you can't get other players' skins displayed in the game, please turn on -Dauthlibinjector.debug option and upload the MC client console log.

I found the reason. For himself, the client requests a skin with the parameter "unsigned=true". But for other players, a similar request is sent with the "unsigned=false" parameter. After that I get the message:

Signature is missing from textures payload

Is it possible to always use a parameter in the false state? I don't quite understand how signatures work, I can't find good documentation on how to create them and how to associate them with textures. Therefore, I would like to always ignore this parameter.

Currently, there is no way to disable the signature.

But the document is here (I think Google Translate will work on this part of document): https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#%E8%A7%92%E8%89%B2%E4%BF%A1%E6%81%AF%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96

I can also paste my implementation code here later if you need.

Shark-vil commented 3 years ago

Please refer to the API specification (unfortunately it's in Chinese, but Google Translate should work). If you can't get other players' skins displayed in the game, please turn on -Dauthlibinjector.debug option and upload the MC client console log.

I found the reason. For himself, the client requests a skin with the parameter "unsigned=true". But for other players, a similar request is sent with the "unsigned=false" parameter. After that I get the message:

Signature is missing from textures payload

Is it possible to always use a parameter in the false state? I don't quite understand how signatures work, I can't find good documentation on how to create them and how to associate them with textures. Therefore, I would like to always ignore this parameter.

Currently, there is no way to disable the signature.

But the document is here (I think Google Translate will work on this part of document): https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#%E8%A7%92%E8%89%B2%E4%BF%A1%E6%81%AF%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96

I can also paste my implementation code here later if you need.

It would be nice if you can attach an example. I tried to do everything according to the available documentation, but to no avail. Here's what I did:

  1. Created a private key using the command - "openssl genrsa 4096"
  2. I entered the command - "openssl rsa -pubout", and inserted the previously created private key there in order to get the public key on its basis.
  3. I uploaded both keys to the server and started using them. The public key is displayed at this address "https://mc.pipbuck.ru/api/authserver/".
  4. I use the private key when retrieving skin data. I am using PHP, the code looks like this:
    
    $properties_textures_value = json_encode([
    'timestamp' => strtotime('now'),
    'profileId' => $profile_id,
    'profileName' => $user->name,
    'signatureRequired' => !$unsigned,
    'textures' => $textures,
    ]);

$properties_textures_value_base64 = base64_encode($properties_textures_value); $private_key = $this->get_private_key();

if (!$unsigned) { openssl_sign($properties_textures_value_base64, $signature, $private_key); $signature_base64 = base64_encode($signature); array_push($properties, [ 'name' => 'textures', 'value' => $properties_textures_value_base64, 'signature' => $signature_base64, ]); } else { array_push($properties, [ 'name' => 'textures', 'value' => $properties_textures_value_base64 ]); }

return response()->json([ 'id' => $profile_id, 'name' => $user->name, 'properties' => $properties ], 200);


I get a result like this:
https://mc.pipbuck.ru/api/authserver/sessionserver/session/minecraft/profile/338da3b48cf74a438de8755026535a6d

But now he writes to me that:
"Textures payload has been tampered with (signature invalid)"

I have no idea what I'm doing wrong.  I understand that this problem is not related to your development, and are solely my misunderstanding of the system. But I hope you will help to understand in which direction you need to move.
yushijinhun commented 3 years ago

The documentation is not very clear and needs to be improved. Here are some examples of implementing texture signature:

WooMai commented 3 years ago

Here is my implementation, which is in Laravel's ORM model, FYR

class Profile extends Model
{

    protected $connection = 'default';
    protected $table = 'profile';

    /**
     * @return array
     */
    public function getSerialization($properties = false, $unsigned = true)
    {
        $arr = [
            'id' => $this->uuid,
            'name' => $this->name
        ];

        if ($properties) {

            $textures = array();

            // steve
            if ($this->skin_default != null) {
                $url = Config::get('base_url') . "/textures/$this->skin_default";
                $textures['SKIN'] = array(
                    'url' => $url,
                    'metadata' => array(
                        'model' => 'default'
                    )
                );
            }

            // alex
            if ($this->skin_slim != null) {
                $url = Config::get('base_url') . "/textures/$this->skin_slim";
                $textures['SKIN'] = array(
                    'url' => $url,
                    'metadata' => array(
                        'model' => 'slim'
                    )
                );
            }

            // cape
            if ($this->cape != null) {
                $url = Config::get('base_url') . "/textures/$this->cape";
                $textures['CAPE'] = array(
                    'url' => $url
                );
            }

            $value = array(
                'timestamp' => time() * 1000,
                'profileId' => $this->uuid,
                'profileName' => $this->name,
                'textures' => $textures,
                'signatureRequired' => !$unsigned
            );

            $value = base64_encode(json_encode($value, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK));

            $properties = array(
                array(
                    'name' => 'textures',
                    'value' => $value
                )
            );

            if (!$unsigned) {
                foreach ($properties as $i => $p) {
                    $key = openssl_pkey_get_private(Config::get('yggdrasil_private_key'));
                    if (!$key) throw new \Exception("Can not parse private key.");
                    openssl_sign($p['value'], $s, $key);
                    $properties[$i]['signature'] = base64_encode($s);
                }
            }

            $arr['properties'] = $properties;
        }

        return $arr;
    }
}
Shark-vil commented 3 years ago

Well, I seem to have almost achieved what I wanted, but I have another question.

chrome_epPhzwfjFc

The documentation says that I have to output the public key from the main api (index) link. By default, however, the key is displayed with "\r\n, \n" hyphenation characters. Should I get rid of them as shown in the screenshot or not? Or does this string also need to be converted to base64 format?

Shark-vil commented 3 years ago

Okay, I checked this output with "openssl_verify", and it looks like I should leave it, java decodes everything by itself.

Shark-vil commented 3 years ago

That's it, it works! Thank you so much for your help! 😊