pps-php

Laravel Integration

This document shows how to integrate PPS-PHP with Laravel.


Install

composer require pps-protocol/pps-php

Routes

use App\Http\Controllers\PpsAuthController;

Route::get('/pps/challenge', [PpsAuthController::class, 'challenge']);
Route::post('/pps/verify', [PpsAuthController::class, 'verify']);
Route::post('/pps/register', [PpsAuthController::class, 'register']);

Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Pps\Server\AuthenticationHandler;
use Pps\Server\ChallengeGenerator;
use Pps\Server\RegistrationHandler;
use Pps\Storage\PdoStorage;
use Pps\Util\SystemTimeProvider;

class PpsAuthController extends Controller
{
    private function storage(): PdoStorage
    {
        $pdo = new \PDO(
            config('database.connections.mysql.dsn'),
            config('database.connections.mysql.username'),
            config('database.connections.mysql.password')
        );

        return new PdoStorage($pdo);
    }

    public function challenge(): JsonResponse
    {
        $generator = new ChallengeGenerator(new SystemTimeProvider());
        $challenge = $generator->create(config('app.url'));

        session(['pps_nonce' => $challenge->nonceB64u]);

        return response()->json([
            'nonce' => $challenge->nonceB64u,
            'epoch' => $challenge->epoch,
            'qr_payload' => $challenge->qrPayload,
        ]);
    }

    public function verify(Request $request): JsonResponse
    {
        $request->validate([
            'pulse_token' => 'required|string',
            'trust_code' => 'nullable|string',
        ]);

        $handler = new AuthenticationHandler($this->storage(), new SystemTimeProvider());

        $result = $handler->verify(
            pulseToken: $request->input('pulse_token'),
            rpId: config('app.url'),
            nonceB64u: session('pps_nonce'),
            trustCode: $request->input('trust_code')
        );

        if ($result->honey) {
            return response()->json(['status' => 'ok']);
        }

        if (!$result->ok) {
            return response()->json(['status' => 'error'], 401);
        }

        session()->forget('pps_nonce');

        return response()->json(['status' => 'ok']);
    }

    public function register(Request $request): JsonResponse
    {
        $request->validate([
            'registration_token' => 'required|string',
            'nonce' => 'required|string',
        ]);

        $handler = new RegistrationHandler($this->storage(), new SystemTimeProvider());

        $result = $handler->verify(
            token: $request->input('registration_token'),
            rpId: config('app.url'),
            nonceB64u: $request->input('nonce')
        );

        if (!$result->ok) {
            return response()->json(['status' => 'error'], 400);
        }

        return response()->json(['status' => 'ok']);
    }
}

Service Provider Optional

You may bind storage in a service provider:

use Pps\Storage\PdoStorage;
use Pps\Storage\StorageInterface;

$this->app->singleton(StorageInterface::class, function ($app) {
    return new PdoStorage($app['db']->connection()->getPdo());
});