As in every application, users should be able to update their profile. Personally, I like to separate two actions: change profile details and change password.
So, I vote for these API endpoints:
You could also make both PUT actions into one endpoint, with an if-else statement, but I personally like the separation of those concerns.
Let's generate a Profile Controller - this time with two methods in it.
I will still keep the namespace of Auth because those features are related to the authenticated users.
php artisan make:controller Api/V1/Auth/ProfileController
This will be the code inside.
app/Http/Controllers/Api/V1/Auth/ProfileController.php:
namespace App\Http\Controllers\Api\V1\Auth; use App\Http\Controllers\Controller;use Illuminate\Http\Request;use Illuminate\Http\Response;use Illuminate\Validation\Rule; class ProfileController extends Controller{ public function show(Request $request) { return response()->json($request->user()->only('name', 'email')); } public function update(Request $request) { $validatedData = $request->validate([ 'name' => ['required', 'string'], 'email' => ['required', 'email', Rule::unique('users')->ignore(auth()->user())], ]); auth()->user()->update($validatedData); return response()->json($validatedData, Response::HTTP_ACCEPTED); }}
Not sure I need to explain much here: in the show()
method we just show a few fields of a logged-in user (we don't show any ID or password-sensitive fields), and in the update()
method we validate the data, update the DB row and return the updated data as JSON.
Now, the most important part: how do we get that auth()->user()
or $request->user()
automatically?
In the routes, we will make those endpoints as a group, with the Middleware auth:sanctum
. Then, we pass a Bearer token in the API request. Yes, the one that we got returned from login/register.
routes/api.php:
Route::middleware('auth:sanctum')->group(function () { Route::get('profile', [Auth\ProfileController::class, 'show']); Route::put('profile', [Auth\ProfileController::class, 'update']);});
Now, if we try to make a request without any token, we should get a 401 status code, which means unauthenticated.
But look what happens if we make the same request, but add a "Bearer Token" in the Auth section of Postman: it returns the user's fields!
Notice: in your API client the Bearer token may be placed elsewhere than in my Postman, check for your tool's documentation.
Now, what happens if we try to update the name/email? The same Bearer token should be included, too, for this and all other upcoming logged-in requests.
Successfully updated!
Now, let's create the Change Password endpoint.
By now, you could totally do this one without my help, right? It will be almost identical. But let me just list my actions, with just a few comments:
php artisan make:controller Api/V1/Auth/PasswordUpdateController
This will be an invokable Controller:
app/Http/Controllers/Api/V1/Auth/PasswordUpdateController.php:
namespace App\Http\Controllers\Api\V1\Auth; use App\Http\Controllers\Controller;use Illuminate\Http\Request;use Illuminate\Http\Response;use Illuminate\Support\Facades\Hash;use Illuminate\Validation\Rules\Password; class PasswordUpdateController extends Controller{ public function __invoke(Request $request) { $request->validate([ 'current_password' => ['required', 'current_password'], 'password' => ['required', 'confirmed', Password::defaults()], ]); auth()->user()->update([ 'password' => Hash::make($request->input('password')), ]); return response()->json([ 'message' => 'Your password has been updated.', ], Response::HTTP_ACCEPTED); }}
Now, we add it to the routes.
routes/api.php:
Route::middleware('auth:sanctum')->group(function () { Route::get('profile', [Auth\ProfileController::class, 'show']); Route::put('profile', [Auth\ProfileController::class, 'update']); Route::put('password', Auth\PasswordUpdateController::class);});
Try it out... Success!
Now we know how to make the requests for the logged-in user, passing a Bearer Token.
If we want the user to log out of the API access, all we need to do is destroy the token that Sanctum had generated at the login/register.
So, a new Controller:
php artisan make:controller Api/V1/Auth/LogoutController
And a new endpoint inside the same group with auth:sanctum
Middleware:
routes/api.php:
Route::post('auth/logout', Auth\LogoutController::class);
The content of the method is very simple:
app/Http/Controllers/Api/V1/Auth/LogoutController.php:
namespace App\Http\Controllers\Api\V1\Auth; use App\Http\Controllers\Controller;use Illuminate\Http\Request; class LogoutController extends Controller{ public function __invoke(Request $request) { $request->user()->currentAccessToken()->delete(); return response()->noContent(); }}
We can access the currently logged-in user with $request->user()
or auth()->user()
, they are identical. And then, we call the Sanctum's methods of currentAccessToken()
and then delete()
, returning the noContent()
which shows 204 HTTP Status Code.
After this call, the previous token will be deleted and no longer valid for any future requests, the user would need to re-login again to get a new token.