Skip to content

Conversation

@renatoathaydes
Copy link
Collaborator

As @pmhsfelix mentioned, we were not doing enough validations about the OAuth client performing the HAAPI flow.

This PR locks down which HAAPI client is allowed to be used, and forbids completely non-HAAPI clients, by:

  • assert that a valid, confidential OAuth client initiated the authorization flow.
  • only accept requests containing the HAAPI media-type (since the Authentication Service ensures that this will trigger the HAAPI handlers, which ensure the OAuth client is doing dPoP and has the haapi capability.
  • allow configuring allowed-oauth-client-ids explicitly in case the customer desires to lock it down even more. It's reasonable in some cases only a particular server will perform authentication on behalf of end users.

Must be a HAAPI flow.
Must be a confidential client.
Optionally must be one of the allowed clients.
@renatoathaydes renatoathaydes force-pushed the lock-down-access-to-authenticator branch from 431ab37 to 41f7608 Compare November 5, 2025 12:24
}

private fun enforceHaapiFlow(request: Request, response: Response) {
if (request.acceptableMediaTypes != MediaType.HAAPI_JSON.toString()) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we expect any HAAPI client to send any "complex" Accept header, so I guess it's ok to enforce the header has the exact value we want.

failAuthentication(
response,
clientNotAllowed,
detailedMessage = "OAuth client is not allowed, allowed clients are: $allowedClientsText"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will allowedClientsText only be visible if detailed message are disabled in the profile? We should not reveal client IDs for other clients in prod.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly. That's why I put an explict detailedMessage parameter there.
Also, this is tested in the main repo.

val subjectClaimName: String

@get:Description("The IDs of the allowed OAuth clients. If empty, any confidential OAuth client will be allowed.")
val allowedOauthClientIds: List<@SizeConstraint(min = 1, max = 128) String>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This @SizeConstraint applies to the String and not to the List, right? I always have doubts on this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, Java allows specifying annotations on values of container types :) . Quite handy and we handle this properly in the YANG generator.

val keyVerification: AsymmetricSignatureVerificationCryptoStore

/**
* This authenticator will allow only confidential clients.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detail: perhaps

Service to obtain the requesting client.

That client must be in [allowedOauthClientIds], if this list is not empty.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I should've mentioned it. Adding it.

@renatoathaydes renatoathaydes force-pushed the lock-down-access-to-authenticator branch from eba1c75 to 0af0500 Compare November 5, 2025 15:54
@renatoathaydes renatoathaydes force-pushed the lock-down-access-to-authenticator branch from 43d1db3 to 63e79a0 Compare November 5, 2025 17:17
A custom Authenticator plugin for the Curity Identity Server.

This plugin allows users to authenticate using HAAPI by first obtaining an access token via other means.
.. warning::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use a separate header for this:

Warning
~~~~~~

This plugin...

You can use different level of header if you don't want it to be H2.


This plugin allows users to authenticate using HAAPI by first obtaining an access token via other means.
.. warning::
This plugin cannot be used by users to authenticate directly from a browser.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

You can use this plugin with the Hypermedia Authentication API (`HAAPI`_). If a user tries to run an authentication flow with this authenticator from a browser, they will see an error.

Only confidential clients with the HAAPI capability are able to use this authenticator in an authentication flow.

* ``key-verification/id`` - ID of an existing token signature verification key.

.. note::
Even if an OAuth client is allowed by the ``allowed-oauth-client-ids`` setting, it will NOT be allowed to perform authorization
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rewrite slightly:

OAuth clients added to the ``allowed-oauth-client-ids`` list must be confidential clients. Public clients will be rejected when trying to use this authenticator, even if they are on the list. This is to ensure that only a limited set of OAuth clients, ones that the authorization server trusts, will have the power to obtain sensitive tokens on behalf of end users.

This limitation applies only to the client that runs an authentication flow with this authenticator. The access token used as the input to this authenticator can be obtained by any OAuth client, even a public one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants