I could connect to the communication app database directly from the intranet application over the internet - but I am not comfortable with that.
Why not?
Sharing storage between applications that belong to the same domain/organization is a common strategy to share data between different apps.
For example, when running multiple instances of the same app across several servers within the same infra-structure, you need a shared storage (database, redis, memcached, etc.) to synchronize queues or share session between them.
If you want to have an endpoint to totally separate the apps, or are planning to have these apps deployed on different infra-structure that cannot be shared you can have an API endpoint on one app that the other can call.
To secure this endpoint you can add a token based authentication with a shared secret (token) between both apps.
The easiest way to implement it is to use a closure request guard:
https://laravel.com/docs/8.x/authentication#closure-request-guards
The code sample in the docs fetches a User from the users table. But you can return any object that implements the \Illuminate\Contracts\Auth\Authenticatable interface.
For example, you could have Dummy a class like this:
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Str;
class Client implements Authenticatable
{
private $client;
public function __construct($client)
{
$this->client = $client;
}
public function getAuthIdentifierName()
{
return 'client';
}
public function getAuthIdentifier()
{
return $this->client;
}
public function getAuthPassword()
{
return '';
}
public function getRememberToken()
{
// avoid using this with a session-based guard
return Str::random(40);
}
public function setRememberToken($value)
{
}
public function getRememberTokenName()
{
return '';
}
}
And register a closure request guard like this:
Auth::viaRequest('intranet', function (Request $request) {
$token = $request->header('Authorization');
if (! \hash_equals('my-shared-secret', Str::after($token, 'Bearer '))) {
return null;
}
return new Client('intranet');
});
Then in a route in your ./routes/api.php you would have:
Route::post('/sync-intranet', [SyncController::class, 'sync'])->middleware('auth:intranet');
And from the client application's scheduled job you can use Laravel's HTTP Client to call this endpoint:
Http::asJson()
->withToken('my-shared-secret')
->post('https://example.com/sync/intranet', [
'data' => [...],
]);
Refer to https://laravel.com/docs/8.x/http-client if you are not familiar with the HTTP Client.
Notes:
- Here the shared secret is hard-coded in the code to exemplify usage. Ideally you would have it in a
.envfile on both apps, and accessed from a config file. - Security in this approach can be improved by signing the payload using the shared secret in the sender application and comparing it on the receiving application to ensure the data was not compromised during the request. It is out of the scope of this question to handle this.
- Another security measure is to check the request origin and compare it on an allowed domain list to avoid requests from unexpected domains.
- If both applications can access the same database, I, personally, would prefer the client application to access it. Configure a separate connection on the
./config/database.phpfile and treat it as an external storage. - One upside on sharing storage vs using endpoints is that if someone knows the shared secret (token) and you don't secure your endpoint with the aforementioned measures, they can call this endpoint from an external machine and send compromised data. It is much more common to have a database behind a firewall and manage different credentials for different applications with adequate access permissions.
Hope this helps.