richard-scott commented on issue #216:
URL: https://github.com/apache/bifromq/issues/216#issuecomment-3707256614

   Thank you for the helpful feedback! You were absolutely correct - the plugin 
JAR was missing the required PF4J metadata files.
   
   ## Root Cause Confirmed
   
   After checking the actual running configuration and verifying the plugin JAR 
contents, we confirmed:
   
   1. ✅ **Configuration was correct**: `authProviderFQN: 
"com.bifromq.plugin.authprovider.CustomAuthProvider"` was properly set in 
`standalone.yml`
   2. ❌ **Missing `META-INF/extensions.idx`**: This file was not present in the 
plugin JAR
   3. ❌ **Missing PF4J manifest entries**: The `META-INF/MANIFEST.MF` file was 
missing:
      - `Plugin-Class`
      - `Plugin-Id`
      - `Plugin-Version`
   
   ## Documentation Gap
   
   While implementing this fix, we noticed that these PF4J metadata 
requirements are **not explicitly documented** in the BifroMQ plugin 
documentation:
   
   - [Plugin Overview](https://bifromq.apache.org/docs/plugin/intro/)
   - [Auth Provider 
Documentation](https://bifromq.apache.org/docs/plugin/auth_provider/)
   - [Plugin Practice and 
Notice](https://bifromq.apache.org/docs/plugin/practice/)
   
   The documentation references PF4J but doesn't detail the required metadata 
files (`extensions.idx` and manifest entries) that must be present in plugin 
JARs. This information is documented in PF4J's own documentation 
(https://pf4j.org/doc/extensions.html), but developers creating BifroMQ plugins 
may not realize they need to consult PF4J documentation for packaging 
requirements.
   
   **Suggestion**: It would be helpful if the BifroMQ plugin documentation 
included a section on plugin packaging requirements, specifically mentioning:
   - The need for `META-INF/extensions.idx` file (or how to generate it)
   - Required manifest entries (`Plugin-Class`, `Plugin-Id`, `Plugin-Version`)
   - The need for a PF4J Plugin wrapper class when using FQN configuration
   
   This would help prevent similar issues for other developers.
   
   ## Solution Implemented
   
   We've fixed the issue by adding the required PF4J metadata:
   
   ### 1. Created PF4J Plugin Wrapper Class
   
   ```java
   package com.bifromq.plugin.authprovider;
   
   import org.pf4j.Plugin;
   import org.pf4j.PluginWrapper;
   
   public class AuthProviderPlugin extends Plugin {
       public AuthProviderPlugin(PluginWrapper wrapper) {
           super(wrapper);
       }
   }
   ```
   
   ### 2. Updated Maven POM Configuration
   
   Added PF4J dependency and configured Maven plugins. **Important**: We also 
added `maven-shade-plugin` to bundle all dependencies (including BCrypt 
library) into the plugin JAR, as plugins need to be self-contained:
   
   ```xml
   <dependencies>
       <!-- BifroMQ Plugin API -->
       <dependency>
           <groupId>org.apache.bifromq</groupId>
           <artifactId>bifromq-plugin-auth-provider</artifactId>
           <version>4.0.0-incubating</version>
       </dependency>
   
       <!-- PF4J for plugin framework -->
       <dependency>
           <groupId>org.pf4j</groupId>
           <artifactId>pf4j</artifactId>
           <version>3.9.0</version>
       </dependency>
   
       <!-- BCrypt library - Using at.favre.lib:bcrypt which supports $2a$, 
$2b$, and $2y$ hash formats -->
       <dependency>
           <groupId>at.favre.lib</groupId>
           <artifactId>bcrypt</artifactId>
           <version>0.9.0</version>
       </dependency>
   </dependencies>
   
   <build>
       <plugins>
           <!-- Maven Compiler Plugin - Configure PF4J annotation processor -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.11.0</version>
               <configuration>
                   <source>17</source>
                   <target>17</target>
                   <annotationProcessorPaths>
                       <path>
                           <groupId>org.pf4j</groupId>
                           <artifactId>pf4j</artifactId>
                           <version>3.9.0</version>
                       </path>
                   </annotationProcessorPaths>
               </configuration>
           </plugin>
   
           <!-- Maven JAR Plugin - Add PF4J manifest entries -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-jar-plugin</artifactId>
               <version>3.3.0</version>
               <configuration>
                   <archive>
                       <manifestEntries>
                           
<Plugin-Class>com.bifromq.plugin.authprovider.AuthProviderPlugin</Plugin-Class>
                           <Plugin-Id>auth-provider</Plugin-Id>
                           <Plugin-Version>${project.version}</Plugin-Version>
                       </manifestEntries>
                   </archive>
               </configuration>
           </plugin>
   
           <!-- Maven Shade Plugin - Bundle all dependencies into plugin JAR -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-shade-plugin</artifactId>
               <version>3.5.1</version>
               <executions>
                   <execution>
                       <phase>package</phase>
                       <goals>
                           <goal>shade</goal>
                       </goals>
                       <configuration>
                           <filters>
                               <filter>
                                   <artifact>*:*</artifact>
                                   <excludes>
                                       <exclude>META-INF/*.SF</exclude>
                                       <exclude>META-INF/*.DSA</exclude>
                                       <exclude>META-INF/*.RSA</exclude>
                                   </excludes>
                               </filter>
                           </filters>
                           <transformers>
                               <transformer 
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                   <manifestEntries>
                                       
<Plugin-Class>com.bifromq.plugin.authprovider.AuthProviderPlugin</Plugin-Class>
                                       <Plugin-Id>auth-provider</Plugin-Id>
                                       
<Plugin-Version>${project.version}</Plugin-Version>
                                   </manifestEntries>
                               </transformer>
                               <transformer 
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                           </transformers>
                       </configuration>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>
   ```
   
   ### 3. Created `META-INF/extensions.idx` File
   
   Created `src/main/resources/META-INF/extensions.idx` containing:
   
   ```
   com.bifromq.plugin.authprovider.CustomAuthProvider
   ```
   
   ### 4. Additional Implementation Fixes
   
   After fixing the PF4J metadata, we encountered and resolved two additional 
issues:
   
   **a) BCrypt Library Compatibility**: The `org.mindrot:jbcrypt` library 
(version 0.4) doesn't fully support `$2b$` salt revisions commonly used in 
modern password hashes. We switched to `at.favre.lib:bcrypt` (version 0.9.0) 
which provides full support for `$2a$`, `$2b$`, and `$2y$` hash formats.
   
   **b) MQTT3AuthResult Builder Usage**: The `MQTT3AuthResult` protobuf uses a 
nested `Ok` message structure. The correct way to build an authentication 
result is:
   
   ```java
   // Build MQTT3AuthResult using protobuf builder
   // MQTT3AuthResult uses a nested Ok message that contains tenantId and userId
   MQTT3AuthResult.Ok okMessage = MQTT3AuthResult.Ok.newBuilder()
       .setTenantId(result.tenantId())
       .setUserId(result.userId())
       .build();
   
   MQTT3AuthResult authResult = MQTT3AuthResult.newBuilder()
       .setOk(okMessage)
       .build();
   ```
   
   **c) Dependency Bundling**: Plugins must bundle all their dependencies (like 
BCrypt) into the JAR using `maven-shade-plugin`, otherwise you'll get 
`NoClassDefFoundError` at runtime. The shade plugin creates a "fat JAR" that 
includes all required classes.
   
   ## Verification & Testing
   
   We've tested the fix and confirmed it works:
   
   ✅ **JAR Verification**:
   - `META-INF/extensions.idx` exists and contains: 
`com.bifromq.plugin.authprovider.CustomAuthProvider`
   - `META-INF/MANIFEST.MF` contains all required entries:
     - `Plugin-Class: com.bifromq.plugin.authprovider.AuthProviderPlugin`
     - `Plugin-Id: auth-provider`
     - `Plugin-Version: 1.0.0`
   - Plugin wrapper class (`AuthProviderPlugin.class`) is present in JAR
   
   ✅ **Runtime Verification**:
   - Plugin loads successfully (confirmed via startup logs)
   - Configuration is correct (`authProviderFQN: 
"com.bifromq.plugin.authprovider.CustomAuthProvider"`)
   - Authentication works correctly (tested with MQTT clients)
   - BCrypt password verification works with `$2b$` hash format
   - All plugin dependencies are bundled and available at runtime
   
   ✅ **End-to-End Testing**:
   - All test suites pass (100% success rate)
   - MQTT client connections authenticate successfully
   - Password verification works with BCrypt hashes
   - Plugin is fully functional
   
   ## Status
   
   ✅ **Issue resolved** - plugin now loads correctly with proper PF4J metadata 
files and all dependencies bundled. The plugin is fully functional and tested.
   
   ## Additional Recommendations
   
   For developers creating BifroMQ plugins, we recommend:
   
   1. **Always use `maven-shade-plugin`** to bundle dependencies into plugin 
JARs
   2. **Use modern BCrypt libraries** (like `at.favre.lib:bcrypt`) if password 
hashing is needed
   3. **Test plugin loading** by checking startup logs and verifying the plugin 
is actually being used (not falling back to Demo plugins)
   4. **Verify JAR contents** using `jar tf plugin.jar | grep META-INF` to 
ensure all metadata files are present
   
   Thank you again for pointing us in the right direction!
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to