Weave 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

Persona User Bundle Laravel Package

champs-libres/persona-user-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require champs-libres/persona-user-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        ChampsLibres\PersonaUserBundle\ChampsLibresPersonaUserBundle::class => ['all' => true],
    ];
    
  2. Configuration Publish the default config:

    php bin/console champs-libres:persona-user:install
    

    Update config/packages/champs_libres_persona_user.yaml:

    champs_libres_persona_user:
        persona_api_endpoint: 'https://verifier.login.persona.org/verify'
        user_class: App\Entity\User
        user_provider: 'app.user_provider.persona'
    
  3. First Use Case Add the login button to a template (e.g., templates/base.html.twig):

    <button id="login-button" data-persona-verifier-url="{{ app.config.persona_api_endpoint }}">Login with Persona</button>
    

    Include the JS bundle:

    {{ encore_entry_script_tags('app') }}
    

    Ensure webpack.config.js includes:

    Encore
        .addEntry('app', './assets/js/persona-login.js')
        .enableReactPreset()
        .copyFiles({
            from: './assets/js/persona-login.js',
            to: 'build/[name].js'
        });
    

Implementation Patterns

Workflow: User Authentication

  1. Frontend Flow

    • Trigger Persona login via JS:
      // assets/js/persona-login.js
      document.getElementById('login-button').addEventListener('click', () => {
          navigator.id.request({
              onlogin: (assertion) => {
                  fetch('/login/persona', {
                      method: 'POST',
                      body: JSON.stringify({ assertion }),
                      headers: { 'Content-Type': 'application/json' }
                  })
                  .then(response => response.json())
                  .then(data => {
                      if (data.authenticated) window.location.reload();
                  });
              }
          });
      });
      
  2. Backend Handling Create a controller (src/Controller/PersonaAuthController.php):

    use ChampsLibres\PersonaUserBundle\PersonaAuthenticator;
    
    class PersonaAuthController extends AbstractController {
        public function login(PersonaAuthenticator $authenticator, Request $request) {
            $assertion = json_decode($request->getContent(), true)['assertion'];
            $user = $authenticator->authenticate($assertion);
            $this->loginManager->authenticateUser($user);
            return new JsonResponse(['authenticated' => true]);
        }
    }
    
  3. User Creation Extend the default user provider (src/Security/UserProvider/PersonaUserProvider.php):

    use ChampsLibres\PersonaUserBundle\Security\User\PersonaUser;
    
    class PersonaUserProvider extends AbstractPersonaUserProvider {
        public function loadUserByPersonaAssertion($assertion) {
            $email = $this->extractEmailFromAssertion($assertion);
            $user = $this->userRepository->findOneBy(['email' => $email]);
    
            if (!$user) {
                $user = new PersonaUser($email, $this->passwordEncoder);
                $user->setRoles(['ROLE_USER']);
                $this->userRepository->save($user, true);
            }
            return $user;
        }
    }
    

Integration Tips

  • Symfony Security Configure security.yaml:

    firewalls:
        main:
            pattern: ^/
            persona_login:
                provider: app.user_provider.persona
    providers:
        app.user_provider.persona:
            id: app.user_provider.persona
    
  • User Entity Extend PersonaUser in your User entity:

    use ChampsLibres\PersonaUserBundle\Security\User\PersonaUser as BasePersonaUser;
    
    class User extends BasePersonaUser {
        // Custom fields/methods
    }
    
  • Event Listeners Subscribe to PersonaUserEvents for post-login actions:

    use ChampsLibres\PersonaUserBundle\Event\PersonaUserEvent;
    
    class PostLoginHandler {
        public function onPersonaLogin(PersonaUserEvent $event) {
            // e.g., log activity, update last_login_at
        }
    }
    

    Register in services.yaml:

    services:
        App\EventListener\PostLoginHandler:
            tags:
                - { name: kernel.event_listener, event: persona.user.post_login, method: onPersonaLogin }
    

Gotchas and Tips

Pitfalls

  1. CORS Issues

    • Persona’s API requires CORS headers. Ensure your frontend and backend domains are whitelisted in the Persona verifier config.
    • Fix: Configure persona_api_endpoint to include CORS headers or use a proxy.
  2. Assertion Validation

    • The bundle validates assertions server-side, but malformed assertions can cause silent failures.
    • Debug: Enable debug mode and check monolog for PersonaAssertionException.
  3. User Deduplication

    • If multiple users share the same email (e.g., via different Persona accounts), the bundle will overwrite the first user.
    • Solution: Add a unique constraint (e.g., email + persona_assertion_hash) or implement a merge strategy.
  4. Session Management

    • Persona assertions are single-use. Reusing an assertion will fail.
    • Tip: Store the assertion in the session temporarily if multi-step auth is needed.

Debugging

  • Log Assertions Enable debug logging in config/packages/monolog.yaml:

    handlers:
        main:
            type: stream
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
            channels: ["!event"]
    

    Check logs for PersonaAssertion payloads during failed logins.

  • Test Locally Use the Persona test server for development:

    champs_libres_persona_user:
        persona_api_endpoint: 'https://verifier.login.testmk2.mozilla.org/verify'
    

Extension Points

  1. Custom Assertion Handling Override PersonaAuthenticator to add logic:

    class CustomPersonaAuthenticator extends PersonaAuthenticator {
        protected function validateAssertion($assertion) {
            // Add custom validation (e.g., check email domain)
            return parent::validateAssertion($assertion);
        }
    }
    

    Register in services.yaml:

    services:
        ChampsLibres\PersonaUserBundle\PersonaAuthenticator:
            class: App\Security\CustomPersonaAuthenticator
    
  2. Post-Login Redirects Extend the PersonaUserEvent listener to redirect users:

    public function onPersonaLogin(PersonaUserEvent $event) {
        if ($event->getUser()->getEmail() === '[email protected]') {
            $event->setResponse(new RedirectResponse('/admin'));
        }
    }
    
  3. Multi-Factor Authentication (MFA) Combine with Symfony’s Security component to add MFA post-Persona login:

    use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
    
    class PersonaMFASuccessHandler implements AuthenticationSuccessHandlerInterface {
        public function onAuthenticationSuccess(Request $request, TokenInterface $token) {
            if (!$this->isMFAEnabled($token)) {
                return new RedirectResponse('/mfa/setup');
            }
            return new RedirectResponse('/dashboard');
        }
    }
    

Configuration Quirks

  • User Class Ensure your user_class implements PersonaUserInterface or extends PersonaUser. Missing methods (e.g., getPersonaAssertion()) will throw BadMethodCallException.

  • API Endpoint The persona_api_endpoint must support POST requests with application/json content type. Avoid GET endpoints or custom formats.

  • Rate Limiting Mozilla’s Persona API has rate limits. For high-traffic sites, implement client-side rate limiting or use a queue (e.g., Symfony Messenger) for assertion processing.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log