How to protect Laravel API with Magic Link
A tutorial to demonstrate how to add authorization to a Laravel API with Magic's Laravel Plugin.
#Quickstart
01$ git clone https://github.com/magiclabs/example-laravel-api.git
02$ cd example-laravel-api
03$ mv .env.example .env
04$ composer install
05$ php artisan serve
Update .env
with your LIVE_SECRET_KEY
01MAGIC_SECRET_API_KEY=sk_live_12A34V567B89
#Prerequisites
This guide covers building an API protected by Magic issued DID token. Mobile, Desktop, and Native applications typically consume this type of API using Magic’s Web, React Native, iOS, or Android Client SDK.
This guide demonstrates:
- How to check for a Decentralized ID Token (DIDT) in the
Authorization
header of an incoming HTTP request.; if - How to check if the token is valid, using the validate() of Magic’s Laravel SDK
This guide assumes:
You have already configured your client-side app with the Magic Client SDK
Your Laravel Application is up and running. If not, please run the following command to get it started.
#Via Composer
Bash01composer create-project laravel/laravel magic-app
02
03cd magic-app
04
05php artisan serve
#Via Laravel Installer
Or, you may install the Laravel Installer as a global Composer dependency:
Bash01composer global require laravel/installer
02
03laravel new magic-app
04
05cd magic-app
06
07php artisan serve
#Validate DID Tokens
#Install dependencies
Protecting your Laravel API requires a middleware to check for and verify a bearer tokeAuthorizationDecentralizedauthorizationorization header of an incoming HTTP request. We'll do that using tools provided by the Magic Laravel SDK.
01composer require magiclabs/magic-laravel
#Configure the plugin
The magic-laravel
plugin comes with a configuration file that can be generated using Artisan. First, generate the configuration file from the command line:
01php artisan vendor:publish --provider="MagicLaravel\ServiceProvider"
After the file is generated, it will be located at config/magic.php
.
01// config/magic.php
02return [
03 /*
04 |-----------------------------------------------------------------------
#Get your Magic Secret Key
Sign Up with Magic and get your MAGIC_SECRET_KEY
.
Feel free to use the First Application automatically configured for you or create a new one from your Dashboard.
Edit the .env
file to add the configuration values needed to verify the incoming DID tokens.
01MAGIC_SECRET_API_KEY=sk_123456789
02MAGIC_RETRIES=
03MAGIC_TIMEOUT=
04MAGIC_BACKOFF_FACTOR=
#Protect API Endpoints
The routes shown below are available for the following requests:
GET /api/public
: available for non-authenticated requestsGET /api/private
: available for authenticated requests containing a DID Token
For the private
API route, we'll need middleware to check for a bearer token in an Authorization
header for the request and verify that the token is valid. We'll create that middleware using the make:middleware
Artisan command:
01php artisan make:middleware CheckDIDT
Now, let's implement the handle()
method that Laravel will call automatically for the route:
01<?php
02// App/Http/Middleware/CheckDIDT.php
03
04namespace App\Http\Middleware;
05
06use Closure;
07use Illuminate\Http\Request;
08use Magic;
09use MagicAdmin\Exception\DIDTokenException;
10use MagicAdmin\Exception\RequestException;
11
12class CheckDIDT
13{
14 /**
15 * Handle an incoming request.
16 *
17 * @param \Illuminate\Http\Request $request
18 * @param \Closure $next
19 * @return mixed
20 */
21 public function handle(Request $request, Closure $next)
22 {
23
24 $did_token = $request->bearerToken();
25
26 if($did_token){
27
28 try {
29 //verifying the DID Token
30 Magic::token()->validate($did_token);
31 $user = Magic::user()->get_metadata_by_token($did_token);
32 if (!$user) {
33 return response()->json(["message" => "Unauthorized user"], 401);
34 }
35 } catch (DIDTokenException $e) {
36 return response()->json(["message" => $e->getMessage()], 401);
37 } catch (RequestException $e) {
38 return response()->json(["message" => "Request Exception"], 401);
39 }
40
41 } else {
42 return response()->json(["message" => "Bearer token missing"], 401);
43 }
44
45 return $next($request);
46 }
47}
This middleware:
- Retrieves the Bearer token from the request and call the
validate
function to verify the DIDT. - Uses the DID Token to retrieve User’s Meta Data
Magic::user()->get_metadata_by_token($did_token)
- Catches any exceptions thrown if the DIDT is expired, malformed or invalid. Also, checks for
RequestException
.
Next, we register this middleware in the HTTP Kernel with the name didt
:
01// App/Http/Kernel.php
02// …
03class Kernel extends HttpKernel {
04 // ...
05 protected $routeMiddleware = [
06 // ...
07 'didt' => \App\Http\Middleware\CheckDIDT::class,
08 // …
09}
We are now able to protect individual API endpoints by applying the didt
middleware:
01// routes/api.php
02use Magic;
03
04// This endpoint does not need authentication.
05Route::get('/public', function (Request $request) {
06 return response()->json(["message" => "Hello from a public endpoint! You don't need to be authenticated to see this."]);
07});
08
09// These endpoints require a valid did token and fetches user's data using did token
10Route::get('/private', function (Request $request) {
11 return response()->json([
12 "message" => "Hello from a private endpoint! You need to have a valid DID Token to see this.",
13 "user" => Magic::user()->get_metadata_by_token($request->bearerToken())->data
14 ]);
15})->middleware('didt');
The /api/private
route is now only accessible if a valid DID Token is included in the Authorization
header of the incoming request.
#Obtaining DID Token
#Fork the template code on Codesandbox
To get the DID Token, fork our Laravel API Authorization template in CodeSandBox.
#Update the MAGIC_PUBLISHABLE_KEY
Replace the pk_live5A2E52658C87281B
string with your Publishable API Key
from the Magic Dashboard: on line 46
01/* 2️⃣ Initialize Magic Instance */
02const magic = new Magic('pk_live_5A2E52658C87281B');
#Live Frontend Application
You now have a working Frontend Application.
Login and copy the DID Token
for Testing the API with Postman.
#Using your API
The /api/private
route is now only accessible if a valid DID Token is included in the Authorization
header of the incoming request.
Now, let’s start the Laravel server locally:
01php artisan serve --port=8001
Send a GET
request to the public route - http://localhost:8001/api/public
- and you should receive back:
01{
02 "message": "Hello from a public endpoint! You don't need to be authenticated to see this."
03}
Now send a GET
request to the private route - http://localhost:8001/api/private
- and you should get a 401 status and the following message:
01{ "message": "Bearer token missing" }
Add an Authorization
header set to Bearer DID_TOKEN
using the token generated above. Send the GET
request to the private route again, and you should see:
01{
02 "message": "Hello from a private endpoint! You need to have a valid DID Token to see this.",
03 "user": {
04 "email": "[email protected]",
05 "issuer": "did:ethr:0xaa12b334C1f3d……….62367e5B8e",
06 "public_address": "0xaa12b334C1f3d……….62367e5B8e"
07 }
08}
#Done
Congratulations! You have successfully secured your Laravel API with Magic.
#What's Next
#Use Magic with existing tools
Node.js – Now that you understand how Magic works on the client-side, we strongly recommend you take a look at an end-to-end, full-stack example to learn about how to connect it to a Node.js backend server and see the full potential of Magic!
Firebase – Magic provides much flexibility and composability to be combined with many other powerful platforms such as Firebase. Learn how to plug Magic into the entire Firebase suite of tools!
Next.js (Vercel) – Learn how to use the popular Next.js framework to build a React app and deploy it with Vercel.
#Customize your Magic flow
You can customize the login experience using your own UI instead of Magic's default one and/or customize the magic link email with your brand. Learn how to customize.