Create a Custom Authenticator

Last modified by Thomas Mortagne on 2023/04/28

It is possible to plug to any existing authentication mechanism by providing a bridge for it.

Implement the authenticator

To implement a custom authentication the minimum is to implement the XWikiAuthService interface. It's recommended to extend the XWikiAuthServiceImpl class which is the default implementation(this is very useful if you want to reuse the standard login form, for example).

XWiki 13.3+ Your custom authentication should trigger a UserAuthenticatedEvent when it implements itself checkAuth(XWikiContext context) (if your authenticator is reusing the standard login form this part is handled by XWiki). You can find implementation examples in xwiki-platform-oldcore as MyFormAuthenticator and MyBasicAuthenticator. Note that a UserAuthenticatedEvent should be created with a UserReference.

XWiki 15.3+ 

If you want your custom authenticator to be listed and selectable in the administration at runtime, you will need to expose it as a component with role `org.xwiki.security.authservice.XWikiAuthServiceComponent` and a unique hint. For existing authenticators adding this feature, a org.xwiki.security.authservice.AbstractXWikiAuthServiceWrapper helper is provided which simply wrap your existing old style authenticator as a component.

Note that you don't need to upgrade your XWiki dependency to 15.3 in your authenticator for this, you can depend on org.xwiki.contrib:authservice-backport:1.1.1 (which supports XWiki 8.4+) instead.

Use your authenticator

Edit the WEB-INF/xwiki.cfg file and add a xwiki.authentication.authclass property pointing to your class. For example:

xwiki.authentication.authclass = com.acme.MyCustomAuthenticationService

Example

Here is an example code for a custom authenticator:

package com.acme;

public class MyCustomAuthenticationService extends XWikiAuthServiceImpl
{
   // We cannot use "real" component injection here because authenticators are not components currently
   // But it's recommended to put most of your authenticator's actual code in a component (or several components) and use this component,
   // it will make it a lot easier to reuse various XWiki tools and APIs
   private MyCustomAuthentor authenticator = Utils.getComponent(MyCustomAuthenticator.class);

   // If you don't plan to reuse the standard XWiki login you should implement this method which is usually in charge of gathering the user credentials
   // or other means of indicating what is the current user (HTTP headers, etc.)
   @Override
   public XWikiUser checkAuth(XWikiContext context)
   {
       // Call the actual authenticator
       return this.authenticator.checkAuth(context);
   }

   // This is the method which will be called if you reuse the standard means of gathering the credentials (login page, BASIC auth)
   // What's left on your side is to validate the credential and create/update the XWiki user profile (and eventually synchronize other user-related info like the groups, etc.)
   @Override
   public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException
   {
       // Call the actual authenticator
       return this.authenticator.checkAuth(context);
   }
}

@Component(roles = MyCustomAuthenticator.class)
@Singleton
public MyCustomAuthenticator
{
   @Inject
   private ObservationManager observation;

   public XWikiUser checkAuth(XWikiContext context)
   {
       // You authenticate a user somehow
       ...

       // Since 13.3, if this is a new authentication (the user was not already authenticated in this session) you should send a notification about that
       if (newAuth) {
         // You have to retrieve its UserReference
         // You should be able to use a UserReferenceResolver if needed
         UserReference userReference = ...;

         // Then, trigger a UserAuthenticatedEvent by passing previously retrieved user reference to UserAuthenticatedEvent constructor
         this.observationManager.notify(new UserAuthenticatedEvent(userReference), null);
       }
   }

 public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException
 {
   ...
 }
}

@Component
@Singleton
@Named(MyCustomAuthService.ID)
public class MyCustomAuthService extends AbstractXWikiAuthServiceWrapper implements XWikiAuthServiceComponent
{
   /**
     * The identifier of the authenticator.
     */

   public static final String ID = "mycustom";

   /**
     * Wrap a {@link MyCustomAuthenticationService} instance.
     */

   public LDAPAuthService()
   {
       super(new MyCustomAuthenticationService());
   }

   @Override
   public String getId()
   {
       return ID;
   }
}

security.authservice.mycustom.name=My custom authenticator
security.authservice.mycustom.description=An example to show how to implement a custom authenticator

You can find various authenticators examples in extensions or sandbox.

Here's a tutorial on implementing a custom authentication class for authenticating against Oracle's SSO.

Tags:
   

Get Connected