As you may have already noticed I’m working for the biggest job platform in Austria, karriere.at. Naturally our main feature is to provide a fast and reliable job search on our platform and one user feature on the job search is to allow them to add a certain job to their so called “watchlist”.

This feature is available for guest and of course for logged in users. The key difference for guest users is, that when they click on “watch job” the get a tooltip that tells them, that they need to be logged in to be able to watch the job. After clicking on the login link and entering their credentials they get redirected back to the search page and the “watch” action was already triggered (as intended).

In this article I will demonstrate how you can implement such actions, that should only be executed when the user is authenticated.

AuthorizedExecutor

To be able to check and execute our action we need to introduce a new class called AuthorizedExecutor. This class needs to be able to serialize closures, therefor the following dependency needs to be installed.

composer require jeremeamia/superclosure

No we can define the class that has two methods:

  • handle: called with the closure that should be executed. The method will either call the closure directly (for logged in users) or will store the closure in the session to defer the execution.
  • handleDeferred: to be called later on, when deferred closures should be executed.
class AuthorizedExecutor
{
    const SESSION_KEY = 'auth-exec';

    public function handle($closure)
    {
        if (!Auth::check()) {
            session()->put(static::SESSION_KEY, new SerializableClosure($closure));
            return;
        }

        $closure();
    }

    public function handleDeferred()
    {
        $deferredTask = session(static::SESSION_KEY);

        if (!is_null($deferredTask)) {
            $deferredTask();
        }

        session()->forget(static::SESSION_KEY);
    }
}

To make the usage of the functionality as easy as possible we will add a helper method authorized_execute that uses the defined class.

if (!function_exists('authorized_execute')) {
    function authorized_execute($closure)
    {
        app(\App\AuthorizedExecutor::class)->handle($closure);
    }
}

Executing deferred actions

We need some point in the authentication flow to be able to execute a deferred action. Laravel provides a Login event, which is triggered after a successful login. We will install a listener for this event and call our handleDeferred method.

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'Illuminate\Auth\Events\Login' => [
            'App\AuthorizedExecutor@handleDeferred',
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

We can use the handleDeferred method directly as our listener method, because we will not use any input parameters.

Example

Let’s look at a simplified implementation of the “watch job” functionality by asuming that the application uses an endpoint /job/{id}/watch. The endpoint implementation will use our authorized_execute helper to save the given job to the users watchlist.

function watch(Job $job)
{
    authorized_execute(function () use ($job) {
        Auth::user()->watchJob($job);
    });
}

Our helper method will ensure, that the given closure is only executed when the user is logged in and if it needs to be deferred, all used variables (the $job object and the authenticated user) will be saved or evaluated at execution time.

Wrap up

In combination with a correct login redirect this functionality will be very useful for our clients because, they do not need to trigger the intended action again after a successful login.

By simply adding this class definition to your code base you empower your application with the desired functionality.

Resources