I am working on my project and I have an issue with the API. The registering and login system work very well. I have a details route where I am able to retrieve informations about the current user. It means that the application is able to see that an user is authenticated and act in the right way.
I started working on the email verification (and honestly, it was pretty hard to make everything work properly). The email sends properly, but I have an issue with the link. When I click on it in the email, I get a error saying the the route doesn't exist (which is normal since I am on the api). However, when I add the header "Accept" for application/json and the Authorization header, I receive the message "Unauthenticated" even if the user is authenticated properly.
Routes :
Auth::routes(['verify' => true]);
Route::post('login', 'UserController@login');
Route::post('register', 'UserController@register');
Route::group(['middleware' => 'auth:api'], function() {
Route::post('details', 'UserController@details');
});
User model :
<?php
namespace App;
use App\Notifications\VerifyEmail;
use Laravel\Passport\HasApiTokens;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable implements MustVerifyEmail
{
use Notifiable, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'firstname', 'lastname', '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',
];
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
Register controller :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Support\Facades\Auth;
use Validator;
class UserController extends Controller
{
public $successStatus = 200;
public function login() {
if(Auth::attempt(['email' => request('email'), 'password' => request('password')])) {
$user = Auth::user();
$success['token'] = $user->createToken('MyApp')->accessToken;
return response()->json(['success' => $success], $this->successStatus);
}
else {
return response()->json(['error' => 'Unauthorised'], 401);
}
}
public function register(Request $request) {
$validator = Validator::make($request->all(), [
'firstname' => 'required',
'lastname' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required',
'c_password' => 'required|same:password'
]);
if($validator->fails()) {
return response()->json(['error' => $validator->errors()], 401);
}
$input = $request->all();
$input['password'] = bcrypt($input['password']);
$user = User::create($input);
$success['token'] = $user->createToken('MyApp')-> accessToken;
$success['name'] = $user->name;
$user->sendEmailVerificationNotification();
return response()->json(['success' => $success], $this->successStatus);
}
public function details()
{
$user = Auth::user();
return response()->json(['success' => $user], $this->successStatus);
}
}
Verification controller :
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}
Email Verification Notification :
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class VerifyEmail extends \Illuminate\Auth\Notifications\VerifyEmail
{
/**
* 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(('Vérification de l\'addresse email'))
->line(('Veuillez cliquer sur le bouton ci-dessous pour vérifier votre addresse email.'))
->action(('Vérifier mon addresse email'), $verificationUrl)
->line(('Si vous n\'avez pas créé de compte, vous n\'avez rien à faire de plus.'));
}
}
You can see at the constructor of the VerificationController
, there is $this->middleware('auth');
. You should changed it to auth:api
.
But, verification is something that browser loads. It shoudn't use api
guard, it should be web
(the default behaviour). You need to login first using normal login method (not via API), since that's not how API works.
The correct use case, just placed Auth::routes(['verify' => true]);
in routes/web.php
.