Consentor Plugin#

Plugin Descriptor interface:

ConsentorPluginDescriptor

A Consentor Plugin implements logic required to obtain authorization consent from users.

See User Consent for more information.

Reading the OAuth client#

Declare RequestingOAuthClient on the plugin’s configuration interface to obtain the client that triggered the consent flow. This gives the plugin the client’s metadata, and, through its scope map and claim set, the resolved scope and claim configuration needed to render a consent screen.

public interface MyConsentorConfiguration extends Configuration
{
    RequestingOAuthClient getRequestingOAuthClient();
}

The consent attributes carry scope and claim names. Resolve those names against the client to get descriptions and the rest of the configuration:

public final class MyConsentorHandler implements ConsentorCompletionRequestHandler<Request>
{
    private final IntermediateConsentState _consentState;
    private final OAuthClient _client;

    public MyConsentorHandler(MyConsentorConfiguration configuration, IntermediateConsentState consentState)
    {
        _consentState = consentState;
        _client = configuration.getRequestingOAuthClient().getClient();
    }

    @Override
    public Request preProcess(Request request, Response response)
    {
        ConsentAttributes consent = _consentState.getConsentAttributes();

        Set<ScopeValue> scopes = _client.getScopeMap().getAllMatching(consent.getScopeNames());

        Set<ClaimConfig> claims = _client.getClaimSet()
                .filtered(Set.copyOf(consent.getClaimNames()))
                .getAll()
                .stream()
                .filter(claim -> claim.isConsentable() && !claim.isSystemClaim())
                .collect(Collectors.toSet());

        response.setResponseModel(ResponseModel.templateResponseModel(Map.of(
                        "clientName", _client.getName().orElse(_client.getId()),
                        "clientDescription", _client.getDescription().orElse(""),
                        "logoUri", _client.getLogoUri().filter(MyConsentorHandler::isHttpUri)
                                .map(URI::toString).orElse(""),
                        "scopes", scopes,
                        "claims", claims),
                "index"), Response.ResponseModelScope.ANY);

        return request;
    }

    private static boolean isHttpUri(URI uri)
    {
        return "https".equalsIgnoreCase(uri.getScheme()) || "http".equalsIgnoreCase(uri.getScheme());
    }
}

Templates do not escape values automatically, and a dynamically registered client controls its own name, description, and URIs.

Encode text with $_tr.encodeHtml($clientName) and attribute values with $_tr.encodeHtmlAttribute($clientName), and place an encoded attribute value in a double-quoted attribute.

Restrict URI-valued metadata to the http and https schemes before it reaches the template, as the example above does for the logo URI.

Each ScopeValue exposes the scope’s description, the claims it entitles, and whether it is required. For a prefix scope, it also exposes the suffix. Each ClaimConfig exposes the claim’s description, whether it is required, and whether it is a system claim.

To show every scope the client may request, rather than only those in the current request, read _client.getScopeMap().getScopeValues() instead. This is useful on screens that present the full set of permissions an application can ask for.

Scopes that entitle claims#

Only scopes that entitle no claims and that are consentable appear in ConsentAttributes.getScopeNames(). A scope that entitles claims is presented through those claims instead, so its name is absent from getScopeNames() while its claims appear in getClaimNames().

A consent screen that renders only getScopeNames() therefore looks empty for a request such as openid profile email, because profile and email entitle claims. Render both collections, as in the example above. The built-in consent screen behaves the same way.

Was this helpful?