laravel

A collection of 2 posts

Easy JSON API Responses with Laravel

Laravel has evolved so much since I last laid hands on in over a year-and-a-half ago. Eloquent API Resources are a massive boost to those of us looking to easily hammer out standardized JSON API responses. While these are not fully-spec compliant {json:api} responses, they're pretty darn close and good enough for most use cases.

Simply put, an API Resource consumes a database query in the form of a model and spits out structured JSON. In the usual Laravel way, API Resources are easily customized.

Heads up: this is based on Laravel 8.x. and may not work in previous versions.

Create a Resource for a Single Object

So, let's say I want to query a single user. First I'll start by using artistan to create the Resource class file.

php artisan make:resource UserResource

Then in the toArray($request) method, I'll return an array of what I want to expose to the API consumer. This is great, as I can abstract the model properties away from the response and do some basic serialization before responding, if desired.

public function toArray($request)
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}

Now I can simply return the Resource from the controller. By default, the array will be wrapped in a data key. I recommend leaving this setting as is but it can be overridden as per the documentation.

use App\Http\Resources\UserCollection;
use App\Models\User;

Route::get('/users', function () {
    return new UserCollection(User::all());
});

The object returned will look similar to the following.

{
    data: {
    	id: 1,
        name: 'Luke Skywalker',
        email: 'l.skywalker@jedi.org',
        created_at: 'timestamp',
        updated_at: 'timestamp'
    }
}

Return a Collection

The JsonResource class has a static collection method to return an array of Resources, or in my case, Users. This is so dang easy I want to weep for joy.

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users', function () {
    return UserResource::collection(User::all());
});

This returns an array of Users in the data key.

{
    data: [
        {
            id: 1,
            name: 'Luke Skywalker',
            email: 'l.skywalker@jedi.org',
            created_at: 'timestamp',
            updated_at: 'timestamp'
        },
        {
            id: 2,
            name: 'Leia Organa',
            email: 'l.organa@rebelalliance.org',
            created_at: 'timestamp',
            updated_at: 'timestamp'
        },
        ...
    ]
}

More complex customizations can be done by creating a UserCollection resource, which Laravel is smart enough to proxy to a collection of UserResource instances.

Other Goodies

Eloquent API Resources also makes these features seem a breeze to implement:

View the full documentation for the full details of Eloquent API Resources.

Getting Started with Laravel Sanctum

Getting Started with Laravel Sanctum

Update on 01/06/2021: Added links to some helpful articles.


Laravel Sanctum is a slim authentication system for Single Page Applications that don't need the full-meal-deal of an OAuth2 implementation. Sanctum protects an API from Cross Site Forgery Requests by setting a browser cookie and checking for the cookie (or token) on request to ensure requests are coming from approved requesters, be it a first-party SPA or mobile application. It should be paired with a user authentication system.

The majority of this article is a concise summary of what's in the official documentation. If you need more details, follow the link below.

Official Documentation: https://laravel.com/docs/8.x/sanctum

Other helpful sources:

Quotes from the Docs

"Laravel Sanctum provides a featherweight authentication system for SPAs (single page applications), mobile applications, and simple, token based APIs."

"Laravel Sanctum is only concerned with managing API tokens and authenticating existing users using session cookies or tokens. Sanctum does not provide any routes that handle user registration, password reset, etc."

"When Sanctum examines an incoming HTTP request, it will first check for an authentication cookie and, if none is present, Sanctum will then examine the Authorization header for a valid API token."

Installation

Go to the project folder in terminal and add the Sanctum package with composer.

composer require laravel/sanctum

Publish the config and migration files.

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

Then run migrations.

php artisan migrate

Config for SPA Authentication

Sanctum uses Laravel's built-in cookie based session authentication services. You get CSRF protection, session auth, and leakage protection of auth credentials via XSS.

Note! Because using cookies, SPA and API must share the same TLD but can be on separate subdomains.

First Party Domains

Decide what domain(s) the SPA will be making requests from using stateful config in config/sanctum.php or set as SANCTUM_STATEFUL_DOMAINS in .env[.local]

The Sanctum config file accepts a comma-separated list of domains. Include port numbers, if required!

"Stateful" means the cookies will be set for these domains. If the cookie exists, the SPA is authenticated. The session cookie being stored in the browser signifies the app knows the user is logged in and can retrieve information about the logged in user. A "stateless" system would require a token to be passed to the API on every single request.

Add Middleware

Add EnsureFrontendRequestsAreStateful::class, to api middleware group in app/Http/Kernel.php file.

CORS & Cookies

Update supports_credentials to true in config/cors.php file. This ensures the applications CORS configuration is returning the Access-Control-Allow-Credentials header with a value of True.

More on CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Gotcha: One thing to note, which tripped me up for a bit. When requesting a cookie from /sanctum/csrf-cookie from a domain other than the API, ensure you set "credentials" : "include" or the cookie won't be saved to browser storage.

Axios make this easy with the following global configuration.

axios.defaults.withCredentials = true;

Lastly, update sessions cookie domain config in config/session.php or .env to allow subdomains.

'domain' => '.domain.com',

Protecting Routes

Attach the sanctum authentication guard to API routes in the routes/api.php file. All requests to these routes require the X-XSRF-TOKEN header or a valid API token header.

use Illuminate\Http\Request;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Authenticating

CSRF

To authenticate the SPA, the "login" page should first make a request to /sanctum/csrf-cookie endpoint to initialize. This sets an XSRF-TOKEN cookie, which should be passed in an X-XSRF-TOKEN header on subsequent requests.

axios.get('/sanctum/csrf-cookie').then(response => {
    // Login...
});

Logging In

Now a POST request to /login can be implemented.

If successful, subsequent requests to application routes will be automatically authenticated via the session cookie that the Laravel app issued to the client.

If the users' session expires due to lack of activity and a 401 or 419 HTTP response is received, redirect to /login.

Next Up

This is only step one. Now to configure user token authentication with Laravel Fortify a custom implementation.