Skip to content

Commit

Permalink
Laravel API quickstart (#2200)
Browse files Browse the repository at this point in the history
* 🎉 Laravel API quickstart

---------

Co-authored-by: Dan Moore <[email protected]>
  • Loading branch information
vcampitelli and mooreds authored Jul 31, 2023
1 parent aa837e0 commit 19dec6f
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 4 deletions.
9 changes: 9 additions & 0 deletions astro/src/pages/docs/quickstarts/quickstart-sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { getCollection } from "astro:content";
interface QuickStartSection {
key: string;
icon?: string;
faIcon?: string;
color: string;
title: string;
anchorTag: string;
desc?: string;
Expand Down Expand Up @@ -185,6 +187,13 @@ const qsSections: QuickStartSection[] = [
faIcon: 'fa-x',
navColor: 'fuchsia',
},
{
href: '/docs/v1/tech/tutorials/integrate-laravel-api',
title: 'Laravel',
icon: '/img/icons/laravel.svg',
faIcon: 'fa-block',
navColor: 'rose',
},
],
},
];
Expand Down
4 changes: 4 additions & 0 deletions site/_data/exampleapps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@
name: Laravel
description: Protecting Laravel pages with FusionAuth
language: php
- url: https://github.com/fusionauth/fusionauth-example-laravel-api
name: Laravel API
description: Protecting a Laravel API with a JWT
language: php
- url: https://github.com/fusionauth/fusionauth-example-laravel-single-sign-on
name: Laravel Single Sign-On
description: Single sign-on with Laravel and FusionAuth
Expand Down
1 change: 1 addition & 0 deletions site/_layouts/doc.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-express-api.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-express-api">Express API</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-flutter.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-flutter">Flutter</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-java-spring.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-java-spring">Java Spring</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-laravel-api.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-laravel-api">Laravel API</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-python-django.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-python-django">Python Django</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-python-flask.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-python-flask">Python Flask</a></li>
<li {% if page.url == "/docs/v1/tech/tutorials/integrate-react.html" %}class="active"{% endif %}><a href="/docs/v1/tech/tutorials/integrate-react">React</a></li>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 3 additions & 4 deletions site/docs/quickstarts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,10 @@ <h2 class="docs-lp">Backend/API</h2>
<a id="w-node-_5bebad79-34b5-c0c4-57da-0bbf9662eb50-3d0acb82" href="/docs/v1/tech/tutorials/integrate-express-api" class="docs-lp-link-block shadow-removed quickstart w-inline-block"><img src="/assets/img/docs/landing/javascript.svg" loading="lazy" alt="" class="quickstart-tech-icon">
<div class="quickstart-tech-title">Express</div>
</a>
{% comment %}
<a id="w-node-_5bebad79-34b5-c0c4-57da-0bbf9662eb54-3d0acb82" href="/blog/2023/03/13/single-sign-on-laravel-fusionauth" class="docs-lp-link-block shadow-removed quickstart w-inline-block coming-soon"><img src="/assets/img/docs/landing/laravel.svg" loading="lazy" alt="" class="quickstart-tech-icon">
<div class="quickstart-tech-title">Laravel • Coming Soon</div>
</a>
{% endcomment %}
<a id="w-node-_5bebad79-34b5-c0c4-57da-0bbf9662eb54-3d0acb82" href="/docs/v1/tech/tutorials/integrate-laravel-api" class="docs-lp-link-block shadow-removed quickstart w-inline-block"><img src="/assets/img/docs/landing/laravel.svg" loading="lazy" alt="" class="quickstart-tech-icon">
<div class="quickstart-tech-title">Laravel</div>
</a>
<a id="w-node-_6ee16e80-ed71-b0ff-5c68-97a8ebdcd35d-3d0acb82" href="/docs/v1/tech/tutorials/integrate-ruby-rails-api" class="docs-lp-link-block shadow-removed quickstart w-inline-block"><img src="/assets/img/docs/landing/ruby-on-rails.svg" loading="lazy" alt="" class="quickstart-tech-icon">
<div class="quickstart-tech-title">Ruby On Rails</div>
</a>
Expand Down
1 change: 1 addition & 0 deletions site/docs/v1/tech/tutorials/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Tutorials in the documentation section:
* link:/docs/v1/tech/tutorials/integrate-expressjs[Integrate Express.js With FusionAuth]
* link:/docs/v1/tech/tutorials/integrate-flutter[Integrate Flutter With FusionAuth]
* link:/docs/v1/tech/tutorials/integrate-java-spring[Integrate Java Spring With FusionAuth]
* link:/docs/v1/tech/tutorials/integrate-laravel-api[Integrate a Laravel API With FusionAuth]
* link:/docs/v1/tech/tutorials/integrate-python-django[Integrate Python Django With FusionAuth]
* link:/docs/quickstarts/quickstart-python-flask-web[Integrate Flask With FusionAuth]
* link:/docs/v1/tech/tutorials/integrate-react[Integrate React With FusionAuth]
Expand Down
250 changes: 250 additions & 0 deletions site/docs/v1/tech/tutorials/integrate-laravel-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
---
layout: doc
title: Integrate Your Laravel API With FusionAuth
description: Integrate your Laravel API with FusionAuth
navcategory: getting-started
prerequisites: Composer, Docker and at least PHP 8.2
technology: Laravel
language: PHP
---

## Integrate Your {{page.technology}} API With FusionAuth

{% include docs/integration/_intro-api.md %}

## Prerequisites

{% include docs/integration/_prerequisites.md %}

## Download and Install FusionAuth

{% include docs/integration/_install-fusionauth.md %}

## Create a User and an API Key

{% include docs/integration/_add-user.md language=page.language %}

## Configure FusionAuth

Next, you need to set up FusionAuth. This can be done in different ways, but we’re going to use the [{{page.language}} client library](/docs/v1/tech/client-libraries/php). You can use the client library with an IDE of your preference as well.

First, make a directory:

```shell
mkdir setup-fusionauth && cd setup-fusionauth
```

Now, copy and paste the following file into `composer.json`.

```javascript
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-client-libraries/main/php/composer.json %}
```
Install the dependencies.
```shell
composer install
```
Then copy and paste the following file into `setup.php`.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-client-libraries/main/php/setup.php %}
```
Then, you can run the setup script.
{% include _callout-note.liquid content="The setup script is designed to run on a newly installed FusionAuth instance with only one user and no tenants other than `Default`. To follow this guide on a FusionAuth instance that does not meet these criteria, you may need to modify the above script. <br><br> Refer to the [PHP client library](/docs/v1/tech/client-libraries/php) documentation for more information." %}
This will create the FusionAuth configuration for your {{page.technology}} API.
```shell
php setup.php "YOUR_API_KEY_FROM_ABOVE"
```
### Retrieve JWKS Endpoint
Instead of manually storing the public key to verify JWTs, your application should automatically look it up using the [JWKS endpoint](/docs/v1/tech/oauth/endpoints#json-web-key-set-jwks).
As both the Laravel application, which you are going to create in the next step, and FusionAuth instance are running in different Docker Compose projects, they can't reach each other. To allow that, expose your FusionAuth instance to the Web using [ngrok](/docs/v1/tech/developer-guide/exposing-instance).
{% include _callout-note.liquid content="You could also add network connectivity between them by running [`docker network connect`](https://docs.docker.com/engine/reference/commandline/network_connect/)." %}
Now, log into your instance using the address `ngrok` gave you and browse to <span>Applications</span>{:.breadcrumb}. Locate the `PHP Example App` application that the setup script created for you and click <i/>{:.ui-button .green .fa .fa-search} to view its details. In the <span>OAuth2 & OpenID Connect Integration details</span>{:.uielement} section, locate <span>JSON Web Key (JWK) Set</span>{:.uielement} and copy it. You'll need this value later.
## Create Your {{page.technology}} API
Now you are going to create a {{page.technology}} application. While this section uses a simple {{page.technology}} application, you can use the same steps to integrate any {{page.technology}} application with FusionAuth.
First, make a directory:
```shell
mkdir ../setup-laravel && cd ../setup-laravel
```
Next, create a simple {{page.technology}} template using Laravel's build script. For this API, you don't need all the tools that the script would normally install (like Redis and Selenium), so we are only requiring [MariaDB](https://mariadb.org/), one of the most popular relational database management systems used with Laravel.
```shell
curl -s "https://laravel.build/fusionauth-example-laravel-api?with=mariadb" | bash
```
This can take several minutes to complete, so please be patient.
### Adding JWT Authentication
After it is done, change into the `fusionauth-example-laravel-api` directory and install [`fusionauth/jwt-auth-webtoken-provider`](https://github.com/fusionauth/fusionauth-laravel-jwt-auth-webtoken-provider), a library created by FusionAuth to add JWT validation capabilities to Laravel, and [`web-token/jwt-signature-algorithm-rsa`](https://github.com/web-token/jwt-signature-algorithm-rsa), a package to handle RSA algorithms.
```shell
cd fusionauth-example-laravel-api
./vendor/bin/sail composer require fusionauth/jwt-auth-webtoken-provider web-token/jwt-signature-algorithm-rsa
```
Add some authentication routes to `routes/api.php` and another endpoint to allow `GET` requests to `/api/messages`, which you'll create later.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/routes/api.php %}
```
Create `app/Http/Controllers/Api/AuthController.php` for all authentication logic.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/Http/Controllers/Api/AuthController.php %}
```
Laravel uses something called [Guards](https://laravel.com/docs/10.x/authentication#adding-custom-guards) to protect your endpoints, so we need to tell it about the new Guard provided from that library by editing `config/auth.php`.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/config/auth.php %}
```
Edit the `config/app.php` file to add some information about your FusionAuth instance.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/config/app.php %}
```
To make the library available for use, publish its configuration by running the command below.
```shell
./vendor/bin/sail artisan vendor:publish --provider="FusionAuth\JWTAuth\WebTokenProvider\Providers\WebTokenServiceProvider"
```
### Disabling Username/Password Authentication
The Laravel installer already brings some useful resources for many applications because it usually expects users to have a username and password for authentication. APIs in general should only expect an API key or a token to authenticate a request.
In this quickstart, disable the typical username/password authentication method and only allow JWTs as an authentication method. Start by removing the need for users to have a password by editing `app/Models/User.php`.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/Models/User.php %}
```
Change the created migration at `database/migrations/2014_10_12_000000_create_users_table.php`.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/database/migrations/2014_10_12_000000_create_users_table.php %}
```
You may remove all other migrations, as you won't need them.
```shell
rm database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php
rm database/migrations/2019_08_19_000000_create_failed_jobs_table.php
rm database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php
```
Run the remaining migrations to create the necessary tables in your database.
```shell
./vendor/bin/sail artisan migrate
```
### Provisioning New Users
By default, Laravel only allows JWTs that correspond to users in your database, but one of the greatest benefits of using FusionAuth is to have a single source of truth of user management. So, you want your API to automatically provision new users when it receives a trusted JWT from FusionAuth.
To do so, extend the default [User Provider](https://laravel.com/docs/10.x/authentication#adding-custom-user-providers) to create new users when receiving valid JWTs from FusionAuth.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/FusionAuth/Providers/FusionAuthEloquentUserProvider.php %}
```
Now, add an [Authentication Guard](https://laravel.com/docs/10.x/authentication#adding-custom-guards) in `app/FusionAuth/FusionAuthJWTGuard.php` to call that custom method from the User Provider created above.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/FusionAuth/FusionAuthJWTGuard.php %}
```
To override the existing classes with the ones you just added, you must create a [Service Provider](https://laravel.com/docs/10.x/providers) in `app/FusionAuth/Providers/FusionAuthServiceProvider.php`.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/FusionAuth/Providers/FusionAuthServiceProvider.php %}
```
Edit the `.env` file and add these lines there.
```ini
FUSIONAUTH_CLIENT_ID=e9fdb985-9173-4e01-9d73-ac2d60d1dc8e
FUSIONAUTH_URL=http://localhost:9011
JWT_ALGO=RS256
JWT_JWKS_URL=https://address.that.ngrok.gave.you/.well-known/jwks.json
JWT_JWKS_URL_CACHE=86400
```
You should change the `JWT_JWKS_URL` value to the [JWKS Endpoint you copied earlier](#retrieve-jwks-endpoint).
### Validating Issuer and Audience Claims
Besides verifying the signature, it is also recommended to validate the issuer (`iss`) and audience (`aud`) claims when receiving a JWT. To do those checks, you need to create two files.
First, create a file named `app/FusionAuth/Claims/Audience.php` that will check if the audience (`aud`) claim actually contains the Client Id for the application you created in FusionAuth.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/FusionAuth/Claims/Audience.php %}
```
Now, create a file called `app/FusionAuth/Claims/Issuer.php` to check if the issuer (`iss`) is actually your FusionAuth instance address.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/FusionAuth/Claims/Issuer.php %}
```
### Creating a Controller
Create a controller in `app/Http/Controllers/Api/MessagesController.php` to return a JSON message. This is going to be the protected API.
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/app/Http/Controllers/Api/MessagesController.php %}
```
### Creating the View
Change the view file located at `resources/views/welcome.blade.php` to add a <span>Log in with FusionAuth</span>{:.uielement} button when you are logged out and a container for the messages API response when you are logged in. This is of course not really all that typical; usually you'd have the user log into FusionAuth, store the token in the browser or mobile app, and then call the API. But since this is a quickstart to show you how to validate the
```php
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/resources/views/welcome.blade.php %}
```
Create a file named `public/js/fusionauth.js` that will fetch both `/api/messages` from the Laravel API and your user details from FusionAuth.
```javascript
{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-laravel-api/main/laravel/public/js/fusionauth.js %}
```
### Testing the Authentication Flow
Finally, start your application.
```shell
./vendor/bin/sail up -d
```
Browse to [localhost](http://localhost/) and click the <span>Log in with FusionAuth</span>{:.uielement} button.
{% include _image.liquid src="/assets/img/docs/tutorials/laravel-api/login.png" alt="Laravel application login screen." class="img-fluid" width="1200" figure=false %}
Log in with username `richard@example.com` and password `password`. You should be redirected back to your Laravel application with both your user details and the result of the messages API call.
{% include _image.liquid src="/assets/img/docs/tutorials/laravel-api/logged-in.png" alt="Laravel application welcome screen." class="img-fluid" width="1200" figure=false %}
The full code for this guide can be found [here](https://github.com/FusionAuth/fusionauth-example-laravel-api).

0 comments on commit 19dec6f

Please sign in to comment.