Lumen 6, verify user email address.

Nahuel Bulian
6 min readJan 20, 2020

This is a simple guide to verify user email address in Lumen 6 with the Laravel style ;)

This tutorial assumes that you already created a new project, with JSON Web Token Authentication, Notification enabeled and your Email credentials set up. Find more about jwt-auth in the official repo or here http://jwt-auth.com.

1. Add email_verified_at field to your users table.

Inside your users table migration you should add the following line $table->timestamp('email_verified_at'); when you’re done, your migration should look something like this:

Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamp('email_verified_at')->nullable();
$table->timestamps();
});

You can see a full version of the users migration in the official laravel repository.

Then re-run your migrations and if all went well, that should have created a new version of the users table with email_verified_at field.

php artisan migrate:fresh

2. Create a notification for the users that request an email verification

You can find the original file version here, you’ll notice that the main change is in function verificationUrl where I use JWTAuth to generate the token.

Create a VerifyEmail.php file with the next content into your app\Notifications folder.

<?phpnamespace App\Notifications;use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;
use Tymon\JWTAuth\Facades\JWTAuth;
class VerifyEmail extends Notification
{
use Queueable;
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
{
$token = JWTAuth::fromUser($notifiable);
return route('email.verify', ['token' => $token], false);
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}

3. Create a trait to add some functionality to the User model

Create a MustVerifyEmail.php ile with the next content into your app\Trais folder. You can find the original file version here.

<?phpnamespace App\Traits;use App\Notifications\VerifyEmail;trait MustVerifyEmail
{
/**
* Determine if the user has verified their email address.
*
* @return bool
*/
public function hasVerifiedEmail()
{
return ! is_null($this->email_verified_at);
}
/**
* Mark the given user's email as verified.
*
* @return bool
*/
public function markEmailAsVerified()
{
return $this->forceFill([
'email_verified_at' => $this->freshTimestamp(),
])->save();
}
/**
* Send the email verification notification.
*
* @return void
*/
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
/**
* Get the email address that should be used for verification.
*
* @return string
*/
public function getEmailForVerification()
{
return $this->email;
}
}

4. Create a middleware to ensure that the user email is verified

Create a EnsureEmailIsVerified.php ile with the next content into your app\Http\Middleware folder. You can find the original file version here.

Some changes were needed in the response, instead a redireccion I throwed an authorization exception.

<?phpnamespace App\Http\Middleware;use Closure;
use Illuminate\Auth\Access\AuthorizationException;
class EnsureEmailIsVerified
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null
* @return mixed
*/
public function handle($request, Closure $next)
{
if ( $request->fullUrl() != route('email.request.verification') &&
( ! $request->user() || ! $request->user()->hasVerifiedEmail() ) )
{
throw new AuthorizationException('Unauthorized, your email address '.$request->user()->email.' is not verified.');
}
return $next($request);
}
}

5. Register the new middleware

Add the new middleware into your Lumen bootstrap/app.php file.

$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
'verified' => App\Http\Middleware\EnsureEmailIsVerified::class,
]);

6. Add the trait to your user model for more functionalities

Pay attention to the “saved” event, any time that the email changed we set the field email_verified_at to null and send the the user an email to validate it. For more information about events please take a look to the For more information about events please take a look to the Laravel documentation topic.

<?phpnamespace App;use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
use App\Traits\MustVerifyEmail;
class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
use Authenticatable, Authorizable, Notifiable, MustVerifyEmail;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
protected static function boot()
{
parent::boot();

static::saved(function ($model) {
/**
* If user email have changed email verification is required
*/
if( $model->isDirty('email') ) {
$model->setAttribute('email_verified_at', null);
$model->sendEmailVerificationNotification();
}
});}}

7. Add the methods to the Controller

Inside the folder app\Http\Controllers I have a controller called AuthController.php and there is were I created the next two functions.

/**
* Request an email verification email to be sent.
*
* @param Request $request
* @return Response
*/
public function emailRequestVerification(Request $request)
{
if ( $request->user()->hasVerifiedEmail() ) {
return response()->json('Email address is already verified.');
}

$request->user()->sendEmailVerificationNotification();

return response()->json('Email request verification sent to '. Auth::user()->email);
}
/**
* Verify an email using email and token from email.
*
* @param Request $request
* @return Response
*/
public function emailVerify(Request $request)
{
$this->validate($request, [
'token' => 'required|string',
]);
\Tymon\JWTAuth\Facades\JWTAuth::getToken();
\Tymon\JWTAuth\Facades\JWTAuth::parseToken()->authenticate();
if ( ! $request->user() ) {
return response()->json('Invalid token', 401);
}

if ( $request->user()->hasVerifiedEmail() ) {
return response()->json('Email address '.$request->user()->getEmailForVerification().' is already verified.');
}
$request->user()->markEmailAsVerified();return response()->json('Email address '. $request->user()->email.' successfully verified.');
}

8. Add the new routes

You have to add two new routes to your web.php

$router->group(['middleware' => ['auth', 'verified']], function () use ($router) {
$router->post('/logout', 'AuthController@logout');
$router->get('/user', 'AuthController@user');
$router->post('/email/request-verification', ['as' => 'email.request.verification', 'uses' => 'AuthController@emailRequestVerification']);
$router->post('/refresh', 'AuthController@refresh');
$router->post('/deactivate', 'AuthController@deactivate');
});
$router->post('/register', 'AuthController@register');
$router->post('/login', 'AuthController@login');
$router->post('/reactivate', 'AuthController@reactivate');
$router->post('/password/reset-request', 'RequestPasswordController@sendResetLinkEmail');
$router->post('/password/reset', [ 'as' => 'password.reset', 'uses' => 'ResetPasswordController@reset' ]);
$router->post('/email/verify', ['as' => 'email.verify', 'uses' => 'AuthController@emailVerify']);

Notice that email.request.verification is under the auth middleware.

Let’s prove it

  1. First login to your API

2. Try to access some protected endpoint and you’ll receive a 403.

3. Now request an email verification

4. If everything goes well you’ll see an email like this:

5. Now copy the token and send it to the endpoint to verify your email

That is all!

This tutorial doesn’t cover everything. For instance, you still need to setup your mail.php file with your mail drivers configuration. But hopefully, at the very least, this gets you creating some errors that you can actually understand.

For more information visit the official Lumen documentation.

Enjoy verifying emails from your Lumen application!

--

--

Nahuel Bulian

#Entrepreneur, tech developer & #crypto enthusiast #bitcoin #ethereum