Why does Laravel Sanctum append token key to the plainTextToken?

1.8k Views Asked by At

Referring to the following code from Sanctum:

public function createToken(string $name, array $abilities = ['*'])
    {
        $token = $this->tokens()->create([
            'name' => $name,
            'token' => hash('sha256', $plainTextToken = Str::random(40)),
            'abilities' => $abilities,
        ]);

        return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken);
    }

Source: https://github.com/laravel/sanctum/blob/31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473/src/HasApiTokens.php#L44-L53

Why does Sanctum append $token->getKey() to the $plainTextToken? In other words, what is the purpose of the $token->getKey() part? Is it used during authentication as a key to retrieve the encrypted token string and compare it using Hash::check or something?

I have another question: The migration that creates the personal_access_tokens table indicates that the token column is unique. However, in the above code I can see that the value stored in the token column is just a hashed value of a random string hash('sha256', $plainTextToken = Str::random(40)). Does the hash function always return unique values so that it doesn't violate the unique constraint?

My first thought was that the $token->getKey() should be appended to the encrypted string to make it unique. But I think this is not the case.

1

There are 1 best solutions below

0
On

Sanctum createToken function creates a string of 40 characters,

Str::random(40)

then hashs it but before hashing, stores it into a variable $plainTextToken.

hash('sha256', $plainTextToken = Str::random(40))

Hashed token goes to the database and un-hashed token returns to user.

 $token = $this->tokens()->create([
        'name' => $name,
        'token' => hash('sha256', $plainTextToken = Str::random(40)),
        'abilities' => $abilities,
    ]);

Return the un-hashed part to the user with primary key of database record. the primary key helps to find the token quickly. if you remove primary key it will work too.

 return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken)