The blog of Andrei Ciuculescu

Laravel Passport - Up and running with Laradock

30 Jul 2019

Here are my fresh notes on Laravel Passport. Even thought this is an already established project I took some time to document my trials getting it up and working with Laradock. Hopefully I get around to adding some text and make this easier to read.

The target here is to create an api authentication endpoint with laravel passport.

Here’s the shortlist of the steps

Create a new project

Since the last post the laravel command is no longer working in the laradock client. Which is no wonder since if you run commands in a docker container you shouldn’t expect them there after reboot :)

To fix this we:

  • check that laradock/workspace/Dockerfile has the line composer global require "laravel/installer"
  • to enable this then, just set the laradock/.env WORKSPACE_INSTALL_LARAVEL_INSTALLER=true then do dc build workspace and reboot
  • finally ssh back inside and check by doing laravel new api-auth

Setup the new project in bitbucket

I am doing this from the host

cd /path/to/api-auth
git init
git remote add origin git@bitbucket.org:lpgfmk/api-auth.git
git add -A
git commit -m "- initial commit"
git push -u origin master

You may follow along with the api-auth project here.

Setup the project locally

Config .env

DB_HOST=mysql
DB_DATABASE=api_auth
DB_USERNAME=default
DB_PASSWORD=secret

Config DB

  • Setup credentials

Start from the default sql config by doing

cp laradock/mysql/docker-entrypointinitdb.d/createdb.sql.example laradock/mysql/docker-entrypointinitdb.d/createdb-api_auth.sql

  • Update createdb-api_auth.sql content with
    CREATE DATABASE IF NOT EXISTS `api_auth` COLLATE 'utf8_general_ci' ;
    CREATE USER 'api_auth'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';
    GRANT ALL ON `api_auth`.* TO 'api_auth'@'%' ;
    
  • To run these changes do
    winpty docker-compose exec mysql bash
    mysql -u root -proot < /docker-entrypoint-initdb.d/createdb-api_auth.sql
    

Config webserver

  • Start from the default config by doing
    cp laradock/nginx/sites/laravel.conf.example laradock/nginx/sites/api_auth.conf
    
  • Replace laravel with api-auth in the file

  • Reboot nginx: dc build nginx && dc down && fmk-lara-up

  • Create local DNS

In your host’s windows config add 127.0.0.1 api-auth.local

  • Test by opening http://api-auth.local in browser

Code work

Scaffolding done, getting ready to install the project proper.

Project setup

  • ignore .idea and IntelliJ setup
  • setup initial db structure by doing php artisan migrate
  • composer require laravel/passport
  • php artisan migrate
  • add the api trait to the User.php model
  • add routes
  • update the config/auth.php file with the passport driver

Install vue

  • run vendor publish
  • run npm build
    • errors
    • npm run cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js –progress –hide-modules –config=node_modules/laravel-mix/setup/webpack.conf ig.js
    • error npm ERR! missing script: cross-env
    • google
    • do npm install --global cross-env
    • npm run dev: still missing
    • npm install: ok
    • npm run dev: ok
  • npm install axios

Create a Oauth client from cli

php artisan passport:client

Create a Oauth client as user

  • scaffold auth php artisan make:auth
  • make the view api-auth.blade.php and add a route for it (api-auth)
@extends('layouts.app')
@section('content')

    <div id="app">
        <passport-clients></passport-clients>
        <passport-authorized-clients></passport-authorized-clients>
        <passport-personal-access-tokens></passport-personal-access-tokens>
    </div>

@endsection
  • register a new user in the app
  • go to /api-auth and Create new client (client id 5, client secret cHfCmLgWXttLNiV2TNVGeH9r7slrCA9DyiFr7EBg)

Use a personal access token to authorize your calls

  • do php artisan passport:client --personal
  • go to /api-auth and Create new token
  • use this token in postman to access the api

Authorize your application with oauth

Create a new application api-client. I won’t include the steps for this since they’re the same as for api-auth.

There are some extra things to check you include in this app

  • guzzle composer require "guzzlehttp/guzzle:~5.3"
  • ext-json "ext-json": "*"

Then update routes/web.php with

Route::get('/redirect', function () {
    $query = http_build_query([
        'client_id' => '5',
        'redirect_uri' => 'http://api-client.local/callback',
        'response_type' => 'code',
        'scope' => '',
    ]);

    return redirect('http://api-auth.local/oauth/authorize?'.$query);
});

Route::get('/callback', function (Request $request) {
    $http = new GuzzleHttp\Client;

    $response = $http->post('http://api-auth.local/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => '5',
            'client_secret' => 'cHfCmLgWXttLNiV2TNVGeH9r7slrCA9DyiFr7EBg',
            'redirect_uri' => 'http://api-client.local/callback',
            'code' => $request->code,
        ],
    ]);

    return json_decode((string) $response->getBody(), true);
});

Now access the http://api-client.local app in an incognito browser. Notice you are redirected and you need to login with the credentials from http://api-auth.local. So try and authorize the thing.

… anticlimactic error No method can handle the form_params config key

  • fix this by going to composer.json and set guzzlehttp/guzzle : ~6.0
  • then do a composer update

… anticlimactic error #2 cURL error 7: Failed to connect to api-auth.local port 80: Connection refused (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)

  • find the fix here
  • change docker-compose.yml in laradock at the php-fpm: section add
      extra_hosts:
    - "dockerhost:${DOCKER_HOST_IP}"
    - "api_auth.local:${DOCKER_HOST_IP}"
    - "api_client.local:${DOCKER_HOST_IP}"
  • do docker-compose build --no-cache php-fpm workspace then dc down && fmk-lara-up
  • still no go… actually because typo… (even though I also tested in the workspace section)
  • change docker-compose.yml in laradock at the php-fpm: section add
      extra_hosts:
    - "dockerhost:${DOCKER_HOST_IP}"
    - "api-auth.local:${DOCKER_HOST_IP}"
    - "api-client.local:${DOCKER_HOST_IP}"
  • no more error but token expired so just redo the login, click authorize, get the response which is an array like: {“token_type”:”Bearer”,”expires_in”:31622400,”access_token”:””,”refresh_token”:””}
  • now you may also view your authorized applications by reloading the view

Authorize internal application api calls

  • First do npm run dev
  • Then add to the ExampleComponent.vue
import axios from 'axios'

export default {
    ready() {
        console.log('Component mounted.')

        axios.get("/api/user")
            .then(response => {
                console.log(response.data);
            });
    }
}
  • notice you get GET http://api-auth.local/api/user 401 (Unauthorized)
  • open Http\Kernel.php and in the web middlewareGroups add \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class
  • reload the page and notice the call going through by magic

More reading