How to integrate Meveto with a Laravel application
The following document demonstrates how to integrate Meveto in a Laravel application. Although this guide uses a blank Laravel application, you can still follow this guide step by step to integrate Meveto with an existing Laravel application as well.
What you need to know
The Meveto integration process that we will see in this guide is based on the OAuth 2.0 protocol. If you have no idea what the OAuth protocol is, then we strongly recommend that you first familiarize yourself with OAuth before attempting to implement this guide. If you want, you can take a quick look at this guide and decide if you need more information or not. If you do, here is a very detailed information at the oauth.net page that's divided into multiple sections that you should be able to easily follow.
Additionally, we also recommend you to go through the OAuth Apps section of our general documentation that explains how Meveto works. You can start reading from the What Meveto does page. Once you are done, you can come back to this guide and you will be able to not only easily follow it step by step but also customize the features given in this guide as per your needs.
Register your application
The first thing to do is to register your application with Meveto. This will allow you to get the credentials your application needs to communicate with the Meveto APIs. You will need the following information while registering your application.
- Application type
- Application name
- Application description
- Application web address
- Application login URL
- Application redirect URL
- Application webhook URL
- Application account sharing
If you need to read about what the above information is and why it’s required, you can read more here.

For the webhook URL, you will need to create a tunnel and expose the webhook URL of your app to the internet. This is required because Meveto servers can not send payloads to your computer’s localhost and therefore you will not be able to test the webhook functionalities while in development.You can learn more about the Meveto webhook calls here at the webhooks section.
If you need to know how to create a tunnel to your localhost URL i.e. how to expose it to the internet, you can take a look at the following services. Use anyone you feel comfortable with and can learn easier.
If you prefer a locally installed service that’s lightweight and very easy to use.
https://github.com/localtunnel/localtunnel
ngrok is another great option and this service is very popular. However, ngrok has more features and therefore absolute beginners might find it a bit harder to learn. Otherwise, you should be fine.
https://ngrok.com
Another great tool is tunnelto.dev
https://tunnelto.dev

Keep in mind that you might need to update the webhook URL of your application at the Meveto dashboard every time you use one of the above services to expose your localhost URL. This is because the free versions of these services assign any random public URL and port each time and therefore it might be different than what you used the last time. Just be aware of that and make sure your webhook URL is correct at Meveto every time you are testing.
For other URLs mentioned above that are required for your application’s initial registration at Meveto, you can choose to use an exposed version of those as well, just like for the webhook URL. However, this is not required since Meveto’s OAuth integration depends on browser redirects.
At the end of the registration process, you will get your application’s ID and secret. We call this the Client ID and Client Secret. Collect this information as your application needs to use it for communication with Meveto. The Client Secret is shown on your screen at this stage only once. If you ever lose this value, it can never be recovered so be sure to save it before you close your Meveto dashboard.

We have so far collected the following information:
- Client ID
- Client Secret
Install and setup a fresh Laravel application
If you are integrating Meveto to an existing Laravel application, then simply skip this step as the process remains the same. Otherwise, setup a new Laravel application and have it up and running before continuing. If you need help with setting up a Laravel application, you can refer to Laravel’s official documentation or if you have an issue, you can look up the internet for help as Laravel has a very vibrant community and there are tons of content about Laravel that you can discover via search engines.
Add the Meveto PHP SDK to your Laravel application
Once your Laravel application is up and running, the next step is to add our PHP SDK (Since Laravel is a PHP framework) to your application. You can do this by executing the following Composer command.
composer require meveto/meveto-php-sdk
Configure Meveto PHP SDK for your application
Next, let’s configure the PHP SDK so that it can communicate with the Meveto servers on behalf of your application. Since the Meveto SDK can be used in any PHP project, therefore, it does not support the usual php artisan
commands used by Laravel. However, the configuration is still very convenient and easy. Just go ahead and create a new php file in the config folder of your application and let’s call it meveto.php
Add the following content to this file and save it.
//config/meveto.php
return [
'id' => env('MEVETO_CLIENT_ID'),
'secret' => env('MEVETO_CLIENT_SECRET'),
'scope' => env('MEVETO_CLIENT_SCOPE'),
'redirect_url' => env('MEVETO_CLIENT_REDIRECT_URL'),
];
Next, let’s edit the .env
file and set the values for the variables we used in meveto.php
MEVETO_CLIENT_ID=93c4efac-5bf1-47f4-88eb-11b1a606d24f
MEVETO_CLIENT_SECRET=5xyzYajfvipqw0EIKwBwhClTGNYKzuhPmxDQDulbeCcqK4R2xV6B5iKbczrDVWEU
MEVETO_CLIENT_SCOPE=default-client-access
MEVETO_CLIENT_REDIRECT_URL=http://localhost:8000/meveto/callback
You will need to use the Client ID and Client Secret you got after you registered your application with Meveto. Do not use the Client ID and Client Secret values you see in the above snippet as this is only to show you how this would look like. For the MEVETO_CLIENT_SCOPE
use the value default-client-access
exactly as you see it in the snippet as this value remains the same for all Meveto OAuth clients. The last value, which is the redirect URL of your application, also must be set to exactly what you registered with Meveto. The above URL is again just an example. This URL also needs to be of the exact same format as you provided at the time of registration. For example, if you provided http://example.com/meveto/callback at the time of registration, then www.example.com/meveto/callback won’t work as this has to be of the exact same format.
You will now need to let Laravel know about the configuration values you have just set up. You can do this with multiple php artisan commands. Execute the following command to clear your application’s cached configuration values and recache it which will take into account the Meveto configuration you just added. Also remember that you might need to execute this command every time you make a change to the Meveto’s configuration values in your application’s .env
php artisan config:cache
Set up database via migrations
When a user completes authentication at Meveto, Meveto returns a meveto_id
to your application at the end of the login process. Your application needs to keep track of this unique ID to identify local (your application) users with it. One easy way to do this is to add a column to the users table
of your database and set the initial value of this column to NULL
. We will then see how your application can populate these columns in the users tables against each user.
Your application also needs to keep track of a user’s last login and last logout time via Meveto. Meveto not only logs users into your application, it also requests logouts via the Webhook URL of your application for a user. We will learn more about the logout feature in a later section (The webhooks section) but for now we will see how we can use a simple database table to keep track of a user’s last logged in and last logged out time via Meveto.
The final piece of information that your application needs to keep track of, is the state variable. This is simply a random string of a certain length (depending on preference) that’s mandated by the OAuth protocol. Before initiating the login request and redirecting users to Meveto, your application needs to generate a completely random string that’s at least 128 characters long (that’s our preference) and store it (this is where the database comes in). However, you are free to use any solution for storing the state variable. We will be using a simple database table for this guide.
Since a migration for the users table is included by default with a fresh Laravel installation, we will add the other 2 tables by executing the following artisan commands. These commands will also create the corresponding models which may or may not be needed.
php artisan make:model MevetoUser -m
php artisan make:model MevetoState -m
Let’s edit the users migration and add the meveto_id
column to it. Notice that the meveto_id column is a string that is unique, nullable and by default set to null.
// database/migrations/2014_10_12_000000_create_users_table.php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('meveto_id')->unique()->nullable()->default(null);
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
}
Next, let’s create the MevetoUser
migration. Notice that the primary key (id) of this table is a foreign key that references the id on the users table.
// database/migrations/2019_12_14_063548_create_meveto_users_table.php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMevetoUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('meveto_users', function (Blueprint $table) {
$table->bigInteger('id')->unsigned()->unique();
$table->foreign('id')->references('id')->on('users')->onDelete('cascade');
$table->bigInteger('last_logged_in')->unsigned()->nullable()->default(null);
$table->bigInteger('last_logged_out')->unsigned()->nullable()->default(null);
});
}
}
Lastly, let’s create the MevetoState
migration. You can see that it only needs a primary key and a string column called state
which is where the OAuth state will be stored.
// database/migrations/2019_12_15_161044_create_meveto_states_table.php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMevetoStatesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('meveto_states', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('state');
$table->timestamps();
});
}
}
After this is all set, run the migrations by the following artisan command.
php artisan migrate
If the users table of your application had already been migrated, then you can manually make the required update (add the meveto_id column) to your table. Or if your application is only a test app and has no important data in the database yet, then you can run the following artisan command to re-migrate after cleaning the database entirely. This will take all the migration updates into account.
php artisan migrate:fresh
Set up required routes
Head over to the routes/web.php
file and add the required routes to your application. If your application is a REST API server, in that case, other routes that your applications might have will most likely go in routes/api.php
file. In this case, you can add the routes that are required to process the Meveto integration to the api.php file but because the OAuth protocol relies on the standard browser based HTTP communication, therefore, these routes, if placed in the api.php will not be accessible when the Meveto server redirects to your application. The easiest workaround for this is to create these routes at your frontend application as well which most likely will be an SPA (a Single Page Application). You will then need to delegate the requests received by your frontend app to your backend’s REST APIs i.e the Meveto routes at your application’s api.php file.
However, Let’s first take on how the routes would work with the rather more traditional web.php
file. Once we learn how Meveto works with these routes, we will come back to the REST API concept discussed in the previous paragraph and it will make more sense. The routes that we are going to need are:
// routes/web.php
Route::get('meveto/login', ['', ''])->name('meveto.login');
Route::get('meveto/redirect', ['', ''])->name('meveto.redirect');
Route::get('login-to-connect', ['', ''])->name('login.old');
Route::post('meveto/connect', ['', ''])->name('meveto.connect');
Route::get('use-meveto', ['', ''])->name('meveto.use');
// Application's webhook that will be called by Meveto
Route::post('meveto/webhook', ['', '']);
Notice that the route URLs must match those that you have registered with Meveto for meveto/login
, meveto/redirect
and meveto/webhook
otherwise the integration won’t work. There are 3 other routes that we haven’t discussed before but we will explain what those routes do in a short bit and it will all make sense. Additionally, we have also named most of the routes except for the webhook route, to make it easy to reference them in our application’s code. Next, let’s add a new controller that will handle the requests made to these routes, will process it and return a response. Let’s name this controller MevetoController
. Create the controller by using the following php artisan command:
php artisan make:controller MevetoController
Add the following methods to the controller that will handle the requests.
The login
method will handle requests when the meveto/login
route is called.
// MevetoController.php
/**
* Login to Meveto
*/
public function login(Request $request)
{
}
The handleRedirect
method will handle requests made to route meveto/redirect
. This route will be invoked by Meveto when it redirects a user back to your application after completing the authentication process.
// MevetoController.php
/**
* Handle Redirect when Meveto returns the authorization code.
*/
public function handleRedirect(Request $request)
{
}
The oldLoginPage
method which will handle requests made to the login-to-connect
route. This route is supposed to be triggered when your application receives a Meveto ID that it does not recognize and it needs to now allow the user to either create a new account (at your application) or connect an existing account by authenticating it the old way (e.g. a username and password). This is to allow existing users of your app to be able to start using Meveto with your app. We will see more about this later in this guide.
// MevetoController.php
/**
* Show the connect to Meveto page
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function oldLoginPage(Request $request)
{
}
The connectToMeveto
method that will handle requests made to meveto/connect
route. This route is supposed to be triggered by a form that’s submitted from the page displayed by the oldLoginPage
method. This method will try to authenticate new users using your application’s old authentication process and connect the user’s Meveto ID with their existing accounts. More on this later when we add code to these methods.
// MevetoController.php
/**
* Validate a User's credentials before attaching a Meveto ID to the user
*/
public function connectToMeveto(Request $request)
{
}
The useMeveto
method that will respond to requests on the use-meveto
route. This page will be displayed to users when they attempt to login to their accounts using the old authentication method e.g. username and password. The concept here is that a user must never be allowed to use passwords once they start using Meveto. If your application still allows passwords, then Meveto can’t protect those accounts because they are still accessible by passwords.
// MevetoController.php
/**
* Display the warning to use Meveto for logging in to Meveto protected accounts
*/
public function useMevetoPage(Request $request)
{
}
Finally, the handleWebhookCall
which will handle webhook calls made by Meveto to your application at the meveto/webhook
route.
// MevetoController.php
/**
* This method will handle a webhook call to your application by Meveto
*
* @param Request
* @return Response
*/
public function handleWebhookCall(Request $request)
{
}
Now let's assign these methods to their corresponding routes before adding anything to them. Our web.php file will look something like this.
// routes/web.php
Route::get('meveto/login', ['MevetoController', 'login'])->name('meveto.login');
Route::get('meveto/redirect', ['MevetoController', 'handleRedirect'])->name('meveto.redirect');
Route::get('login-to-connect', ['MevetoController', 'oldLoginPage'])->name('login.old');
Route::post('meveto/connect', ['MevetoController', 'connectToMeveto'])->name('meveto.connect');
Route::get('use-meveto', ['MevetoController', 'useMevetoPage'])->name('meveto.use');
// Application's webhook that will be called by Meveto
Route::post('meveto/webhook', ['MevetoController', 'handleWebhookCall']);
Set up MevetoController
Let’s head over to the MevetoController and set it up so that whenever this controller’s class is instantiated by Laravel, it will also create an object of the Meveto SDK we added earlier to our application. We can then use the Meveto SDK object in other methods of the controller as we will see later. We will use the configuration values from the config/meveto.php
file to instantiate the Meveto object whenever the MevetoController is instantiated.
use Meveto\Client\MevetoService;
...
class MevetoController extends Controller
{
/**
* The Meveto Object
* @var MevetoService
*/
protected $meveto;
/**
* Instantiate the Meveto object
*/
public function __construct()
{
$this->Meveto();
}
...
...
/**
* @return void
*/
protected function Meveto()
{
$this->meveto = new MevetoService([
'id' => config('meveto.id'),
'secret' => config('meveto.secret'),
'scope' => config('meveto.scope'),
'redirect_url' => config('meveto.redirect_url'),
]);
}
}
Fill the MevetoController methods
It’s about time to write some code inside the methods we defined earlier. We will go over each method one by one. Each method’s code is given first followed by a brief but adequate explanation.
// [email protected]
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Redirect;
use App\MevetoState;
...
...
/**
* Login to Meveto
*/
public function login(Request $request)
{
// Create and store a state
$state = Str::random(128);
MevetoState::create([
'state' => $state
]);
// set the state on the Meveto SDK's object
$this->meveto->setState($state);
/**
* Get a URL from the the Meveto SDK's object. Redirect user to this URL.
* Check for client_token and sharing_token. The get() method on the $request
* will return NULL if these values or not set which is accepted by
* $this->meveto->login()
*/
$loginUrl = $this->meveto->login($request->get('client_token'), $request->get('sharing_token'));
// Redirect the user to the Meveto authentication portal
return Redirect::away($loginUrl);
}
As you can see, the login method initiates a user’s authentication. We have first generated a 128 characters long string as state (at least 128 characters long state is required by Meveto), we then store this state in our database (the MevetoState model). This is because we will need to retrieve this value later. We then set the state on the Meveto object.
Because this method handles requests to meveto/login
URL of your application, this URL can be directly invoked from outside of your application, i.e. the Meveto dashboard as well. Users can login to your application by a single click on their Meveto dashboards. Meveto makes this possible with the help of your application by requiring it to have a public login URL hence the meveto/login
route. Meveto sometimes passes additional information as query parameters to the login URL of your application. Therefore, this method must check for expected query parameters from Meveto and pass those on to the Meveto authentication portal.
Your application must check for a client_token
query parameter. Additionally, if your application allows account sharing between people, read more at getting started guide. You also need to check for a sharing_token
query parameter. You can pass these values or NULL
(which is also the default value on the Meveto SDK object’s method) to get a URL back for the Meveto authentication portal. Finally, you will need to redirect the user’s browser to this URL so that the user can authenticate with Meveto.
// [email protected]
use App\MevetoUser;
use App\User;
use Illuminate\Support\Facades\Auth;
...
...
/**
* Handle Redirect when Meveto returns the authorization code.
*/
public function handleRedirect(Request $request)
{
// Get the authorization code and state from the redirect URL
$code = $request->get('code');
$state = $request->get('state');
/**
* Retrieve a state, if a match is found, good,
* otherwise, the response most probably did not come form Meveto.
*/
$appMevetoState = MevetoState::where('state', $state)->first();
if ($appMevetoState !== null) {
// You must delete the record for this state before continuing
$appMevetoState->delete();
// Exchange auth code with access token
$response = $this->meveto->getAccessToken($code);
// Retrieve User's information
$data = $this->meveto->getResourceOwnerData($response['access_token']);
// Extract the Meveto ID from the user information
$userIdentifer = $data['user'];
// Try to find a user in your application's database by Meveto ID
$user = User::where('meveto_id', '=', $userIdentifer)->first();
if ($user !== null) {
try {
// Create or Update the MevetoUser record for this login
MevetoUser::updateOrCreate(
['id' => $user->id],
['last_logged_in' => Carbon::now()->timestamp]
);
// Log the user in to your app and redirect them to any destination you want
Auth::login($user);
return redirect()->route('home');
} catch(\Exception $e)
{
// Catch any exceptions
}
}
/**
* If a local user was not found by Meveto ID, redirect them to either
* - Register (as they don't have an account with you yet)
* - Map (specify an existing account by old authentication method and Map Meveto ID to that.)
*
* For simplicity, we are only doing the Mapping.
*/
return redirect()->route('login.old', ['meveto_id' => $userIdentifer]);
} else {
return response('States did not match. Invalid OAuth Response', 400);
}
}
After a user completes authentication at Meveto from the previous step, Meveto will redirect the user’s browser back to your application’s specified redirect URL. The handleRedirect
method is supposed to handle this response from Meveto. If everything goes smoothly, we are expecting 2 values in this response from Meveto, the authorization code
and the state
your application sent in the previous step.
The first thing we need to do is to make sure the state Meveto returned is exactly the same as we sent i.e. a record must exist in our database (this is why we stored it in the previous step). This ensures that the response came back from Meveto. Next, we need to use the authorization code to retrieve an authentication token. As the name suggests, the authorization code is a proof that the user who authenticated at Meveto, has authorized your application to get their Meveto ID (and/or other applicable information). We are then exchanging the authorization code using the getAccessToken
method provided by the Meveto SDK object. For the sake of this simple guide, we haven’t checked for the failure of the getAccessToken
method which you might want to do and check its response before continuing. We are then using the access token to get the user’s information by using the getResourceOwnerData
method of the Meveto SDK. Again, you need to check for any failure of this method before continuing but we are skipping that for simplicity. The getResourceOwnerData
method returns an array of which we are only interested in the user key ['user']
. This key contains the Meveto ID of the associated user that had authenticated at Meveto.
Next, we are looking up our application’s users table
to attempt and identify a local user based on the Meveto ID. This is why we set a meveto_id
column on the users table in the beginning of this guide. If we succeed in identifying the local user based on the Meveto ID, we then proceed to the final steps which are to update the user’s last logged in record and to programmatically log the user in using Laravel’s Auth Facade
.
It’s extremely important that we update the user’s last logged in time. This is why we also created a meveto_users table
earlier. We will need the last logged in and last logged out values of a user to calculate their authentication status in a later step. But what if the Meveto ID we received could not be matched to a local user record? This is why we set the meveto_id
column of the users table to be NULL
by default for all of our existing users in the database. Therefore, if this is the first Login with Meveto attempt of this particular user, we won’t be able to retrieve a record based on the Meveto ID we received. However, the user might already have an account with us and their meveto_id
is by default set to NULL
. Ideally, we will need to present the user a choice between registering a new account or mapping an existing account by authenticating the old way one last time. Again, for the sake of simplicity, we will only proceed to the mapping stage as the idea is similar for the registration of a new user.
We need to temporarily store the Meveto ID so that we can associate it with the user after either new registration or specification of an existing account. You can do the temporary storage of the Meveto ID in any way that suits your application i.e. Redis, Database or a Cookie but we are simply passing it on to the next page via a query parameter as you can see. It’s safe for the Meveto ID to be visible to this particular user (in this case in the URL) because we know this user owns the Meveto ID.
// [email protected]
/**
* Show the connect to Meveto page with old login
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function oldLoginPage(Request $request)
{
return view('auth.connect')->with('meveto_id', $request->get('meveto_id'));
}
The oldLoginPage
method simply returns a view that displays a traditional username/email and password based login page to the user. This is supposed to be the authentication page that your application used before Meveto. Additionally, we are also passing the meveto_id
value to this view and we are using a hidden form field
to store and submit the meveto_id
along with the email/username and password. You can take a look at this view’s blade file here. Upon successful authentication, we will need the meveto_id as we will see in the next method.
// [email protected]
...
...
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class MevetoController extends Controller
{
use AuthenticatesUsers;
/**
* Validate a User's credentials before attaching a Meveto ID to the user
*/
public function connectToMeveto(Request $request)
{
$this->validateLogin($request);
// If the login attempt was successful
if ($this->attemptLogin($request)) {
// Attach Meveto ID to the user now
try {
$user = User::where('email', $request->post('email'))->first();
$user->meveto_id = $request->post('meveto_id');
$user->save();
/**
* Create or Update the MevetoUser record for this login
*/
MevetoUser::updateOrCreate(
['id' => $user->id],
['last_logged_in' => Carbon::now()->timestamp]
);
return $this->sendLoginResponse($request);
} catch(\Exception $e) {
// Catch any exceptions
}
}
return $this->sendFailedLoginResponse($request);
}
}
Once a user submits an authentication request to your application (the old way), the connectToMeveto
method takes care of that. The method leverages the AuthenticatesUsers
trait which is provided by Laravel’s core to validate the user’s old credentials and to programmatically log the user in. All methods prefixed with $this->
comes from this trait and their names are pretty self explanatory. The important bits to keep in mind are to attach the Meveto ID by updating the users table meveto_id
column for the user (the meveto_id column for this user will then no longer be NULL) and to update or create the user’s last_logged_in
record via the MevetoUsers
model just like we did in the handleRedirect
method. Also notice that we are accessing the current user’s meveto_id via the post method on the request. This is why in our oldLoginPage
view we set it to a hidden field so that it can be submitted with the request automatically.
// [email protected]
/**
* Display the warning to use Meveto for logging in to
* Meveto protected accounts
*/
public function useMevetoPage(Request $request)
{
return view('usemeveto');
}
The useMevetoPage
method simply displays a view that tells the user to use Meveto to login to your application. This page should be shown to the user when they try to login to your application using the old authentication method e.g username and password. You can take a look at the view here. But, what invokes this method to display this warning? For this, you will need to amend the existing authentication logic/code of your application. Of course we don’t want to block all existing users from being able to login via the old authentication method. We only want to block it for those that have started using Meveto (have logged in before with Meveto). This is yet another reason why we initialized the meveto_id
column on the users table with NULL. We can easily add logic that checks if the user’s meveto_id column is not NULL, then they must have used Meveto before. Therefore, our application will block such login attempts and redirect them to the use Meveto page. Let’s take a look at another piece of code.
// LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers {
login as traitLogin;
}
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function login(Request $request)
{
$this->validateLogin($request);
// Check if the user is using Meveto with your application, then reject the old login request.
$user = User::where('email', $request->get('email'))->first();
if($user->meveto_id !== null)
{
return redirect()->route('meveto.use');
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
return $this->sendFailedLoginResponse($request);
}
}
Suppose the login method of the LoginController.php
is your application’s old authentication handler. As you can see, for each old authentication attempt, before we proceed, we simply need to insert a check. It simply checks if the user’s meveto_id
column is not NULL, then blocks the request by returning the meveto.use
route which then executes the useMevetoPage
method of the MevetoController.
// [email protected]
/**
* This method handles webhook calls from Meveto
*
* @param Request
* @return Response
*/
public function handleWebhookCall(Request $request)
{
//First grab payload from the request
$payload = $request->all();
switch($payload['type'])
{
/**
* This `type` means that the User has requested a logout
* from your application using their Meveto dashboard.
*/
case 'User_Logged_Out':
// Identify the user that logged out. Exchange the 'user_token' with Meveto for user ID
$userToken = $payload['user_token'];
try {
// Retrieve the user from Meveto
$user = $this->meveto->getTokenUser($userToken);
// Next, Find the user by Meveto ID
$user = User::where('meveto_id', $user)->first();
if($user !== null) {
try {
// Update the user's last_logged_out time
MevetoUser::where('id', $user->id)->update(['last_logged_out' => Carbon::now()->timestamp]);
/**
* Finally, invoke an event that would let your application's user interface to refresh automatically. This will make
* the logout process seamless for your users. Usually, for this purpose, your application would use something like
* Pusher or Socket.io
*/
event(new LoggedOut($user->id, $user->name));
// You need to return a 200 OK response to Meveto
return response()->json('');
} catch(\Exception $e) {
// Catch any exceptions
}
} else {
// You can ignore the webhook call for logout if the Meveto ID could not be matched to a user at your application.
}
} catch(\Exception $e) {
// Catch any exceptions thrown by Meveto SDK for getTokenUser()
}
break;
case 'Meveto_Protection_Removed':
// Identify the user that removed Meveto protection. Exchange the 'user_token' with Meveto for user ID
$userToken = $payload['user_token'];
try {
// Retrieve the user from Meveto
$user = $this->meveto->getTokenUser($userToken);
// Next, Find the user by Meveto ID
$user = User::where('meveto_id', $user)->first();
if($user !== null)
{
try {
// Set Meveto ID on the user to NULL
$user->meveto_id = null;
$user->save();
// Next, also remove the corresponding MevetoUser Record
MevetoUser::where('id', $user->id)->delete();
// You need to return a 200 OK response to Meveto
return response()->json('');
} catch(\Exception $e) {
// Catch any exceptions
}
} else {
// You can ignore the webhook call for logout if the Meveto ID could not be matched to a user at your application.
}
} catch(\Exception $e) {
// Catch any exceptions thrown by Meveto SDK
}
break;
}
}
As discussed in the beginning, Meveto sends webhook calls to your application to request certain actions on behalf of the users. You can read more about this at the webhooks section of our general OAuth documentation here. The handleWebhookCall
method handles these requests from Meveto to the webhook URL of your application. We are expecting 2 types of webhooks in this implementation. The User_Logged_Out
and the Meveto_Protection_Removed
. Both of these webhook calls will contain a user_token from Meveto which can be used to retrieve the Meveto ID (meveto_id) of the user associated with this action. Next, we are using the Meveto SDK’s getTokenUser
method to exchange the user token directly with the user’s Meveto ID.
For the User_Logged_Out
event, if you notice, we are not executing an actual user logout logic. Even if we did, it won’t work because this part of the code, when it’s being executed upon a call from Meveto, will have no direct session with the user’s browser (where login cookies might live) and therefore we can not invalidate those cookies. Instead, it’s very important to update the users last_logged_out
time at the meveto_users table
. We will see in the next step how we can use this timing information to decide whether a user stays logged in or must be logged out on the very next request the user’s browser makes. We are going to achieve that via middleware.
However, if your application is a REST APIs based backend and uses tokens for user authentications e.g JWT tokens , then you can easily invalidate the user’s current valid token which will essentially log the user out and their browser or mobile app won’t be able to use that token anymore. But we still recommend keeping a track of the user’s last_logged_in
and last_logged_out
times as this information may come in handy in other scenarios.
The next step is optional for the User_Logged_Out
event but recommended. It’s to dispatch an event that can trigger the user’s browser or your mobile application’s interface, to automatically refresh and display the logged out state. This enhances the user experience. This auto refresh functionality can be achieved with websockets for browsers and something as simple as push notifications for mobile apps. We are using Pusher’s (a websockets service provider) free tier with Laravel’s easy event dispatching. If you need to learn how to configure Pusher with Laravel, you can easily find plenty of material online. Just keep in mind that this is not an endorsement of Pusher. Any other service you like, or even your own websockets implementation is good enough.
For the Meveto_Protection_Removed
event, the user identification process is identical. After the user has been identified, we are setting the user’s meveto_id
back to NULL in the users table. If you remember from earlier, our application blocks login attempts by non Meveto methods if a user’s meveto_id is set. Therefore, setting it back to NULL will allow users to login using other methods. Additionally, we also need to remove the user’s record from the meveto_users table
. We have kept things as simple as possible. You can see that much of the code can be easily refactored. You can adjust everything as per your preferences and application specific needs.
Setup the logout middleware
So far, we have seen the last_logged_in
and last_logged_out
time for a user. In the User_Logged_Out
event, we did not execute any actual user logout logic but we set the user’s last_logged_out
time. In this middleware, we will see how we can calculate a user’s login status. We can then apply this middleware to all protected routes of our application to make sure the user’s login status is kept in check. Execute the following php artisan command to create the middleware. Name it anything you like.
php artisan make:middleware CheckLoginStatus
// App\Http\Middleware\CheckLoginStatus.php
namespace App\Http\Middleware;
use App\MevetoUser;
use Closure;
use Illuminate\Support\Facades\Auth;
class CheckLoginStatus
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// First, check if an authenticated user exists
if(Auth::check()) {
$user = Auth::user();
// Next let's check if the authenticated user has started using Meveto with our application
$mevetoUser = MevetoUser::where('id', '=', $user->id)->first();
if($mevetoUser !== null) {
// Check if the user should stay logged in
if($mevetoUser->last_logged_out != null && ($mevetoUser->last_logged_out > $mevetoUser->last_logged_in)) {
// Log the user out. This means that the user must have logged out using their Meveto dashboard
return redirect()->route('logout');
}
}
}
return $next($request);
}
}
In the middleware, we first check for a logged in user. If one exists, then next we need to determine if the user has logged in with Meveto before. If a MevetoUser
record exists for the user, then that means the user has logged in with Meveto before. Remember that we won’t have the MevetoUser
record if either the user has never used Meveto or if your application has processed the Meveto_Protection_Removed
webhook event for the user.
If we determine that the user has started using Meveto, then we need to make sure the user was logged in to the application using Meveto. We are comparing last_logged_in
with last_logged_out
such that if last_logged_out
is later than last_logged_in
, that means the user must have logged out of the application using their Meveto dashboard. In other words, our application has processed the User_Logged_Out
event where we set the last logged out time. We also need to keep in mind that last_logged_out
might be NULL
since it can have NULL value if the user has never logged out using their Meveto dashboard so far.
Important considerations
As discussed while we were setting up the required routes, if your application is a REST APIs backend, then you will need to set up the frontend of your application to handle the routes we did in routes/web.php
and then forward the request further to your application’s REST APIs. This is not necessary for the webhook route as webhook calls work without the interaction of a browser agent. This means that you can put the webhooks route in web.php
or api.php
wherever you like.
Your application’s frontend will need to be able to receive and process the responses from Meveto properly. The most important route is the redirect URL which will also live at the frontend of your application before it’s delegated to your REST APIs. On this URL, the data is sent back to your app via the query parameters inside the URL itself. Once a request reaches your application’s backend via the frontend, the rest of the process remains pretty much exactly the same or otherwise as considered suitable.
One particularly tricky use case is when you need to redirect your user to Meveto for authentication. Since your backend is disconnected from the browser and can only send json payloads to your frontend, you will not be able to redirect the user to the Meveto authentication portal. The most convenient workaround for this is to simply generate the URL at the backend as we did and send it to your frontend. Your frontend can then easily pick the URL and redirect the user’s browser to it.
Another important consideration is the use of the default browser on a mobile device if your Laravel based REST APIs are powering a mobile application. Because the OAuth protocol literally depends on the presence of a user agent aka a web browser, you will need to figure out how to effectively transfer data between the default web browser and your app on a mobile device. You can easily achieve this feature by making use of a concept called Deep Linking. It’s only a bit more work.
The basic concept is that your app on a mobile device constantly listens to the default browser. Whenever a particular link is processed by the browser, your app can take over. Similarly your app can force the default browser to open any URL it wants. Therefore, your app can receive the URL for the Meveto authentication portal from your backend and then have the default browser on the device process it. Once the authentication is complete, Meveto will invoke the redirect URL of your app (which is what it will be listening to) on the same browser and your app can grab the data from the URL and send it to your REST backend for further processing.