Application Plugin#

Plugin Descriptor interface:

ApplicationPluginDescriptor

Application plugins provide auxiliary functionality to the Curity Identity Server.

They are declared in the Application Profile . In addition to RequestHandler, application plugins can implement HttpRequestHandler to handle HTTP requests using HTTP methods (as defined in the HTTP standard) other than GET and POST.

The Curity Identity Server comes with a few built-in application plugins, as you can find in the Application Plugins section of the documentation.

Using an OAuth Client of the Linked OAuth Profile#

An application plugin can drive an OAuth authorization code flow against the OAuth profile linked to the application profile that hosts the plugin, using a client configured on that linked profile. The plugin does not need to redeclare endpoints, secrets or cryptographic material; it references the client by ID and the SDK takes care of building the authorization URL and exchanging the code for tokens.

To use it, declare a method that returns TokenServiceOAuthClient on the plugin’s Configuration interface, alongside an HttpClient for back-channel calls and the other SDK services the handlers below use:

import se.curity.identityserver.sdk.config.Configuration;
import se.curity.identityserver.sdk.config.annotation.DefaultService;
import se.curity.identityserver.sdk.service.ExceptionFactory;
import se.curity.identityserver.sdk.service.HttpClient;
import se.curity.identityserver.sdk.service.SessionManager;
import se.curity.identityserver.sdk.service.oauth.TokenServiceOAuthClient;

public interface MyPluginConfig extends Configuration
{
    TokenServiceOAuthClient getOAuthClient();

    @DefaultService
    HttpClient getHttpClient();

    SessionManager getSessionManager();

    ExceptionFactory getExceptionFactory();
}

The administrator configures the plugin by picking a client from the linked OAuth profile’s client list. At runtime the plugin uses the service to drive the flow.

The example below illustrates how to use the TokenServiceOAuthClient service from plugin request handlers. The startAuthorizationCodeFlow method returns an AuthorizationCodeFlow object that contains the authorization URL and other control data (state, verifier, redirect URI, response mode). The plugin is responsible for persisting this data until the callback is received. The example uses the session for persistence (AuthorizationCodeFlow extends MapAttributeValue, so it can be stored directly in a SessionManager); plugins are free to persist via cookie, database, or any other store.

private static final String FLOW_KEY = "oauth-flow";

public final class StartHandler implements RequestHandler<Request>
{
    private final TokenServiceOAuthClient _client;
    private final HttpClient _httpClient;
    private final SessionManager _session;

    public StartHandler(MyPluginConfig config)
    {
        _client = config.getOAuthClient();
        _httpClient = config.getHttpClient();
        _session = config.getSessionManager();
    }

    @Override
    public Object get(Request request, Response response)
    {
        AuthorizationCodeFlow flow = _client.startAuthorizationCodeFlow(_httpClient);
        _session.put(Attribute.of(FLOW_KEY, flow));
        // ... redirect the user-agent to flow.getAuthorizationUrl()
        return null;
    }
}

public final class CallbackHandler implements RequestHandler<Request>
{
    private final TokenServiceOAuthClient _client;
    private final HttpClient _httpClient;
    private final SessionManager _session;
    private final ExceptionFactory _exceptionFactory;

    public CallbackHandler(MyPluginConfig config)
    {
        _client = config.getOAuthClient();
        _httpClient = config.getHttpClient();
        _session = config.getSessionManager();
        _exceptionFactory = config.getExceptionFactory();
    }

    @Override
    public Object get(Request request, Response response)
    {
        Attribute stored = _session.remove(FLOW_KEY);
        if (stored == null)
        {
            throw _exceptionFactory.badRequestException(
                    ErrorCode.INVALID_INPUT, "No in-flight authorization code flow");
        }
        AuthorizationCodeFlow flow = AuthorizationCodeFlow.of(
                (MapAttributeValue) stored.getAttributeValue());

        Map<String, String> callbackParameters = /* read 'code', 'state', 'iss' from the request */;
        OAuthTokens tokens = _client.handleAuthorizationCodeFlowCallback(_httpClient, flow, callbackParameters);
        // tokens.accessToken(), tokens.idTokenClaims(), ...
        return null;
    }
}

PKCE is always used. The SDK validates state and (when present) iss on the callback automatically. The referenced client must have the authorization_code capability and be configured with a symmetric key for client authentication; both are checked the first time the service is used and surface as a configuration error otherwise. PAR and JARM are handled transparently when the referenced client requires them.

Extra authorization parameters can be passed via the startAuthorizationCodeFlow(Map, HttpClient)overload; they are appended to the authorization URL as-is. Whitelisting parameters from end-user input is the plugin author’s responsibility.

AuthorizationCodeFlow carries secret material — the PKCE verifier and the OAuth state. Persist it through an encrypted, short-lived store (such as SessionManager or an encrypted cookie). Do not log the flow object or its underlying attribute map.

Was this helpful?