Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Dpop Laravel Package

labrodev/laravel-dpop

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to Begin

  1. Installation:

    composer require labrodev/dpop
    php artisan dpop:install
    
    • Follow the interactive prompts to configure .env and publish the config file.
  2. First Use Case:

    • Issue a DPoP-bound JWT: Use the built-in token endpoint (POST /oauth/token) with a DPoP proof in the DPoP header. Example cURL:
      curl -X POST "http://your-app.test/oauth/token" \
           -H "DPoP: <your-dpop-jwt>" \
           -H "Content-Type: application/x-www-form-urlencoded" \
           -d "grant_type=client_credentials"
      
    • Protect a Route: Apply the dpop middleware to a route to enforce DPoP verification:
      Route::get('/protected', function () {
          return response()->json(['message' => 'Protected route']);
      })->middleware('dpop');
      
  3. Key Setup:

    • Generate a client key pair (e.g., using OpenSSL) and configure the DPOP_CLIENT_PRIVATE_KEY and DPOP_CLIENT_PUBLIC_KEY in .env.
    • Example key generation:
      openssl ecparam -genkey -name prime256v1 -out client_private.pem
      openssl ec -in client_private.pem -pubout -out client_public.pem
      

Implementation Patterns

Workflows

  1. Token Issuance:

    • Client-Side: Generate a DPoP JWT using the client's private key. Include the jti (JWT ID) and htp (HTTP request target) claims. Example (pseudo-code):
      const dpopJwt = createDpopJwt({
          jti: 'unique-request-id',
          htp: 'http://your-app.test/protected',
          privateKey: clientPrivateKey
      });
      
    • Request: Send the DPoP JWT in the DPoP header along with the OAuth2 token request.
  2. Route Protection:

    • Middleware: The dpop middleware automatically:
      • Extracts the DPoP JWT from the DPoP header.
      • Verifies the JWT signature using the client's public key.
      • Ensures the jti and htp claims match the request.
    • Custom Logic: Extend the middleware or use events (DpopVerified, DpopFailed) for custom logic.
  3. Token Verification:

    • Incoming Requests: For protected routes, the middleware validates the DPoP proof before processing the request.
    • Token Binding: The issued JWT (e.g., OAuth2 access token) is bound to the client's public key via the cnf claim.

Integration Tips

  • OAuth2 Integration:

    • Use the package alongside laravel/passport or league/oauth2-server for OAuth2 flows. The dpop middleware can be chained with OAuth2 middleware.
    • Example:
      Route::get('/api/user', function () {
          return response()->json(['user' => auth()->user()]);
      })->middleware(['auth:api', 'dpop']);
      
  • API Clients:

    • For API clients (e.g., mobile/web apps), generate DPoP proofs dynamically using the client's private key. Libraries like dpop-js can help.
  • Testing:

    • Mock the DPoP header in tests:
      $response = $this->withHeaders(['DPoP' => $validDpopJwt])
                       ->get('/protected');
      
  • Key Rotation:

    • Store client keys securely (e.g., AWS KMS, HashiCorp Vault). Rotate keys periodically and update the DPOP_CLIENT_PUBLIC_KEY in the config.

Gotchas and Tips

Pitfalls

  1. Key Management:

    • Private Key Exposure: Never hardcode private keys in your application. Use environment variables or secure key management systems.
    • Key Format: Ensure the private key is in PEM format with the correct header/footer:
      -----BEGIN EC PRIVATE KEY-----
      ...
      -----END EC PRIVATE KEY-----
      
    • Key Type: The package expects EC P-256 keys. Using other key types (e.g., RSA) will fail.
  2. DPoP JWT Claims:

    • jti Uniqueness: The jti (JWT ID) must be unique per request. Reusing jti values can lead to validation failures.
    • htp Mismatch: The htp (HTTP target) claim must exactly match the request's host and path. Trailing slashes or case sensitivity may cause issues.
      • Example: htp: "https://example.com/api" vs. htp: "https://example.com/api/".
  3. Middleware Order:

    • Place the dpop middleware after OAuth2 authentication middleware to avoid redundant checks:
      // Correct order
      ->middleware(['auth:api', 'dpop'])
      
    • Placing dpop before auth:api may cause unnecessary DPoP validation for unauthenticated requests.
  4. Token Endpoint:

    • The built-in token endpoint (/oauth/token) expects the DPoP header. Ensure your OAuth2 client libraries support custom headers.
    • For manual requests, include the DPoP header even for grant_type=client_credentials.
  5. CORS:

    • If using CORS, ensure the DPoP header is included in the allowed headers:
      // config/cors.php
      'paths' => ['api/*', 'oauth/token'],
      'allowed_headers' => ['*', 'DPoP'],
      

Debugging

  1. Validation Errors:

    • Check the dpop.log file (if configured) for detailed errors.
    • Common errors:
      • Invalid DPoP signature: Verify the private/public key pair.
      • JTI already used: Regenerate the jti for the request.
      • HTP mismatch: Ensure the htp claim matches the request URL.
  2. Logging:

    • Enable debug logging in config/dpop.php:
      'debug' => env('DPOP_DEBUG', false),
      
    • Logs will include DPoP verification details and errors.
  3. Testing DPoP:

    • Use tools like JWT.io to decode and verify DPoP JWTs manually.
    • For testing, generate a DPoP JWT locally:
      php artisan dpop:generate-dpop-jwt --jti=test123 --htp="http://your-app.test/protected" --private-key-path=client_private.pem
      

Extension Points

  1. Custom Middleware:

    • Extend the DpopMiddleware class to add custom logic:
      namespace App\Http\Middleware;
      
      use Labrodev\Dpop\Middleware\DpopMiddleware as BaseDpopMiddleware;
      
      class CustomDpopMiddleware extends BaseDpopMiddleware
      {
          public function handle($request, Closure $next)
          {
              // Custom logic before DPoP verification
              $response = parent->handle($request, $next);
      
              // Custom logic after DPoP verification
              return $response;
          }
      }
      
    • Register the middleware in app/Http/Kernel.php:
      protected $routeMiddleware = [
          'dpop' => \App\Http\Middleware\CustomDpopMiddleware::class,
      ];
      
  2. Events:

    • Listen for DPoP events to react to verification outcomes:
      use Labrodev\Dpop\Events\DpopVerified;
      use Labrodev\Dpop\Events\DpopFailed;
      
      DpopVerified::listen(function (DpopVerified $event) {
          // Log successful DPoP verification
      });
      
      DpopFailed::listen(function (DpopFailed $event) {
          // Handle failed DPoP verification (e.g., revoke token)
      });
      
  3. Token Binding:

    • Customize how the access token is bound to the client's public key by extending the DpopTokenService:
      namespace App\Services;
      
      use Labrodev\Dpop\Services\DpopTokenService as BaseDpopTokenService;
      
      class CustomDpopTokenService extends BaseDpopTokenService
      {
          public function bindTokenToClient($token, $clientId)
          {
              // Custom binding logic
              return parent::bindTokenToClient($token, $clientId);
          }
      }
      
    • Bind the service in config/dpop.php:
      'token_service' => \App\Services\CustomDpopTokenService::class,
      
  4. Key Storage:

    • Override the default key storage by
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle