diff --git a/app/Auth/Access/UserTokenService.php b/app/Auth/Access/UserTokenService.php
index 40f363ee1..34f3b2851 100644
--- a/app/Auth/Access/UserTokenService.php
+++ b/app/Auth/Access/UserTokenService.php
@@ -61,7 +61,7 @@ class UserTokenService
         }
 
         if ($this->entryExpired($entry)) {
-            throw new UserTokenExpiredException("Token of id {$token->id} has expired.", $entry->user_id);
+            throw new UserTokenExpiredException("Token of id {$entry->id} has expired.", $entry->user_id);
         }
 
         return $entry->user_id;
diff --git a/tests/Auth/UserInviteTest.php b/tests/Auth/UserInviteTest.php
new file mode 100644
index 000000000..331262690
--- /dev/null
+++ b/tests/Auth/UserInviteTest.php
@@ -0,0 +1,111 @@
+<?php namespace Tests;
+
+
+use BookStack\Auth\Access\UserInviteService;
+use BookStack\Auth\User;
+use BookStack\Notifications\UserInvite;
+use Carbon\Carbon;
+use DB;
+use Notification;
+
+class UserInviteTest extends TestCase
+{
+
+    public function test_user_creation_creates_invite()
+    {
+        Notification::fake();
+        $admin = $this->getAdmin();
+
+        $this->actingAs($admin)->post('/settings/users/create', [
+            'name' => 'Barry',
+            'email' => 'tester@example.com',
+            'send_invite' => 'true',
+        ]);
+
+        $newUser = User::query()->where('email', '=', 'tester@example.com')->orderBy('id', 'desc')->first();
+
+        Notification::assertSentTo($newUser, UserInvite::class);
+        $this->assertDatabaseHas('user_invites', [
+            'user_id' => $newUser->id
+        ]);
+    }
+
+    public function test_invite_set_password()
+    {
+        Notification::fake();
+        $user = $this->getViewer();
+        $inviteService = app(UserInviteService::class);
+
+        $inviteService->sendInvitation($user);
+        $token = DB::table('user_invites')->where('user_id', '=', $user->id)->first()->token;
+
+        $setPasswordPageResp = $this->get('/register/invite/' . $token);
+        $setPasswordPageResp->assertSuccessful();
+        $setPasswordPageResp->assertSee('Welcome to BookStack!');
+        $setPasswordPageResp->assertSee('Password');
+        $setPasswordPageResp->assertSee('Confirm Password');
+
+        $setPasswordResp = $this->followingRedirects()->post('/register/invite/' . $token, [
+            'password' => 'my test password',
+        ]);
+        $setPasswordResp->assertSee('Password set, you now have access to BookStack!');
+        $newPasswordValid = auth()->validate([
+            'email' => $user->email,
+            'password' => 'my test password'
+        ]);
+        $this->assertTrue($newPasswordValid);
+        $this->assertDatabaseMissing('user_invites', [
+            'user_id' => $user->id
+        ]);
+    }
+
+    public function test_invite_set_has_password_validation()
+    {
+        Notification::fake();
+        $user = $this->getViewer();
+        $inviteService = app(UserInviteService::class);
+
+        $inviteService->sendInvitation($user);
+        $token = DB::table('user_invites')->where('user_id', '=', $user->id)->first()->token;
+
+        $shortPassword = $this->followingRedirects()->post('/register/invite/' . $token, [
+            'password' => 'mypas',
+        ]);
+        $shortPassword->assertSee('The password must be at least 6 characters.');
+
+        $noPassword = $this->followingRedirects()->post('/register/invite/' . $token, [
+            'password' => '',
+        ]);
+        $noPassword->assertSee('The password field is required.');
+
+        $this->assertDatabaseHas('user_invites', [
+            'user_id' => $user->id
+        ]);
+    }
+
+    public function test_non_existent_invite_token_redirects_to_home()
+    {
+        $setPasswordPageResp = $this->get('/register/invite/' . str_random(12));
+        $setPasswordPageResp->assertRedirect('/');
+
+        $setPasswordResp = $this->post('/register/invite/' . str_random(12), ['password' => 'Password Test']);
+        $setPasswordResp->assertRedirect('/');
+    }
+
+    public function test_token_expires_after_two_weeks()
+    {
+        Notification::fake();
+        $user = $this->getViewer();
+        $inviteService = app(UserInviteService::class);
+
+        $inviteService->sendInvitation($user);
+        $tokenEntry = DB::table('user_invites')->where('user_id', '=', $user->id)->first();
+        DB::table('user_invites')->update(['created_at' => Carbon::now()->subDays(14)->subHour(1)]);
+
+        $setPasswordPageResp = $this->get('/register/invite/' . $tokenEntry->token);
+        $setPasswordPageResp->assertRedirect('/password/email');
+        $setPasswordPageResp->assertSessionHas('error', 'This invitation link has expired. You can instead try to reset your account password.');
+    }
+
+
+}
\ No newline at end of file