2

I'm trying to generate a token to authenticate users in my Controller the following way:

namespace App\Http\Controllers\API;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\User;

class AuthController extends Controller
{
    public function login()
    {
        if (Auth::attempt(['email' => request('email'), 'password' => request('password')])) {
            $user = Auth::user();


            $success['token'] = $user->createToken('myApp')->accessToken;
            dd($success['token']);

        }
    }

Currently, I'm just trying to print out the token. And this is my User's model:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
//use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    const USER_FIRST_NAME_FIELD        = "first_name";
    const USER_LAST_NAME_FIELD         = "last_name";
    const USER_PREFERRED_NAME_FIELD    = "preferred_name";
    const USER_EMAIL_FIELD             = "email";
    const USER_EMAIL_VERIFIED_AT_FIELD = "email_verified_at";
    const USER_PASSWORD_FIELD          = "password";
    const USER_REMEMBER_TOKEN_FIELD    = "remember_token";
    const USER_RECEIVE_NEWSLETTER_FIELD= "receive_newsletter";
    const USER_ACTIVE_FIELD            = "active";

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        self::USER_FIRST_NAME_FIELD, 
        self::USER_LAST_NAME_FIELD,
        self::USER_PREFERRED_NAME_FIELD,
        self::USER_EMAIL_FIELD,
        self::USER_PASSWORD_FIELD,
        self::USER_RECEIVE_NEWSLETTER_FIELD,
        self::USER_ACTIVE_FIELD,
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        self::USER_PASSWORD_FIELD,
        self::USER_REMEMBER_TOKEN_FIELD
    ];

    /**
     * Automatically creates password hash when password is submitted
     *
     * @param string $password
     * @return void
     */
    public function setPasswordAttribute(string $password) : void
    {
        $this->attributes['password'] = Hash::make($password);    
    }
}

As you can see I'm using HasApiTokens, Notifiable traits and nonetheless I'm getting an error from my controller saying:

Call to undefined method App\User::createToken()

Passport is installed and configured correctly.

Here's something weird:

When registering an user (I'm using a separate controller and also using a service) a token is created successfully:

Here's my controller:

<?php

namespace App\Http\Controllers\API;

use App\Services\UserService;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\RegisterUserRequest;


class UserController extends Controller 
{
    private $user;

    public function __construct(UserService $user)
    {
        $this->user = $user;
    }

    public function store(RegisterUserRequest $request) : JsonResponse
    {
        // TODO: verify message on error

        $user = $this->user->register($request->validated());
        $token = $user->createToken('MyApp')->accessToken;
        dd($token);

        return response()->json(['status' => 201, 'user_id' => $user->id]);
    }

}

Here's my service:

<?php

namespace App\Services;

use App\Models\User;
use App\Services\BaseServiceInterface;

class UserService implements BaseServiceInterface
{

    public function register(array $formValues) : User
    {
        // 'terms and conditions' should not be saved into the db, hence it's removed
        unset($formValues['terms_conditions']);
        return User::create($formValues);
    }

}

and here's my model again:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
//use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    const USER_FIRST_NAME_FIELD        = "first_name";
    const USER_LAST_NAME_FIELD         = "last_name";
    const USER_PREFERRED_NAME_FIELD    = "preferred_name";
    const USER_EMAIL_FIELD             = "email";
    const USER_EMAIL_VERIFIED_AT_FIELD = "email_verified_at";
    const USER_PASSWORD_FIELD          = "password";
    const USER_REMEMBER_TOKEN_FIELD    = "remember_token";
    const USER_RECEIVE_NEWSLETTER_FIELD= "receive_newsletter";
    const USER_ACTIVE_FIELD            = "active";

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        self::USER_FIRST_NAME_FIELD, 
        self::USER_LAST_NAME_FIELD,
        self::USER_PREFERRED_NAME_FIELD,
        self::USER_EMAIL_FIELD,
        self::USER_PASSWORD_FIELD,
        self::USER_RECEIVE_NEWSLETTER_FIELD,
        self::USER_ACTIVE_FIELD,
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        self::USER_PASSWORD_FIELD,
        self::USER_REMEMBER_TOKEN_FIELD
    ];

As I told you, when creating a user the token is being generated correctly.

I'd say that Auth::user() is not calling my Model directly, but I don't know for sure that's what is happening.

Any idea why? Thanks

MrCujo
  • 1,218
  • 3
  • 31
  • 56
  • does your userservice/model have a function called createToken? if it doesn't then you need to create/define it there – Udhayan Nair Oct 18 '19 at 02:55
  • no they do not have any method called `createToken` and I shouldn't have to create it since my model uses extends `Authenticable` and uses a trait called `HasApiTokens`. That method is there in those classes and as you can see my register works fine – MrCujo Oct 18 '19 at 03:00
  • You need to retrieve the user instance in order to use the createToken() function – Jesus Erwin Suarez Oct 18 '19 at 03:04
  • @JesusErwinSuarez isn't that what `$user = Auth::user();` is doing? I mean, if I print that out I can see the object. The issue is that that instance doesn't seem to have `createToken()` associated to it, even though my `User` model has `HasApiTokens` :( – MrCujo Oct 18 '19 at 03:10

5 Answers5

7

Since your guard is returning the wrong User model, App\User, you should check your auth configuration, 'config/auth.php'. In the providers array adjust any provider, usually users, that is using the App\User model to App\Models\User instead.

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        // 'model' => App\User::class,
        'model' => App\Models\User::class,
    ],
    ...
],
lagbox
  • 48,571
  • 8
  • 72
  • 83
2

in my case, i missed to use Trait HasApiTokens thats why laravel was unable to create tokens.

just open User.php

afetr name space include

use Laravel\Passport\HasApiTokens;

then inside class

use HasApiTokens

Pls note : I am using laravel 7.

1

So, this is not the right way to do it but it's working at the moment:

<?php

namespace App\Http\Controllers\API;

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\API\BaseController;

class AuthController extends BaseController
{
    public function login()
    {
        if (Auth::attempt(['email' => request('email'), 'password' => request('password')])) {
            $authenticated_user = \Auth::user();
            $user = User::find($authenticated_user->id);
            dd($user->createToken('myApp')->accessToken);
        }

        dd('here');
    }
}

Now I'm seeing the token.

I wanna do it the right way so I still would appreciate if any one could help me. Thanks

MrCujo
  • 1,218
  • 3
  • 31
  • 56
1

you can let the auth.basic middleware do the authentication for you, by calling it in the construct method:

public function __construct()
    {
        $this->middleware('auth.basic'); 
    }

Then generate the access token for the currently authenticated user, and return the user information along with the access token:

public function login()
    {
        $Accesstoken = Auth::user()->createToken('Access Token')->accessToken;

        return Response(['User' => Auth::user(), 'Access Token' => $Accesstoken]);
    }

Now the Controller will look like this:

    <?php


namespace App\Http\Controllers\API;

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\API\BaseController;

class AuthController extends BaseController
{
        /**
         * Instantiate a new controller instance.
         *
         * @return void
         */
        public function __construct()
        {
            $this->middleware('auth.basic'); 
        }

        public function login()
        {
            $Accesstoken = Auth::user()->createToken('Access Token')->accessToken;

            return Response(['User' => Auth::user(), 'Access Token' => $Accesstoken]);
        }
    }
Ali Ali
  • 1,756
  • 2
  • 15
  • 34
0

i have updated laravel 6 to 8 & i am using sanctum for API auth.

This works for me when i want to get token for API auth.

in User model

use Laravel\Sanctum\HasApiTokens;

and use the traits in function

use HasApiTokens

Model/User.php

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Hash;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'status'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function setPasswordAttribute($input)
    {
        if ($input) {
            $this->attributes['password'] = app('hash')->needsRehash($input) ? Hash::make($input) : $input;
        }
    }

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

    public function scopeActive($query){
        return $query->where('status', 'ACTIVE');
    }
}
shazim ali
  • 360
  • 4
  • 18