diff --git a/database/migrations/2019_07_17_000001_make_client_secret_nullable.php b/database/migrations/2019_07_17_000001_make_client_secret_nullable.php
new file mode 100644
index 000000000..4074a77b4
--- /dev/null
+++ b/database/migrations/2019_07_17_000001_make_client_secret_nullable.php
@@ -0,0 +1,32 @@
+string('secret', 100)->nullable()->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('oauth_clients', function (Blueprint $table) {
+ $table->string('secret', 100)->change();
+ });
+ }
+}
diff --git a/resources/js/components/Clients.vue b/resources/js/components/Clients.vue
index 3797faae9..beecfa0b9 100644
--- a/resources/js/components/Clients.vue
+++ b/resources/js/components/Clients.vue
@@ -125,6 +125,22 @@
+
+
+
@@ -222,7 +238,8 @@
createForm: {
errors: [],
name: '',
- redirect: ''
+ redirect: '',
+ confidential: true
},
editForm: {
diff --git a/src/Bridge/ClientRepository.php b/src/Bridge/ClientRepository.php
index 7623d2da6..82077b6c9 100644
--- a/src/Bridge/ClientRepository.php
+++ b/src/Bridge/ClientRepository.php
@@ -37,7 +37,7 @@ public function getClientEntity($clientIdentifier)
}
return new Client(
- $clientIdentifier, $record->name, $record->redirect, ! is_null($record->secret)
+ $clientIdentifier, $record->name, $record->redirect, $record->confidential()
);
}
@@ -52,7 +52,7 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType)
return false;
}
- return hash_equals($record->secret, (string) $clientSecret);
+ return $record->confidential() && hash_equals($record->secret, (string) $clientSecret);
}
/**
@@ -75,8 +75,6 @@ protected function handlesGrant($record, $grantType)
return $record->personal_access_client;
case 'password':
return $record->password_client;
- case 'client_credentials':
- return ! empty($record->secret);
default:
return true;
}
diff --git a/src/Client.php b/src/Client.php
index be4cdc1d4..1b094b66d 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -82,4 +82,14 @@ public function firstParty()
{
return $this->personal_access_client || $this->password_client;
}
+
+ /**
+ * Determine if the client is a confidential client.
+ *
+ * @return bool
+ */
+ public function confidential()
+ {
+ return ! empty($this->secret);
+ }
}
diff --git a/src/ClientRepository.php b/src/ClientRepository.php
index ce0a19aae..0b595cf42 100644
--- a/src/ClientRepository.php
+++ b/src/ClientRepository.php
@@ -106,14 +106,19 @@ public function personalAccessClient()
* @param string $redirect
* @param bool $personalAccess
* @param bool $password
+ * @param bool $confidential
* @return \Laravel\Passport\Client
*/
- public function create($userId, $name, $redirect, $personalAccess = false, $password = false)
+ public function create($userId, $name, $redirect, $personalAccess = false, $password = false, $confidential = true)
{
+ if ($personalAccess) {
+ $confidential = true;
+ }
+
$client = Passport::client()->forceFill([
'user_id' => $userId,
'name' => $name,
- 'secret' => Str::random(40),
+ 'secret' => $confidential ? Str::random(40) : null,
'redirect' => $redirect,
'personal_access_client' => $personalAccess,
'password_client' => $password,
diff --git a/src/Http/Controllers/ClientController.php b/src/Http/Controllers/ClientController.php
index 10895bf3e..255dbec69 100644
--- a/src/Http/Controllers/ClientController.php
+++ b/src/Http/Controllers/ClientController.php
@@ -73,10 +73,12 @@ public function store(Request $request)
$this->validation->make($request->all(), [
'name' => 'required|max:255',
'redirect' => ['required', $this->redirectRule],
+ 'confidential' => 'boolean',
])->validate();
return $this->clients->create(
- $request->user()->getKey(), $request->name, $request->redirect
+ $request->user()->getKey(), $request->name, $request->redirect,
+ false, false, (bool) $request->input('confidential', true)
)->makeVisible('secret');
}
diff --git a/tests/BridgeClientRepositoryTest.php b/tests/BridgeClientRepositoryTest.php
index b861a96c3..3c82bfbc4 100644
--- a/tests/BridgeClientRepositoryTest.php
+++ b/tests/BridgeClientRepositoryTest.php
@@ -153,4 +153,9 @@ public function firstParty()
{
return $this->personal_access_client || $this->password_client;
}
+
+ public function confidential()
+ {
+ return ! empty($this->secret);
+ }
}
diff --git a/tests/ClientControllerTest.php b/tests/ClientControllerTest.php
index fdaac1bf2..5ae841059 100644
--- a/tests/ClientControllerTest.php
+++ b/tests/ClientControllerTest.php
@@ -48,7 +48,7 @@ public function test_clients_can_be_stored()
$clients->shouldReceive('create')
->once()
- ->with(1, 'client name', 'http://localhost')
+ ->with(1, 'client name', 'http://localhost', false, false, true)
->andReturn($client = new Client);
$redirectRule = m::mock(RedirectRule::class);
@@ -60,6 +60,46 @@ public function test_clients_can_be_stored()
], [
'name' => 'required|max:255',
'redirect' => ['required', $redirectRule],
+ 'confidential' => 'boolean',
+ ])->andReturn($validator);
+ $validator->shouldReceive('validate')->once();
+
+ $controller = new ClientController(
+ $clients, $validator, $redirectRule
+ );
+
+ $this->assertEquals($client, $controller->store($request));
+ }
+
+ public function test_public_clients_can_be_stored()
+ {
+ $clients = m::mock(ClientRepository::class);
+
+ $request = Request::create(
+ '/',
+ 'GET',
+ ['name' => 'client name', 'redirect' => 'http://localhost', 'confidential' => false]
+ );
+ $request->setUserResolver(function () {
+ return new ClientControllerFakeUser;
+ });
+
+ $clients->shouldReceive('create')
+ ->once()
+ ->with(1, 'client name', 'http://localhost', false, false, false)
+ ->andReturn($client = new Client);
+
+ $redirectRule = m::mock(RedirectRule::class);
+
+ $validator = m::mock(Factory::class);
+ $validator->shouldReceive('make')->once()->with([
+ 'name' => 'client name',
+ 'redirect' => 'http://localhost',
+ 'confidential' => false,
+ ], [
+ 'name' => 'required|max:255',
+ 'redirect' => ['required', $redirectRule],
+ 'confidential' => 'boolean',
])->andReturn($validator);
$validator->shouldReceive('validate')->once();