Getting Started with the Plugin SDK

Getting Started with the Plugin SDK

On this page

OAuth secured applications can be developed in your preferred technologies, and they then interact with the identity and access management (IAM) system using HTTPS requests. You only need to deploy the IAM system, then point your apps to its endpoints. For the Curity Identity Server, deployment is typically managed by running Docker containers:

docker run -it -e PASSWORD=<ADMIN_PASSWORD> -p 6749:6749 -p 8443:8443 curity.azurecr.io/curity/idsvr

You should never need to build the code for the IAM system or be an expert in its underlying technology. OAuth is a framework though, and at times you need to take closer control over behavior. Extensibility may be needed in some areas, so that you avoid being blocked when implementing security solutions.

AreaDescription
Look and FeelControl over the login branding, appearance and layout
AuthenticationCustomizing authentication or implementing a new authentication method
ActionsUsing and linking user attributes during or after authentication
User DataStoring custom user attributes in the authorization server
Data SourcesInteracting with custom data sources and credential stores
Token IssuingControlling the format and algorithms used during token issuing
API ClaimsReturning custom information to APIs in JWT access tokens
OperationalBeing notified of runtime events that occur in the authorization server

The cleanest way to manage this type of customization is to do so within the IAM system, rather than coding complex workarounds at the application level.

Managing Extensibility

There are three main levels for customizing the Curity Identity Server, from enhancing the look and feel, to writing scripts, to implementing Java plugins.

Look and Feel

Companies often want an early focus on login branding, to customize the Look and Feel. In the Curity Identity Server this can be done in a basic way using the Admin UI, or more detailed HTML customizations can be made via the UI Builder. This typically involves only changes to CSS assets.

Scripting

Later on, to control authentication and token issuing, Scripting is used. This requires code, but it is implemented with only simple Javascript procedures. The scripted logic can also interact with infrastructure, such as sending custom HTTP requests or connecting to data sources.

Plugins

Scripting has some limitations though, especially for more complex tasks. When you want to take closer control over behavior, such as building a completely custom authentication method, the Plugin SDK is the preferred option. This requires you to write Java or Kotlin code, using SDK objects and interfaces.

Development Setup

Many developers are not Java coders, and instead build their user facing apps and APIs in other technology stacks, such as Node.js, Python or C#. There may be concerns about how to manage setup and deployment aspects, so this tutorial shows how to quickly overcome these challenges, and provides some useful links. The only thing left to do in order to implement a plugin will be to write the Java or Kotlin code, which any skilled developer will soon learn.

Plugin Resources

When getting started with plugins, first watch these two introductory videos, then browse the developer documentation:

Plugin Types

To understand more about plugin coding, see the Curity code examples, which cover the following plugin categories. There are many links to GitHub repositories, from which you can borrow coding techniques for your own plugins:

Install Prerequisites

Next ensure that these tools are installed:

The Curity Identity Server stays current with the latest long term support (LTS) version of Java, and it is recommended to use an up to date version such as JDK 17, so that developers can write plugins with productive modern code. See the Java Version Details for further information.

Create a Project

A common type of plugin is a new authenticator, since various third party providers offer an authentication API that is not quite OpenID Connect compliant. To get started building one, run the following command, which uses Maven Archetypes to create Java project files for a custom authenticator:

mvn -B archetype:generate \
      -DarchetypeArtifactId=identityserver.plugins.archetypes.authenticator \
      -DarchetypeGroupId=io.curity \
      -DarchetypeVersion=3.0.0  \
      -DartifactId=example-authenticator \
      -DgroupId=com.mycompany.plugins.exampleauthenticator \
      -DpluginName=Example \
      -Dversion=1.0.0-SNAPSHOT

If you prefer to develop your plugin in Kotlin, run the following command instead:

mvn -B archetype:generate \
      -DarchetypeArtifactId=identityserver.plugins.archetypes.kotlin-authenticator \
      -DarchetypeGroupId=io.curity \
      -DarchetypeVersion=3.0.0  \
      -DartifactId=example-authenticator \
      -DgroupId=com.mycompany.plugins.exampleauthenticator \
      -DpluginName=Example \
      -Dversion=1.0.0-SNAPSHOT

Getting Started Coding

If you open the generated folder in an IDE, you will see the core Java files, and the main development job is to implement a handler class:

Project Files

The archetypes provide a skeleton implementation that is injected with its configuration and also some example SDK objects.

public final class ExampleAuthenticatorRequestHandler implements AuthenticatorRequestHandler<RequestModel>
{
    private static final Logger _logger = LoggerFactory.getLogger(ExampleAuthenticatorRequestHandler.class);

    private final ExampleAuthenticatorPluginConfig _config;
    private final AuthenticatorInformationProvider _authInfoProvider;
    private final ExceptionFactory _exceptionFactory;

    public ExampleAuthenticatorRequestHandler(ExampleAuthenticatorPluginConfig config,
                                              ExceptionFactory exceptionFactory,
                                              AuthenticatorInformationProvider authInfoProvider)
    {
        _config = config;
        _exceptionFactory = exceptionFactory;
        _authInfoProvider = authInfoProvider;
    }

    @Override
    public Optional<AuthenticationResult> get(RequestModel requestModel, Response response)
    {
        _logger.info("GET request received for authentication authentication");

        return Optional.empty();
    }

Example Authenticator

This tutorial will implement a stub authenticator that signs you in with a fixed mock user. This is not a useful real world scenario of course, but provides a starting point for your own logic. It also highlights that there are no limits to the type of authentication you can implement with plugins.

Input Configuration

The plugin will first require the following field to be populated in the Admin UI:

public interface ExampleAuthenticatorPluginConfig extends Configuration
{
    @DefaultString("mockuser")
    @Description("A fixed string to represent a mock user")
    String getMockUser();
}

Implement Plugin Code

To implement a real plugin, the most common approach is to start by finding something similar in the Curity GitHub repositories. Searching for topic:authenticator would find some plugins that implement custom authentication:

Repo Search

For this tutorial, simply replace the request handler class with the following Java code:

public final class ExampleAuthenticatorRequestHandler implements AuthenticatorRequestHandler<RequestModel>
{
    private static final Logger _logger = LoggerFactory.getLogger(ExampleAuthenticatorRequestHandler.class);
    private final ExampleAuthenticatorPluginConfig _config;

    public ExampleAuthenticatorRequestHandler(ExampleAuthenticatorPluginConfig config)
    {
        _config = config;
    }

    @Override
    public RequestModel preProcess(Request request, Response response)
    {
        response.setResponseModel(templateResponseModel(
                emptyMap(),
                "authenticate/get"), NOT_FAILURE);

        return new RequestModel(request);
    }

    @Override
    public Optional<AuthenticationResult> get(RequestModel requestModel, Response response)
    {
        return Optional.empty();
    }

    @Override
    public Optional<AuthenticationResult> post(RequestModel requestModel, Response response)
    {
        var user = _config.getMockUser();
        _logger.debug("Successfully authenticated for user: {}", user);
        return Optional.of(new AuthenticationResult(user));
    }
}

A trivial HTML template will also be used, so create a directory tree and a file called get.vm, at this path, under the resources folder:

/templates/authenticator/example/authenticate/get.vm

Then paste in the following template, which contains a simple custom HTML body. This also uses styles shipped with the Curity Identity Server. For a real plugin it is recommended to use localizable message files instead of the hard coded text used here:

#define ($_body)
<h2>Example Authenticator</h2>
<form method="post">
    <button type="submit" class="button button-fullwidth button-primary">Authenticate</button>
</form>
#end

#parse("layouts/default")

Add a Library Dependency

The example plugin is trivial, but for a more real world one you will often need to use additional Java libraries. Your plugin can use Server Provided Dependencies but others must be deployed. For this tutorial, edit the pom.xml file and add one, such as this JSON serialization library:

<dependencies>
    <dependency>
        <groupId>se.curity.identityserver</groupId>
        <artifactId>identityserver.sdk</artifactId>
        <version>${project.curityVersion}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${project.slf4jVersion}</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>${project.gsonVersion}</version>
    </dependency>
</dependencies>

Deploy and Test the Plugin

Before adding much plugin code, you will want to get an end-to-end setup working, so that you can build, deploy, and interactively test the plugin.

Build to JAR Files

In order to compile code to JAR files and also deal with third party dependencies, you will need to add some plugins to the build setup. If you are using third party libraries, include the maven dependency plugin:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.10.1</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <phase>process-sources</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <includeScope>compile</includeScope>
                        <outputDirectory>${project.build.directory}</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

You can then run the following command line to build a JAR file containing the plugin's compiled code and resources:

mvn package

For the example authenticator, the following files are built to the target folder and will need to be deployed:

example-authenticator-1.0.0-SNAPSHOT.jar
gson-2.9.0.jar

Deploy the Plugin

The JAR files must then be deployed with instances of the Curity Identity Server, to a subfolder of usr/share/plugins. The following docker compose example shows the use of paths under the plugins root folder:

version: '3.8'
services:

  curity-idsvr:
    image: curity.azurecr.io/curity/idsvr:latest
    ports:
     - 6749:6749
     - 8443:8443
    volumes:
     - ./example-authenticator-1.0.0-SNAPSHOT.jar:/opt/idsvr/usr/share/plugins/example-authenticator/example-authenticator-1.0.0-SNAPSHOT.jar
     - ./gson-2.9.0.jar:/opt/idsvr/usr/share/plugins/example-authenticator/gson-2.9.0.jar

Create a Plugin Instance

Once the Curity Identity Server and plugin are deployed, you can create an instance of the plugin using the Admin UI, and enter its required input values. The example authenticator is shown below, and it can be used by one or more OAuth client applications.

Deployed Plugin

Developer Testing

You will need to register a test client that uses the custom authenticator, as described in the Getting Started guides. Using OAuth Tools can be a productive way to test your plugin against a locally deployed instance of the Curity Identity Server:

Testing Plugin

Running the Plugin

Once you have a test client, you can run a code flow, to perform a user login. This will present the minimal HTML template created earlier. Clicking Authenticate will then cause the post method of the example authenticator to be called.

Custom Authenticator

Authentication then completes, and control is returned to the test client, after which the application can redeem an authorization code for a set of tokens.

View Logs

To troubleshoot issues during development you will add log messages to the plugin, which are then included in the Curity Identity Server logs. Use the 'DEBUG' or INFO log levels during development. Ensure that any sensitive information, such as the username shown here, is then removed from logs.

local-curity-idsvr-1  | 2022-09-26T10:09:21:512+0000 DEBUG  GgKats8Z 325ba718 {req-132} com.mycompany.plugins.exampleauthenticator.authentication.ExampleAuthenticatorRequestHandler - Successfully authenticated for user: mockuser

Debugging

It is also possible to debug your plugin's code, while it is running in a Docker deployed instance of the Curity Identity Server. The Attach a Debugger tutorial explains how to do this.

Techniques

This section links to further information for techniques that could be applied to multiple use cases. Once understood, it will also be possible to apply them to your own scenarios.

Custom Authentication Redirects

Most commonly the Curity Identity Server connects to external identity providers using OpenID Connect or SAML. In some cases custom code is needed and these examples show how to initiate them and handle callbacks:

PluginDescription
Redirect ActionRedirect to a custom application, sending the subject claim and receiving back custom data
Slack AuthenticatorA custom authenticator that uses a third party authentication API

Custom User Input

The Username Password Authenticator has a branch that shows how to capture custom data during authentication. This is done by adding the field to the RequestModel for an existing screen.

Custom Templates

Both bespoke HTML forms and emails can be implemented, and this is done using velocity templates, to allow custom data to be processed on the server. The following examples show how to create custom templates.

PluginDescription
Username Password AuthenticatorContains multiple custom forms and also uses custom email templates
Send Email ActionShows how to do bespoke sending of emails, which can also use custom templates

The Username Password Authenticator shows how to use and validate view models using Java annotations. Validation can fail either because the annotations are not satisfied or because deeper checks fail. In these cases the form needs to present error messages and avoid losing the user's input.

Custom User Creation

The following plugins show how to make HTTP requests to external systems. In both cases a remote user is created after authentication, and this responsibility is best implemented as an authentication action.

PluginDescription
Kong Dev Portal User ProvisionerUses an HTTP client managed by the Curity Identity Server
Azure API Management User ProvisionerUses an HTTP client from the Azure SDK

Custom Claims

Claims issued in tokens can be customized with information from third party systems, by implementing a ClaimsProvider. The Salesforce Claims Provider shows how to make a more complex external connection that requires a JWT credential. This plugin also demonstrates the use of caching and Managed Objects.

Custom Data Sources

The Curity Identity Server can connect to many types of data source and provides interfaces for operating on data. The following examples can be used as a guide when integrating with custom data sources directly.

PluginDescription
Custom Credential ValidatorCustom validation of password credentials to support any cryptographic hash algorithm
MongoDB Data SourceShows how to implement various interfaces to connect to NoSQL data sources

Custom Auditing

Auditing can be output to a JDBC data source, though it is also possible to capture events programmatically. The Amazon SQL Event Listener provides an example of receiving audit data, then sending it to a queue. Various subscribers could then use the data.

Conclusion

The plugin SDK for the Curity Identity Server gives you powerful control over core behavior, and you do not need to be a Java expert to use it. A simplified programming model is provided, and this page explained the main steps, with links to further information. This will enable you to customize security related behavior during OAuth flows, in a technically simple and standard manner.