Skip to content

feat(jans-config-api): FIDO2 metrics endpoints #13448

Open
pujavs wants to merge 45 commits intomainfrom
jans-config-api-13226
Open

feat(jans-config-api): FIDO2 metrics endpoints #13448
pujavs wants to merge 45 commits intomainfrom
jans-config-api-13226

Conversation

@pujavs
Copy link
Contributor

@pujavs pujavs commented Mar 11, 2026

Prepare


Description

New endpoints to access FIDO2 metrics endpoints

Target issue

closes #13226

Implementation Details

FIDO2 plugin modified to add new metrics endpoints

Test and Document the changes

  • Static code analysis has been run locally and issues have been fixed
  • Relevant unit and integration tests have been added/updated
  • Relevant documentation has been updated if any (i.e. user guides, installation and configuration guides, technical design docs etc)

Please check the below before submitting your PR. The PR will not be merged if there are no commits that start with docs: to indicate documentation changes or if the below checklist is not selected.

  • I confirm that there is no impact on the docs due to the code changes in this PR.

Summary by CodeRabbit

  • New Features

    • Added FIDO2 metrics API (metrics, aggregations, analytics, health, config), example payloads, expanded OAuth scopes, and related client/utilities.
  • New APIs / Schema

    • Standardized permission booleans across API schemas and added explicit accessToken flag.
  • Improvements

    • Added client factory, constants, JSON utilities, and default search regex; added logger toggle config.
  • Tests

    • Added integration tests for FIDO2 metrics endpoints
  • Chores

    • Removed deprecated session revocation functionality

Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (6)
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (6)

104-133: ⚠️ Potential issue | 🟠 Major

Don't rewrite validation and upstream HTTP errors to 500.

validateDate() and the service call can already raise HTTP errors, but this catch-all converts everything into throwInternalServerException(ex). That masks bad-request validation failures and propagated 4xx/5xx responses here, and the same catch pattern repeats across the rest of the resource.

In JAX-RS, if a resource catches a `WebApplicationException` and converts it into a generic 500 response, does that replace the original HTTP status code returned to the client?
Suggested fix
-        } catch (Exception ex) {
+        } catch (WebApplicationException ex) {
+            throw ex;
+        } catch (Exception ex) {
             logger.error(ERR_MSG, ex);
             throwInternalServerException(ex);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 104 - 133, The current catch-all in Fido2MetricsResource around
validateDate/parseDate/getFido2MetricsEntries swallows and converts any
WebApplicationException (and other HTTP errors) into a 500 via
throwInternalServerException; change the catch block to rethrow
WebApplicationException instances unchanged (e.g., if (ex instanceof
WebApplicationException) throw (WebApplicationException) ex;), and only log+wrap
non-WebApplicationException errors with throwInternalServerException; reference
validateDate, parseDate, fido2MetricsService.getFido2MetricsEntries and
throwInternalServerException to locate the code to update.

405-410: ⚠️ Potential issue | 🟡 Minor

Either implement paging here or drop it from the contract.

limit and startIndex are accepted, documented, and logged, but the handler still calls the service without them. The same no-op paging pattern repeats across the JSON summary/analytics/comparison endpoints, so clients always receive the full payload.

Also applies to: 439-440

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 405 - 410, The handlers (starting with
getFido2MetricsAggregationSummary) declare and log paging params limit and
startIndex but never apply them when calling the service; either wire these
params through to the service call (e.g., add limit and startIndex arguments to
the service method invocation and ensure the service uses them to page results)
or remove the paging params, docs and logs from the method signature to match
the contract. Apply the same fix to the other aggregation endpoints that accept
paging (the JSON/analytics/comparison aggregation handlers) so the API either
supports paging end-to-end or does not expose no-op paging parameters.

95-96: ⚠️ Potential issue | 🟠 Major

Remove @NotNull from optional date filters.

These params are documented and handled as optional, but @NotNull will reject requests that omit them before the StringUtils.isNotBlank() checks run. The same annotation pattern is copied across the other date-filtered metrics endpoints.

In Jakarta REST with Bean Validation enabled, does annotating an optional `@QueryParam String` with `@NotNull` cause requests that omit that query parameter to fail validation before the resource method executes?

Also applies to: 114-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 95 - 96, The method parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` which causes Bean Validation to
reject requests that omit these optional query params before the method logic
(StringUtils.isNotBlank checks) runs; remove the `@NotNull` annotation from the
`@QueryParam` parameters (startDate, endDate) in the affected method and remove
the same `@NotNull` usage from the other date-filtered metric endpoints (the other
parameter list around lines 114-122) so the parameters remain optional and are
validated/handled inside the method body.

175-179: ⚠️ Potential issue | 🟠 Major

Redact userId in the info log.

This writes caller-supplied user identifiers into standard logs. If you need correlation, log a redacted/hash form or move the detailed value to debug.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 175 - 179, The info log in Fido2MetricsResource (logger.info call
building "Fido2MetricsEntry for user search param ...") emits the raw
caller-supplied userId; change it to avoid logging the full identifier by
replacing the plain userId with a redacted or hashed form (e.g., mask characters
or compute a one-way hash) or move the full value to a debug-only log; update
the escapeLog(userId) usage in that logger.info invocation so it logs e.g.
escapeLog(redactOrHash(userId)) and, if needed, add a separate logger.debug that
outputs the original escaped userId for troubleshooting.

1027-1042: ⚠️ Potential issue | 🟠 Major

The paged-result helpers still treat startIndex as 0-based and misreport page size.

The API says startIndex is 1-based, but these helpers validate and slice with the raw value. startIndex=1 skips the first row, single-item/empty results can still fail during pagination, and entriesCount is set to toIndex instead of the number of returned entries.

Suggested fix
-            getStartIndex(identityProviderList, startIndex);
-            int toIndex = (startIndex + limit <= listSize) ? startIndex + limit : listSize;
+            int fromIndex = getStartIndex(identityProviderList, startIndex);
+            int toIndex = Math.min(fromIndex + limit, listSize);
             logger.info("Final startIndex:{}, limit:{}, toIndex:{}", startIndex, limit, toIndex);

             // Extract paginated data
             List<Fido2MetricsEntry> sublist = (identityProviderList == null) ? null
-                    : identityProviderList.subList(startIndex, toIndex);
+                    : identityProviderList.subList(fromIndex, toIndex);

             fido2MetricsEntryPagedResult = new Fido2MetricsEntryPagedResult();
             fido2MetricsEntryPagedResult.setStart(startIndex);
-            fido2MetricsEntryPagedResult.setEntriesCount(toIndex);
+            fido2MetricsEntryPagedResult.setEntriesCount(sublist == null ? 0 : sublist.size());
     private int getStartIndex(List<?> entriesList, int startIndex) {
+        if (startIndex < 1) {
+            throwBadRequestException("Page start index must be >= 1");
+        }
+        int zeroBasedStart = startIndex - 1;
         if (logger.isDebugEnabled()) {
             logger.debug("Get startIndex entriesList:{}, startIndex:{}", entriesList, escapeLog(startIndex));
         }

         if (entriesList != null && !entriesList.isEmpty()) {
             logger.debug("Get startIndex  entriesList.size():{}", entriesList.size());
             try {
-                entriesList.get(startIndex);
+                entriesList.get(zeroBasedStart);
             } catch (IndexOutOfBoundsException ioe) {
                 logger.error("Error while getting data startIndex:{}", startIndex, ioe);
                 throwBadRequestException("Page start index incorrect, total entries:{" + entriesList.size()
                         + "}, but provided:{" + startIndex + "} ");
             }
         }
-        return startIndex;
+        return zeroBasedStart;
     }

Also applies to: 1056-1071, 1080-1095

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1027 - 1042, The pagination treats startIndex as 0-based and
miscomputes counts; update Fido2MetricsResource to convert the incoming 1-based
startIndex to a 0-based offset before slicing and bounds-checking (e.g.,
startIndex = getStartIndex(identityProviderList, startIndex) or have
getStartIndex return the adjusted zero-based index), compute toIndex =
Math.min(startIndex + limit, listSize) using that zero-based start, guard for
null/empty identityProviderList so sublist uses safe indices, and set
entriesCount to the actual number of returned entries (sublist == null ? 0 :
sublist.size()) instead of toIndex; apply the same fixes to the other similar
blocks around the 1056-1071 and 1080-1095 sections.

1010-1018: ⚠️ Potential issue | 🟠 Major

parseDate() still rejects the advertised date-only formats.

LocalDateTime.parse needs time fields, so the ISO_LOCAL_DATE and dd-MM-yyyy branches will throw for inputs like 2026-01-31 or 31-01-2026. That breaks the documented contract for every endpoint using this helper.

Can `LocalDateTime.parse` in Java parse a date-only string such as `"2026-01-31"` with `DateTimeFormatter.ISO_LOCAL_DATE`, or `"31-01-2026"` with `DateTimeFormatter.ofPattern("dd-MM-yyyy")`?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1010 - 1018, parseDate() currently calls LocalDateTime.parse with
date-only formatters which throws for inputs like "2026-01-31"; instead detect
and parse date-only strings with LocalDate.parse (using
DateTimeFormatter.ISO_LOCAL_DATE and
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT)) and then convert to a
LocalDateTime (e.g., .atStartOfDay()) before returning; update the
ISO_LOCAL_DATE and METRICS_DATE_FORMAT branches in parseDate() to use
LocalDate.parse(...).atStartOfDay() to accept advertised date-only inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 292-298: The path parameter aggregationType is marked required but
has a default of "" which is semantically incorrect; remove the default or
replace it with an explicit enum of allowed values (e.g., HOURLY, DAILY, WEEKLY,
MONTHLY) to document valid inputs and keep required: true. Update every
occurrence of the aggregationType path parameter in the spec (the
aggregationType parameter definitions you added) to remove default: "" and, if
appropriate, add a schema.enum listing the permitted aggregation values so
clients and generators validate correctly.
- Around line 2136-2147: The OAuth2 clientCredentials scopes block in the
Swagger YAML is missing the scope "https://jans.io/oauth/config/fido2.admin"
which is referenced by Constants.java and used via
`@SecurityRequirement/`@ProtectedApi on Fido2ConfigResource,
Fido2RegistrationResource, and Fido2MetricsResource; regenerate or update the
Swagger components.securitySchemes.oauth2.flows.clientCredentials.scopes to
include the "https://jans.io/oauth/config/fido2.admin" entry (with a brief
description like "Administer fido2 config") so the YAML matches the Java
annotations and constants.

---

Duplicate comments:
In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 104-133: The current catch-all in Fido2MetricsResource around
validateDate/parseDate/getFido2MetricsEntries swallows and converts any
WebApplicationException (and other HTTP errors) into a 500 via
throwInternalServerException; change the catch block to rethrow
WebApplicationException instances unchanged (e.g., if (ex instanceof
WebApplicationException) throw (WebApplicationException) ex;), and only log+wrap
non-WebApplicationException errors with throwInternalServerException; reference
validateDate, parseDate, fido2MetricsService.getFido2MetricsEntries and
throwInternalServerException to locate the code to update.
- Around line 405-410: The handlers (starting with
getFido2MetricsAggregationSummary) declare and log paging params limit and
startIndex but never apply them when calling the service; either wire these
params through to the service call (e.g., add limit and startIndex arguments to
the service method invocation and ensure the service uses them to page results)
or remove the paging params, docs and logs from the method signature to match
the contract. Apply the same fix to the other aggregation endpoints that accept
paging (the JSON/analytics/comparison aggregation handlers) so the API either
supports paging end-to-end or does not expose no-op paging parameters.
- Around line 95-96: The method parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` which causes Bean Validation to
reject requests that omit these optional query params before the method logic
(StringUtils.isNotBlank checks) runs; remove the `@NotNull` annotation from the
`@QueryParam` parameters (startDate, endDate) in the affected method and remove
the same `@NotNull` usage from the other date-filtered metric endpoints (the other
parameter list around lines 114-122) so the parameters remain optional and are
validated/handled inside the method body.
- Around line 175-179: The info log in Fido2MetricsResource (logger.info call
building "Fido2MetricsEntry for user search param ...") emits the raw
caller-supplied userId; change it to avoid logging the full identifier by
replacing the plain userId with a redacted or hashed form (e.g., mask characters
or compute a one-way hash) or move the full value to a debug-only log; update
the escapeLog(userId) usage in that logger.info invocation so it logs e.g.
escapeLog(redactOrHash(userId)) and, if needed, add a separate logger.debug that
outputs the original escaped userId for troubleshooting.
- Around line 1027-1042: The pagination treats startIndex as 0-based and
miscomputes counts; update Fido2MetricsResource to convert the incoming 1-based
startIndex to a 0-based offset before slicing and bounds-checking (e.g.,
startIndex = getStartIndex(identityProviderList, startIndex) or have
getStartIndex return the adjusted zero-based index), compute toIndex =
Math.min(startIndex + limit, listSize) using that zero-based start, guard for
null/empty identityProviderList so sublist uses safe indices, and set
entriesCount to the actual number of returned entries (sublist == null ? 0 :
sublist.size()) instead of toIndex; apply the same fixes to the other similar
blocks around the 1056-1071 and 1080-1095 sections.
- Around line 1010-1018: parseDate() currently calls LocalDateTime.parse with
date-only formatters which throws for inputs like "2026-01-31"; instead detect
and parse date-only strings with LocalDate.parse (using
DateTimeFormatter.ISO_LOCAL_DATE and
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT)) and then convert to a
LocalDateTime (e.g., .atStartOfDay()) before returning; update the
ISO_LOCAL_DATE and METRICS_DATE_FORMAT branches in parseDate() to use
LocalDate.parse(...).atStartOfDay() to accept advertised date-only inputs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c4bc8f5d-9f14-43c4-9c8d-50872d92429a

📥 Commits

Reviewing files that changed from the base of the PR and between 012f2e7 and 37f4f3d.

📒 Files selected for processing (3)
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (6)
jans-config-api/plugins/docs/fido2-plugin-swagger.yaml (1)

2083-2094: ⚠️ Potential issue | 🟠 Major

Declare the config/fido2.admin scope in the OAuth2 scheme.

These operations use https://jans.io/oauth/config/fido2.admin in their security blocks, but clientCredentials.scopes never declares it here. That leaves the checked-in OpenAPI contract incomplete for generators and consumers.

🧾 Proposed fix
           scopes:
             https://jans.io/oauth/config/fido2.readonly: View fido2 config related
               information
             https://jans.io/oauth/config/fido2.write: Manage fido2 config related
               information
             https://jans.io/oauth/config/fido2.delete: Delete fido2 config related
               information
+            https://jans.io/oauth/config/fido2.admin: Administer fido2 config related
+              information
             https://jans.io/oauth/fido2.readonly: View fido2 related information
             https://jans.io/oauth/fido2.write: Manage fido2 related information
             https://jans.io/oauth/fido2.delete: Delete fido2 related information
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 2083 -
2094, The OAuth2 clientCredentials.scopes block is missing the admin scope used
by operations; add the scope "https://jans.io/oauth/config/fido2.admin" to the
same scopes list (clientCredentials.scopes) alongside the existing
config/fido2.readonly, config/fido2.write, and config/fido2.delete entries and
give it a short description such as "Administer fido2 config related
information" so the OpenAPI contract includes the admin permission referenced in
security blocks.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (5)

175-179: ⚠️ Potential issue | 🟠 Major

Do not log raw userId values at info level.

This writes caller-supplied identifiers into the standard request log. Redact it, hash it, or move it to debug.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 175 - 179, In Fido2MetricsResource, stop emitting raw userId at
info level: update the logger.info call that currently includes
escapeLog(userId) to either remove userId from the info message or replace it
with a non-reversible identifier (e.g., a hashed or redacted value produced by a
new helper like hashOrRedactUserId(userId)), and adjust the log format arguments
accordingly; if full userId is still needed for troubleshooting, move the
unredacted value to logger.debug with appropriate escaping instead.

95-96: ⚠️ Potential issue | 🟠 Major

Remove @NotNull from the optional date filters.

Every one of these handlers treats start_date / end_date as optional in the method body, but @NotNull makes omission fail before the handler runs. If omitted dates are intentional, drop the annotation here and update the generated contract so these query params are no longer marked required.

🧾 Representative fix
-            `@Parameter`(description = "Start date/time for "
-                    + " entries report. Accepted format dd-MM-yyyy or ISO-8601 date-time (e.g. yyyy-MM-ddTHH:mm:ssZ).", schema = `@Schema`(type = "string")) `@QueryParam`(value = "start_date") `@NotNull` String startDate,
-            `@Parameter`(description = "End date/time for the log entries. Accepted format dd-MM-yyyy or ISO-8601 date-time (e.g. yyyy-MM-ddTHH:mm:ssZ).", schema = `@Schema`(type = "string")) `@QueryParam`(value = "end_date") `@NotNull` String endDate)
+            `@Parameter`(description = "Start date/time for "
+                    + " entries report. Accepted format dd-MM-yyyy or ISO-8601 date-time (e.g. yyyy-MM-ddTHH:mm:ssZ).", schema = `@Schema`(type = "string")) `@QueryParam`(value = "start_date") String startDate,
+            `@Parameter`(description = "End date/time for the log entries. Accepted format dd-MM-yyyy or ISO-8601 date-time (e.g. yyyy-MM-ddTHH:mm:ssZ).", schema = `@Schema`(type = "string")) `@QueryParam`(value = "end_date") String endDate)
In Jakarta REST / Bean Validation, does annotating a `@QueryParam` with `@NotNull` cause requests that omit that query parameter to fail validation before the resource method executes?

Also applies to: 171-172, 250-251, 330-331, 409-410, 483-484, 553-554, 623-624, 693-694, 764-765

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 95 - 96, Remove the `@NotNull` annotations from the `@QueryParam`
parameters named start_date / end_date in Fido2MetricsResource so the optional
date filters are not validated as required; specifically edit the method
signatures in class Fido2MetricsResource to delete `@NotNull` on the startDate and
endDate parameters (and the other repeated occurrences noted in the review) and
regenerate/update the OpenAPI contract so those query params are no longer
marked required in the generated API spec.

67-69: ⚠️ Potential issue | 🟡 Minor

The code uses startIndex as 0-based, not 1-based.

The default is 0, and the paginator passes startIndex straight into subList(startIndex, toIndex). A client following the documented 1-based contract will page incorrectly. Please align the docs/Javadocs (and the checked-in Swagger) with the actual behavior, or convert to 0-based before slicing.

Also applies to: 92-93, 1021-1042

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 67 - 69, Javadoc and Swagger state startIndex is 1-based but the
implementation in Fido2MetricsResource passes startIndex directly into the
paginator/subList, which is 0-based; either update the Javadoc/Swagger to
document startIndex as 0-based everywhere or convert the incoming startIndex to
0-based before slicing (e.g., in the method handling the request in
Fido2MetricsResource decrement startIndex by 1 when startIndex>0) and adjust any
bounds checks accordingly; ensure the same change is applied to the other
affected occurrences noted (around the paginator/subList usage and the related
method signatures).

104-134: ⚠️ Potential issue | 🟠 Major

Don't convert validation failures into 500s.

validateDate() calls throwBadRequestException(...), but this blanket catch (Exception) rewrites that into throwInternalServerException(ex). Invalid client input will therefore surface as 500 instead of 400. The same catch-all pattern repeats throughout the class.

🧾 Proposed fix
-        } catch (Exception ex) {
+        } catch (WebApplicationException ex) {
+            throw ex;
+        } catch (Exception ex) {
             logger.error(ERR_MSG, ex);
             throwInternalServerException(ex);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 104 - 134, The current broad catch (Exception) in methods like the
block around validateDate(...) swallows validation errors (thrown by
validateDate/throwBadRequestException) and converts them to 500s; change the
catch to rethrow client errors and only translate unexpected exceptions to 500s
— e.g., replace catch (Exception ex) with logic that if ex is an instance of
BadRequestException or WebApplicationException (or whatever type
throwBadRequestException produces) then rethrow it, otherwise log and call
throwInternalServerException(ex); apply the same pattern to the other catch-all
handlers in this class (including references to validateDate, parseDate,
throwBadRequestException, and throwInternalServerException).

991-1018: ⚠️ Potential issue | 🔴 Critical

Date-only inputs never parse in parseDate().

LocalDateTime.parse(..., ISO_LOCAL_DATE) and the dd-MM-yyyy branch both require a time component, so the two advertised date-only formats are rejected for every endpoint that uses this helper.

🧾 Proposed fix
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.ZonedDateTime;
@@
-        try {
-            return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE);
+        try {
+            return LocalDate.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
         } catch (DateTimeParseException ignored) {
             // continue
         }
 
         // Fallback to legacy dd-MM-yyyy
-        return LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT));
+        return LocalDate.parse(trimmed, fallbackFormatter).atStartOfDay();
In Java, can `LocalDateTime.parse("2026-02-13", DateTimeFormatter.ISO_LOCAL_DATE)` or `LocalDateTime.parse("13-02-2026", DateTimeFormatter.ofPattern("dd-MM-yyyy"))` succeed, or do those formats need to be parsed as `LocalDate` and then converted with `atStartOfDay()`?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 991 - 1018, The parseDate method currently calls
LocalDateTime.parse with ISO_LOCAL_DATE and the METRICS_DATE_FORMAT which both
require time parts and thus fail for date-only strings; update parseDate (the
method parseDate in Fido2MetricsResource) so that when a date-only parse is
intended (ISO_LOCAL_DATE or the dd-MM-yyyy legacy format referenced by
METRICS_DATE_FORMAT) you call LocalDate.parse(...) and then convert to
LocalDateTime via atStartOfDay() before returning; keep the existing
ZonedDateTime/ISO_OFFSET_DATE_TIME and ISO_LOCAL_DATE_TIME branches unchanged
and only switch the date-only branches to parse-as-LocalDate then
atStartOfDay().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@jans-config-api/docs/jans-config-api-swagger.yaml`:
- Around line 18351-18352: The Swagger spec documents a "persisted" property
that is transient in the model (SessionId.java -> persisted marked `@Transient`
and transient) and therefore not serialized by Jackson; either remove the
persisted property from the OpenAPI spec (jans-config-api-swagger.yaml) to match
runtime behavior, or update the Jackson configuration in
ObjectMapperContextResolver to enable serialization of transient fields (so
transient fields are included) and regenerate the Swagger if this field must be
part of the API contract; reference SessionId.java -> persisted and
ObjectMapperContextResolver when making the change so the model, serializer, and
spec remain consistent.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 832-841: The response example for the 200 response references the
schema Fido2MetricsEntryPagedResult but uses an empty string value (""), which
is invalid JSON for that schema; update the example under "Response example" to
be a JSON object that matches the Fido2MetricsEntryPagedResult schema (or remove
the example so the validator uses the schema), ensuring keys/types align with
the components/schemas/Fido2MetricsEntryPagedResult definition so strict OpenAPI
validators and code generators accept it.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 1021-1045: The paging logic in getFido2MetricsEntryPagedResult
allows calls to identityProviderList.subList(startIndex,toIndex) when the list
is null/empty or startIndex is out of bounds; ensure startIndex is validated and
clamped before calling subList by (1) checking identityProviderList != null &&
!identityProviderList.isEmpty(), (2) computing a safeStart = Math.max(0,
Math.min(startIndex, listSize)) (or invoking getStartIndex and using its
returned/clamped value), (3) computing toIndex = Math.max(safeStart,
Math.min(safeStart + limit, listSize)), and (4) only calling
subList(safeStart,toIndex) when safeStart < toIndex; apply the same bounds
validation pattern to the other similar blocks noted (the other paged-result
methods around the later ranges).

---

Duplicate comments:
In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 2083-2094: The OAuth2 clientCredentials.scopes block is missing
the admin scope used by operations; add the scope
"https://jans.io/oauth/config/fido2.admin" to the same scopes list
(clientCredentials.scopes) alongside the existing config/fido2.readonly,
config/fido2.write, and config/fido2.delete entries and give it a short
description such as "Administer fido2 config related information" so the OpenAPI
contract includes the admin permission referenced in security blocks.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 175-179: In Fido2MetricsResource, stop emitting raw userId at info
level: update the logger.info call that currently includes escapeLog(userId) to
either remove userId from the info message or replace it with a non-reversible
identifier (e.g., a hashed or redacted value produced by a new helper like
hashOrRedactUserId(userId)), and adjust the log format arguments accordingly; if
full userId is still needed for troubleshooting, move the unredacted value to
logger.debug with appropriate escaping instead.
- Around line 95-96: Remove the `@NotNull` annotations from the `@QueryParam`
parameters named start_date / end_date in Fido2MetricsResource so the optional
date filters are not validated as required; specifically edit the method
signatures in class Fido2MetricsResource to delete `@NotNull` on the startDate and
endDate parameters (and the other repeated occurrences noted in the review) and
regenerate/update the OpenAPI contract so those query params are no longer
marked required in the generated API spec.
- Around line 67-69: Javadoc and Swagger state startIndex is 1-based but the
implementation in Fido2MetricsResource passes startIndex directly into the
paginator/subList, which is 0-based; either update the Javadoc/Swagger to
document startIndex as 0-based everywhere or convert the incoming startIndex to
0-based before slicing (e.g., in the method handling the request in
Fido2MetricsResource decrement startIndex by 1 when startIndex>0) and adjust any
bounds checks accordingly; ensure the same change is applied to the other
affected occurrences noted (around the paginator/subList usage and the related
method signatures).
- Around line 104-134: The current broad catch (Exception) in methods like the
block around validateDate(...) swallows validation errors (thrown by
validateDate/throwBadRequestException) and converts them to 500s; change the
catch to rethrow client errors and only translate unexpected exceptions to 500s
— e.g., replace catch (Exception ex) with logic that if ex is an instance of
BadRequestException or WebApplicationException (or whatever type
throwBadRequestException produces) then rethrow it, otherwise log and call
throwInternalServerException(ex); apply the same pattern to the other catch-all
handlers in this class (including references to validateDate, parseDate,
throwBadRequestException, and throwInternalServerException).
- Around line 991-1018: The parseDate method currently calls LocalDateTime.parse
with ISO_LOCAL_DATE and the METRICS_DATE_FORMAT which both require time parts
and thus fail for date-only strings; update parseDate (the method parseDate in
Fido2MetricsResource) so that when a date-only parse is intended (ISO_LOCAL_DATE
or the dd-MM-yyyy legacy format referenced by METRICS_DATE_FORMAT) you call
LocalDate.parse(...) and then convert to LocalDateTime via atStartOfDay() before
returning; keep the existing ZonedDateTime/ISO_OFFSET_DATE_TIME and
ISO_LOCAL_DATE_TIME branches unchanged and only switch the date-only branches to
parse-as-LocalDate then atStartOfDay().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 74750e75-c9c5-482e-97ee-4603f2efc1a6

📥 Commits

Reviewing files that changed from the base of the PR and between 37f4f3d and f6a8fb4.

📒 Files selected for processing (3)
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java

Signed-off-by: pujavs <pujas.works@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json (1)

4-4: ⚠️ Potential issue | 🟠 Major

Avoid encoding "no data" as 0% success rate.

This is the same issue flagged in the previous review: avgAuthenticationSuccessRate: 0 is misleading when Lines 24-26 show zero authentication attempts. A value of 0 implies "all attempts failed," not "no data available." Use null or omit the field when the denominator is zero to avoid skewing dashboards and trend calculations during idle periods.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json`
at line 4, The JSON metric field "avgAuthenticationSuccessRate" is currently set
to 0 which incorrectly implies a 0% success rate when there were no
authentication attempts; update the generator/serialization so that when the
authentication attempt denominator is zero it either sets
"avgAuthenticationSuccessRate" to null or omits the field entirely instead of
writing 0 — locate where "avgAuthenticationSuccessRate" is produced in the
metrics aggregation logic and add a check for zero attempts (or NaN) to return
null/skip the property.
jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json (1)

2-4: ⚠️ Potential issue | 🟡 Minor

Use a realistic duration spread in this fixture.

registrationAvgDuration, registrationMaxDuration, and registrationMinDuration are all identical here, so the example reads like a single-sample edge case instead of representative performance data.

Suggested fix
 {
-    "registrationAvgDuration": 471,
-    "registrationMaxDuration": 471,
-    "registrationMinDuration": 471
+    "registrationAvgDuration": 450,
+    "registrationMaxDuration": 850,
+    "registrationMinDuration": 120
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json`
around lines 2 - 4, The three duration fields registrationAvgDuration,
registrationMaxDuration, and registrationMinDuration currently have identical
values; update them to represent a realistic spread (registrationMinDuration <
registrationAvgDuration < registrationMaxDuration) — e.g., pick a lower min
(e.g., ~200–350), a higher max (e.g., ~600–900), and set avg to a plausible
midpoint (e.g., ~400–600) so the fixture reflects typical performance variance;
adjust only the values of registrationMinDuration, registrationAvgDuration, and
registrationMaxDuration to maintain internal consistency.
jans-config-api/docs/jans-config-api-swagger.yaml (2)

18535-18536: 🧹 Nitpick | 🔵 Trivial

Add description for accessToken property in source model.

The accessToken boolean property in TokenAttributes lacks a description. API consumers won't know what true vs false signifies. Per repository conventions, add a @Schema(description="...") annotation in the upstream model class (likely in jans-auth component) so the description propagates to this auto-generated Swagger.

Based on learnings: "When reviewing components like jans-auth, jans-fido2, jans-lock in the JanssenProject/jans repository, raise issues about missing property descriptions in API model classes."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 18535 -
18536, The TokenAttributes model's boolean property accessToken needs a
descriptive annotation: locate the TokenAttributes class in the jans-auth model
sources and add a `@Schema`(description="...") on the accessToken field explaining
what true/false means (e.g., whether the token represents an access token or
whether access token issuance is enabled), so the description propagates into
the generated Swagger (ensure you update the exact field named accessToken in
TokenAttributes).

16632-16633: 🧹 Nitpick | 🔵 Trivial

Consider adding description for fapi property in source model.

The fapi boolean property lacks a description. Based on the AI summary, this indicates "support for Financial-grade API (FAPI) standards." If this property is within the scope of jans-config-api (not a dependency object), adding a @Schema(description="...") annotation in the source model class would clarify its purpose for API consumers.

Since this Swagger file is auto-generated, any description should be added in the upstream source component.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 16632 -
16633, The OpenAPI `fapi` boolean property lacks a description in the source
model; add a `@Schema`(description="Support for Financial-grade API (FAPI)
standards - when true, enables FAPI-related behaviors") (or similar clear text)
to the field definition in the source model class that declares the `fapi`
property, then re-run the Swagger/OpenAPI generation so the docs include the
description; target the field named `fapi` in the model rather than editing the
generated yaml directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json`:
- Line 10: The "growthRate" field currently set to 0 is ambiguous given only one
entry in the "dataPoints" array; update the JSON so that when there are
insufficient historical data points to compute a trend (i.e., length of
"dataPoints" <= 1) the "growthRate" value is set to null (or the field omitted)
instead of 0 — locate the "growthRate" key in the
fido2-analytics-trends-aggregation-type.json example and change its value to
null (or remove the key) so consumers can distinguish "no change" from "cannot
compute".

---

Duplicate comments:
In `@jans-config-api/docs/jans-config-api-swagger.yaml`:
- Around line 18535-18536: The TokenAttributes model's boolean property
accessToken needs a descriptive annotation: locate the TokenAttributes class in
the jans-auth model sources and add a `@Schema`(description="...") on the
accessToken field explaining what true/false means (e.g., whether the token
represents an access token or whether access token issuance is enabled), so the
description propagates into the generated Swagger (ensure you update the exact
field named accessToken in TokenAttributes).
- Around line 16632-16633: The OpenAPI `fapi` boolean property lacks a
description in the source model; add a `@Schema`(description="Support for
Financial-grade API (FAPI) standards - when true, enables FAPI-related
behaviors") (or similar clear text) to the field definition in the source model
class that declares the `fapi` property, then re-run the Swagger/OpenAPI
generation so the docs include the description; target the field named `fapi` in
the model rather than editing the generated yaml directly.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json`:
- Around line 2-4: The three duration fields registrationAvgDuration,
registrationMaxDuration, and registrationMinDuration currently have identical
values; update them to represent a realistic spread (registrationMinDuration <
registrationAvgDuration < registrationMaxDuration) — e.g., pick a lower min
(e.g., ~200–350), a higher max (e.g., ~600–900), and set avg to a plausible
midpoint (e.g., ~400–600) so the fixture reflects typical performance variance;
adjust only the values of registrationMinDuration, registrationAvgDuration, and
registrationMaxDuration to maintain internal consistency.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json`:
- Line 4: The JSON metric field "avgAuthenticationSuccessRate" is currently set
to 0 which incorrectly implies a 0% success rate when there were no
authentication attempts; update the generator/serialization so that when the
authentication attempt denominator is zero it either sets
"avgAuthenticationSuccessRate" to null or omits the field entirely instead of
writing 0 — locate where "avgAuthenticationSuccessRate" is produced in the
metrics aggregation logic and add a check for zero attempts (or NaN) to return
null/skip the property.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f4488d07-9290-4feb-949c-091c84b7d09c

📥 Commits

Reviewing files that changed from the base of the PR and between f6a8fb4 and 78c72d2.

📒 Files selected for processing (17)
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-adoption.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregation-summary.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregations-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-devices.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-operation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-user.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-configuration.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-data.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-health-check.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-period-over-period-comparison.json

@pujavs
Copy link
Contributor Author

pujavs commented Mar 12, 2026

@coderabbitai request you to please review again

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

@pujavs Sure, I'll kick off a full review of the PR now!


🧠 Learnings used
Learnt from: moabu
Repo: JanssenProject/jans PR: 13060
File: CHANGELOG.md:25-26
Timestamp: 2026-01-22T14:33:53.554Z
Learning: Repo: JanssenProject/jans — For Release Please-generated PRs (chore(main): release …), CHANGELOG.md is auto-generated; avoid suggesting manual edits (e.g., duplicate/formatting cleanups) and ignore minor nits in that file.

Learnt from: pujavs
Repo: JanssenProject/jans PR: 13448
File: jans-config-api/plugins/docs/fido2-plugin-swagger.yaml:1823-1826
Timestamp: 2026-03-11T12:57:30.196Z
Learning: In the JanssenProject/jans repository, the `metricsData` property within the `Fido2MetricsAggregation` schema in `jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` is a dependency object that is out of scope of the jans-config-api module. Issues about its schema definition (e.g., `additionalProperties` type restrictions) should not be flagged during reviews of jans-config-api.

Learnt from: pujavs
Repo: JanssenProject/jans PR: 12847
File: jans-config-api/docs/jans-config-api-swagger.yaml:15497-15498
Timestamp: 2025-12-17T13:07:08.750Z
Learning: When reviewing components like `jans-auth`, `jans-fido2`, `jans-lock` in the JanssenProject/jans repository, raise issues about missing property descriptions in API model classes, as these descriptions will be reflected in the auto-generated Swagger specifications.
✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (21)
jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json (1)

1-6: ⚠️ Potential issue | 🟠 Major

Add the missing completionRate and dropOffRate fields.

This example JSON is incomplete. According to the previous review, the MetricsErrorAnalytics response schema requires completionRate and dropOffRate fields that are always populated by the FIDO2 metrics service but are absent from this example.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json`
around lines 1 - 6, The example JSON for the MetricsErrorAnalytics response is
missing the required numeric fields completionRate and dropOffRate; update the
example object that currently contains errorCategories, failureRate,
successRate, and topErrors by adding completionRate and dropOffRate (as numeric
values consistent with failureRate/successRate) so the example matches the
MetricsErrorAnalytics schema (referencing the MetricsErrorAnalytics response and
the fields completionRate and dropOffRate).
jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java (1)

294-304: ⚠️ Potential issue | 🟠 Major

Escape the caller-supplied fragment before building the regex.

pattern is still appended verbatim into a regex, so inputs like [ or *.json can either fail when compiled or change the match semantics. Quote the user fragment before wrapping it with ^.* / .*$.

🔧 Proposed fix
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import java.util.regex.Pattern;
 ...
     public String getRegexPattern(String pattern) {
         String searchPattern = DEFAULT_SEARCH_PATTERN;
         if (StringUtils.isNotBlank(pattern)) {
             StringBuilder stringBuilder = new StringBuilder();
             stringBuilder.append("^.*");
-            stringBuilder.append(pattern);
+            stringBuilder.append(Pattern.quote(pattern));
             stringBuilder.append(".*$");
             searchPattern = stringBuilder.toString();
         }
         return searchPattern;
     }
#!/bin/bash
set -euo pipefail

echo "== Current implementation =="
fd "BaseResource.java" --type f --exec sed -n '294,304p' {}

echo
echo "== Call sites of getRegexPattern(...) =="
rg -nP --type java -C2 '\bgetRegexPattern\s*\('

echo
echo "== Generic regex metacharacter demonstration =="
python3 <<'PY'
import re
samples = ["[", "*.json", "a+b"]
for raw in samples:
    expr = f"^.*{raw}.*$"
    print(f"\ninput={raw!r} regex={expr!r}")
    try:
        re.compile(expr)
        print("compiled: yes (but metacharacters alter semantics)")
    except re.error as e:
        print(f"compiled: no -> {e}")
PY

Expected result: the method body shows raw concatenation, and the demonstration highlights that unescaped metacharacters either fail compilation or stop behaving as literal text.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java`
around lines 294 - 304, The getRegexPattern method appends the caller-supplied
pattern verbatim into a regex, which can break or change semantics for
metacharacters; fix it by quoting the user fragment with
java.util.regex.Pattern.quote(...) (i.e., call Pattern.quote(pattern) before
wrapping with "^.*" and ".*$"), keep the existing blank-check logic, and add the
necessary import for java.util.regex.Pattern so compiled regexes treat the
fragment as a literal.
jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java (1)

24-24: ⚠️ Potential issue | 🟠 Major

Security: Do not log bearer tokens.

The accessToken is logged at ERROR level. Test logs are typically retained in CI systems, which would expose credentials. This applies to lines 24, 39, and 54.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`
at line 24, The test currently logs the raw bearer token via log.error (see the
log.error(...) call referencing accessToken in Fido2MetricsTest.java and similar
calls at lines 39 and 54); remove the raw accessToken from all test log messages
and either omit it entirely or replace it with a safe/masked representation
(e.g., show only last N chars or a fixed placeholder) in the log statements that
reference accessToken so that no full bearer token is ever written to test logs.
jans-config-api/plugins/docs/fido2-plugin-swagger.yaml (2)

1978-1989: ⚠️ Potential issue | 🟠 Major

Declare https://jans.io/oauth/config/fido2.admin in the OAuth2 scope registry.

The new operations reference this scope in their security blocks, but components.securitySchemes.oauth2.flows.clientCredentials.scopes still omits it. That leaves the document inconsistent and trips strict validators/generators.

Suggested fix
           scopes:
             https://jans.io/oauth/config/fido2.readonly: View fido2 config related
               information
             https://jans.io/oauth/config/fido2.write: Manage fido2 config related
               information
             https://jans.io/oauth/config/fido2.delete: Delete fido2 config related
               information
+            https://jans.io/oauth/config/fido2.admin: Administer fido2 config related
+              information
             https://jans.io/oauth/fido2.readonly: View fido2 related information
             https://jans.io/oauth/fido2.write: Manage fido2 related information
             https://jans.io/oauth/fido2.delete: Delete fido2 related information
             https://jans.io/oauth/fido2-metrics.readonly: View fido2 metrics related
               information
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 1978 -
1989, Add the missing OAuth2 scope declaration
"https://jans.io/oauth/config/fido2.admin" to
components.securitySchemes.oauth2.flows.clientCredentials.scopes so the registry
matches the scopes referenced by the FIDO2 operation security blocks; update the
scopes list in the YAML under the clientCredentials flow to include that exact
scope string and a short description like "Administer fido2 config related
information" so strict validators/generators no longer flag the discrepancy.

255-261: ⚠️ Potential issue | 🟡 Minor

Make startIndex docs match the documented default.

These parameters still say "1-based" while default: 0 describes zero-based pagination. Pick one convention; as written, the generated docs are misleading.

Also applies to: 431-437, 524-530, 668-674

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 255 -
261, The parameter docs for startIndex are inconsistent: the description says
"1-based" while the schema default is 0; change the description to match the
default (e.g., "The 0-based index of the first query result") or change the
default to 1 if you intend 1-based pagination; update every occurrence of the
startIndex parameter in this file (the startIndex parameter definitions at the
shown diff and the other occurrences referenced around lines 431-437, 524-530,
668-674) so description and default follow the same convention.
jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml (1)

18-22: ⚠️ Potential issue | 🟡 Minor

Give the metrics suite block a unique TestNG name.

This second <test> still reuses name="Fido2", so reports and name-based filtering cannot distinguish it from Fido2ResourceTest.

Suggested fix
-	<test name="Fido2" enabled="true">
+	<test name="Fido2Metrics" enabled="true">
         <classes>
             <class name="io.jans.configapi.plugin.fido2.test.Fido2MetricsTest" />
         </classes>
     </test>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml` around
lines 18 - 22, The second TestNG <test> entry reuses name="Fido2" which
conflicts with the other suite; update the name attribute to a unique value
(e.g., "Fido2Metrics" or similar) for the test that runs
io.jans.configapi.plugin.fido2.test.Fido2MetricsTest so reports and name-based
filtering can distinguish it from the Fido2ResourceTest suite.
jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java (2)

170-180: ⚠️ Potential issue | 🟠 Major

Keep readList() typed as a List and preserve JsonProcessingException.

This overload returns List<T> but accepts any Collection class, and it converts mapper failures into RuntimeException. That weakens the contract for callers that already declare JsonProcessingException.

Suggested fix
-    public static <T> List<T> readList(String str, Class<T> type) {
+    public static <T> List<T> readList(String str, Class<T> type) throws JsonProcessingException {
         return readList(str, ArrayList.class, type);
     }
     
-    public static <T> List<T> readList(String str, Class<? extends Collection> type, Class<T> elementType) {
+    public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType)
+            throws JsonProcessingException {
         final ObjectMapper mapper = new ObjectMapper();
-        try {
-            return mapper.readValue(str, mapper.getTypeFactory().constructCollectionType(type, elementType));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        return mapper.readValue(str, mapper.getTypeFactory().constructCollectionType(type, elementType));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`
around lines 170 - 180, The readList overload should keep returning List<T> and
preserve JsonProcessingException instead of wrapping IOExceptions in
RuntimeException: change the signature of readList(String, Class<? extends
Collection>, Class<T>) to declare throws JsonProcessingException (or let
JsonProcessingException propagate) and remove the catch that throws new
RuntimeException(e); ensure the first overload still delegates to this method
using ArrayList.class and returns List<T>; use mapper.readValue(str,
mapper.getTypeFactory().constructCollectionType(type, elementType)) and
cast/return it as List<T> so callers keep the List<T> return type and can handle
JsonProcessingException themselves (identify methods: readList(String,
Class<T>), readList(String, Class<? extends Collection>, Class<T>),
mapper.readValue, constructCollectionType).

164-167: ⚠️ Potential issue | 🟠 Major

readListValue() ignores clazz and loses the element type.

new TypeReference<List<T>>() {} cannot retain T here, so Jackson will deserialize elements as generic maps instead of clazz.

Suggested fix
     public static <T> List<T> readListValue(String content, Class<T> clazz) throws JsonProcessingException {
         ObjectMapper mapper = new ObjectMapper();
-        return mapper.readValue(content, new TypeReference<List<T>>() {
-        });
+        return mapper.readValue(content,
+                mapper.getTypeFactory().constructCollectionType(List.class, clazz));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`
around lines 164 - 167, readListValue currently ignores the provided clazz and
deserializes elements as generic maps because TypeReference<List<T>> loses T at
runtime; fix it by building a concrete JavaType for a List of the given element
class using the ObjectMapper's TypeFactory (e.g.,
constructCollectionType/ListType) and pass that JavaType into mapper.readValue
so elements are deserialized as clazz; update the method readListValue and the
ObjectMapper usage to use
mapper.getTypeFactory().constructCollectionType(List.class, clazz) (or
equivalent) when calling readValue.
jans-config-api/docs/jans-config-api-swagger.yaml (2)

1855-1862: ⚠️ Potential issue | 🟡 Minor

Use concrete sample values for these date parameters.

The parenthetical yyyy-MM-ddTHH:mm:ssZ is still a format token, not an example, so callers still have to infer the accepted shape. Publish concrete samples such as 31-12-2025 and 2025-12-31T23:59:59Z in the upstream endpoint annotations and regenerate the spec.

Based on learnings, jans-config-api/docs/jans-config-api-swagger.yaml is auto-generated from upstream source components, so this should be corrected in the owning endpoint annotations rather than in the generated YAML.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 1855 - 1862,
Update the upstream endpoint annotations that generate the swagger descriptions
for the query parameters start_date and end_date so their descriptions include
concrete example values instead of format tokens; replace the parenthetical
"yyyy-MM-ddTHH:mm:ssZ" with explicit samples (e.g. "31-12-2025" and
"2025-12-31T23:59:59Z") in the annotation for both start_date and end_date, then
regenerate the OpenAPI spec so the generated jans-config-api-swagger.yaml
contains those concrete examples.

16632-16633: ⚠️ Potential issue | 🟡 Minor

Document what fapi=true means in the owning model.

Adding fapi as a bare boolean leaves the contract ambiguous for consumers. Please add a property-level description on the source model field/getter and regenerate the Swagger so callers know which behavior this flag enables.

Based on learnings, missing property descriptions in this generated Swagger should be added in the upstream model classes so they propagate into jans-config-api/docs/jans-config-api-swagger.yaml.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 16632 -
16633, The generated Swagger exposes a bare boolean property "fapi" (in
jans-config-api-swagger.yaml) without any description; update the upstream model
class that declares the fapi field/getter (the owning POJO/DTO) to add a clear
`@Schema` or Javadoc/property annotation describing what fapi=true enables
(behavior, effects, defaults), then re-run the OpenAPI/Swagger generation to
regenerate jans-config-api-swagger.yaml so the property-level description is
included for consumers.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java (1)

22-32: ⚠️ Potential issue | 🟠 Major

Declare FIDO2_ADMIN_ACCESS in the OAuth2 scheme.

Fido2MetricsResource advertises Constants.FIDO2_ADMIN_ACCESS, but Line 22-Line 32 never registers that scope in @SecurityScheme. Generated OpenAPI docs and clients will miss a valid authorization option for the metrics endpoints.

Suggested fix
         `@OAuthScope`(name = Constants.FIDO2_DELETE_ACCESS, description = "Delete fido2 related information"),
         `@OAuthScope`(name = Constants.FIDO2_METRICS_READ_ACCESS, description = "View fido2 metrics related information"),
+        `@OAuthScope`(name = Constants.FIDO2_ADMIN_ACCESS, description = "Admin access to fido2 resources"),
         `@OAuthScope`(name = ApiAccessConstants.SUPER_ADMIN_READ_ACCESS, description = "Super admin for viewing application resource information"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java`
around lines 22 - 32, The OAuth2 SecurityScheme on ApiApplication (the
`@SecurityScheme` annotation) is missing the Constants.FIDO2_ADMIN_ACCESS scope
referenced by Fido2MetricsResource; add an `@OAuthScope` entry using
Constants.FIDO2_ADMIN_ACCESS (with an appropriate description like "Admin access
for fido2 operations/metrics") into the flows = `@OAuthFlows`(... `@OAuthFlow`(...
scopes = { ... }) ) list so the generated OpenAPI docs include that
authorization option.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (5)

134-137: ⚠️ Potential issue | 🟠 Major

Preserve bad-request and upstream HTTP statuses.

Line 134 rewrites every WebApplicationException from validateDate() or Fido2MetricsService into a 500. Catch WebApplicationException separately and rethrow it unchanged; the same catch-all is repeated throughout this resource.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 134 - 137, The catch-all in Fido2MetricsResource (e.g., in the
block catching Exception around calls to validateDate() and Fido2MetricsService)
currently converts every WebApplicationException into a 500; change the error
handling to first catch javax.ws.rs.WebApplicationException (or JAX-RS
WebApplicationException) and rethrow it unchanged, then catch Exception for
logging and call throwInternalServerException(ex); ensure you reference the
existing validateDate(), Fido2MetricsService calls, logger.error(ERR_MSG, ex)
and throwInternalServerException(ex) so the new ordering preserves upstream HTTP
statuses for client/upstream errors.

97-99: ⚠️ Potential issue | 🟠 Major

Do not require optional start_date / end_date parameters.

Line 98 and Line 99 are documented and handled as optional, but @NotNull causes Bean Validation to reject omitted query params before the handler runs. The same annotation pattern is repeated across the other metrics handlers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 97 - 99, The query parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` which makes them mandatory even
though they should be optional; remove the `@NotNull` annotation from the method
parameters (startDate, endDate) in Fido2MetricsResource and the same pattern in
the other metrics handlers so Bean Validation won't reject missing query params,
and if your codebase uses nullability annotations add `@Nullable` (or leave
unannotated) and ensure the existing handler logic checks for null/empty values
and applies defaults as needed.

181-185: ⚠️ Potential issue | 🟠 Major

Remove raw userId from info logs.

Line 183-Line 185 writes caller-supplied user identifiers into standard logs. Redact/hash it or move it to debug to avoid PII leakage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 181 - 185, The info log in Fido2MetricsResource (the block that
builds "Fido2MetricsEntry for user search param") currently emits
caller-supplied userId and may leak PII; change it so userId is not logged in
cleartext at info level—either remove it from the info message or replace it
with a non-reversible identifier (e.g., a truncated/hashed value such as SHA-256
hex or a stable prefix) and keep the full userId only under debug (logger.debug)
if needed; update the logging call that currently uses escapeLog(userId)
accordingly and ensure any hashing/obfuscation uses a utility available in the
class or a new helper so tests and other callers (e.g., the method in
Fido2MetricsResource) reference the hashed/redacted symbol instead of the raw
userId.

1012-1040: ⚠️ Potential issue | 🟠 Major

Fix date-only parsing in parseDate().

Line 1033 and Line 1039 parse date-only strings into LocalDateTime, which throws for values like 2026-03-12 and 12-03-2026. That breaks formats the API explicitly advertises.

Suggested fix
-        // Try ISO date only (e.g. 2024-02-13)
+        // Try ISO date only (e.g. 2024-02-13)
         try {
-            return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE);
+            return java.time.LocalDate.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
         } catch (DateTimeParseException ignored) {
             // continue
         }
 
         // Fallback to legacy dd-MM-yyyy
-        return LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT));
+        return java.time.LocalDate.parse(trimmed, fallbackFormatter).atStartOfDay();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1012 - 1040, The parseDate method currently calls
LocalDateTime.parse for date-only inputs, causing DateTimeParseException for
values like "2026-03-12" and "12-03-2026"; update parseDate so that for the ISO
date-only branch you parse into a LocalDate (LocalDate.parse(trimmed,
DateTimeFormatter.ISO_LOCAL_DATE)) and return date.atStartOfDay(), and likewise
for the legacy METRICS_DATE_FORMAT branch parse into LocalDate using
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT) and return atStartOfDay(); keep
the existing ZonedDateTime and ISO_LOCAL_DATE_TIME branches unchanged.

1042-1120: ⚠️ Potential issue | 🟠 Major

Convert the public 1-based index before calling subList().

Line 1051 and Line 1082 validate startIndex as if it were 0-based and then slice with it directly. With the documented 1-based contract, startIndex=1 skips the first item, and empty results with the default index can still throw.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1042 - 1120, The code treats the documented 1-based startIndex as
0-based when slicing; convert the incoming 1-based startIndex to a 0-based index
before validating and calling subList in both getFido2MetricsEntryPagedResult
and getFido2MetricsAggregationPagedResult: create a new variable (e.g.
zeroBasedStart = Math.max(0, startIndex - 1)), call getStartIndex(entriesList,
zeroBasedStart), use zeroBasedStart for toIndex and subList calculations, and
use the original startIndex when populating the paged result's setStart if the
API contract requires returning the 1-based value; update getStartIndex
signature/usages only if necessary to accept the zeroBasedStart.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java (3)

449-453: ⚠️ Potential issue | 🟠 Major

Send periods under its documented query key.

Line 451 stores the comparison window as startTime, so the upstream comparison endpoint never receives the periods parameter.

Suggested fix
         // Query Parameter
         Map<String, String> data = new HashMap<>();
-        data.put("startTime", String.valueOf(periods));
+        data.put("periods", String.valueOf(periods));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 449 - 453, The code in Fido2MetricsService is sending the
comparison window under the wrong query key ("startTime") so the upstream
comparison endpoint doesn't receive the periods value; change the Map key for
the variable periods to the documented query parameter name (use "periods"
instead of "startTime") before calling
getMetricsData(this.getPeriodOverPeriodComparisonUrl() + "/" + aggregationType,
headers, data), ensuring the Map<String,String> data contains
data.put("periods", String.valueOf(periods)).

493-518: ⚠️ Potential issue | 🔴 Critical

Read the paged wrapper before deserializing entries.

Line 507 treats the whole response object as List<T>, but the metrics response is a paged wrapper. This either fails deserialization or discards start / entriesCount / totalEntriesCount, so the REST layer cannot report accurate paging metadata.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 493 - 518, getFido2MetricsPagedResult is deserializing the entire
response as List<T>, which ignores the paged wrapper; change it to first
deserialize the JSON into the PagedResult wrapper (or a JsonNode), extract
paging fields (start, entriesCount, totalEntriesCount) and then deserialize the
"entries" sub-node into List<T> using Jackson.readList(..., type); populate a
new PagedResult<T> with the extracted paging metadata and the deserialized
entries, handling nulls/empty lists safely so the REST layer returns correct
paging info.

162-163: ⚠️ Potential issue | 🟠 Major

Stop building forwarded URLs with //.

getFido2UserMetricsEntriesUrl() already ends with /, so Line 162 produces /entries/user//{userId}. The same concatenation pattern at Line 198, Line 233, Line 267, Line 299, Line 427, and Line 453 can 404 on servers that do not normalize duplicate separators.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 162 - 163, The code is producing duplicate slashes when building
forwarded URLs (e.g., in the call to getFido2MetricsPagedResult using
getFido2UserMetricsEntriesUrl() + "/" + userId). Fix by normalizing
concatenation: check if getFido2UserMetricsEntriesUrl() endsWith("/") and append
userId without adding an extra "/" (or otherwise ensure a single separator), and
apply the same fix to every similar concatenation that appends "/" + userId or
other path segments before calling getFido2MetricsPagedResult; update the call
sites that use getFido2UserMetricsEntriesUrl(), getFido2MetricsEntriesUrl() (and
any similar URL-returning methods) so they conditionally add the slash or use a
small helper to join URL segments safely.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java (2)

115-124: ⚠️ Potential issue | 🟡 Minor

Use the requested envelope key.

Line 123 always extracts "response", so any caller that passes a different nodeName gets the wrong subtree.

Suggested fix
-        if (StringUtils.isNotBlank(nodeName) && jsonNode != null && jsonNode.get(nodeName) != null) {
-            jsonNode = jsonNode.get("response");
+        if (StringUtils.isNotBlank(nodeName) && jsonNode != null && jsonNode.get(nodeName) != null) {
+            jsonNode = jsonNode.get(nodeName);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java`
around lines 115 - 124, The method getResponseJsonNode currently always extracts
the "response" child regardless of the requested key; update it so that after
parsing (in getResponseJsonNode) you use the nodeName parameter (not the
hard-coded "response") when calling jsonNode.get(...), and return that child
when present; ensure you still handle blank jsonSring and null jsonNode cases
and use the existing variable names (jsonSring, nodeName) so callers receive the
correct subtree.

71-112: ⚠️ Potential issue | 🟠 Major

Surface failed upstream GETs instead of returning null.

ConfigHttpService.executeGet() can return null on I/O failure, and Line 75 plus Line 93 treat that the same as “no data”. That makes transport and upstream HTTP failures look like empty metrics responses instead of 4xx/5xx errors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java`
around lines 71 - 112, getResponseJsonNode and getResponseEntityString currently
treat a null HttpServiceResponse (and null inner HttpResponse/entity) as “no
data” and return null, which hides upstream transport failures; change both
methods so that when serviceResponse is null (or when
serviceResponse.getHttpResponse() is null) you throw a WebApplicationException
(e.g., Bad Gateway/502) containing a descriptive message (and any available
error text) instead of returning null, and likewise ensure
getResponseEntityString throws a WebApplicationException with the upstream
status code when httpResponse is present but non-200; update thrown messages to
include the response body or cause to aid debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`:
- Line 31: The test contains copy-paste errors in log messages: replace the
incorrect method names in the log.info calls so they match the test methods
being exercised — change the message "getFido2Configuration()" to
"getFido2MetricsEntry()" for the log in the getFido2MetricsEntry test, change
"getFido2MetricsEntry()" to "getFido2UserMetrics()" for the log in the
getFido2UserMetrics test, and change "getFido2Configuration()" to
"getFido2MetricsConfig()" for the log in the getFido2MetricsConfig test (update
the log.info calls in Fido2MetricsTest.java accordingly so the string literals
reflect the correct method names).

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java`:
- Line 24: The test currently logs the bearer token via log.error in
Fido2ResourceTest (the call that prints accessToken, issuer, fido2ConfigUrl);
remove the accessToken from that log (or replace it with a masked value like
"<REDACTED>" or only log its presence/length) so no live token is written to
test/CI logs, and update the log statement in Fido2ResourceTest.java accordingly
while keeping issuer and fido2ConfigUrl logging intact.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregation-summary.json`:
- Around line 1-8: The JSON currently uses 0 for avgAuthenticationSuccessRate
(and potentially other avg* fields) even when their denominators are zero;
update the logic that produces avgAuthenticationSuccessRate and
avgRegistrationSuccessRate so that when totalAuthentications or
totalRegistrations (respectively) is 0 the average value is null rather than 0,
otherwise compute and emit the numeric rate; locate and modify the code or
template that sets the JSON keys avgAuthenticationSuccessRate and
avgRegistrationSuccessRate to apply this conditional null-on-zero rule.

---

Duplicate comments:
In `@jans-config-api/docs/jans-config-api-swagger.yaml`:
- Around line 1855-1862: Update the upstream endpoint annotations that generate
the swagger descriptions for the query parameters start_date and end_date so
their descriptions include concrete example values instead of format tokens;
replace the parenthetical "yyyy-MM-ddTHH:mm:ssZ" with explicit samples (e.g.
"31-12-2025" and "2025-12-31T23:59:59Z") in the annotation for both start_date
and end_date, then regenerate the OpenAPI spec so the generated
jans-config-api-swagger.yaml contains those concrete examples.
- Around line 16632-16633: The generated Swagger exposes a bare boolean property
"fapi" (in jans-config-api-swagger.yaml) without any description; update the
upstream model class that declares the fapi field/getter (the owning POJO/DTO)
to add a clear `@Schema` or Javadoc/property annotation describing what fapi=true
enables (behavior, effects, defaults), then re-run the OpenAPI/Swagger
generation to regenerate jans-config-api-swagger.yaml so the property-level
description is included for consumers.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 1978-1989: Add the missing OAuth2 scope declaration
"https://jans.io/oauth/config/fido2.admin" to
components.securitySchemes.oauth2.flows.clientCredentials.scopes so the registry
matches the scopes referenced by the FIDO2 operation security blocks; update the
scopes list in the YAML under the clientCredentials flow to include that exact
scope string and a short description like "Administer fido2 config related
information" so strict validators/generators no longer flag the discrepancy.
- Around line 255-261: The parameter docs for startIndex are inconsistent: the
description says "1-based" while the schema default is 0; change the description
to match the default (e.g., "The 0-based index of the first query result") or
change the default to 1 if you intend 1-based pagination; update every
occurrence of the startIndex parameter in this file (the startIndex parameter
definitions at the shown diff and the other occurrences referenced around lines
431-437, 524-530, 668-674) so description and default follow the same
convention.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java`:
- Around line 22-32: The OAuth2 SecurityScheme on ApiApplication (the
`@SecurityScheme` annotation) is missing the Constants.FIDO2_ADMIN_ACCESS scope
referenced by Fido2MetricsResource; add an `@OAuthScope` entry using
Constants.FIDO2_ADMIN_ACCESS (with an appropriate description like "Admin access
for fido2 operations/metrics") into the flows = `@OAuthFlows`(... `@OAuthFlow`(...
scopes = { ... }) ) list so the generated OpenAPI docs include that
authorization option.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 134-137: The catch-all in Fido2MetricsResource (e.g., in the block
catching Exception around calls to validateDate() and Fido2MetricsService)
currently converts every WebApplicationException into a 500; change the error
handling to first catch javax.ws.rs.WebApplicationException (or JAX-RS
WebApplicationException) and rethrow it unchanged, then catch Exception for
logging and call throwInternalServerException(ex); ensure you reference the
existing validateDate(), Fido2MetricsService calls, logger.error(ERR_MSG, ex)
and throwInternalServerException(ex) so the new ordering preserves upstream HTTP
statuses for client/upstream errors.
- Around line 97-99: The query parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` which makes them mandatory even
though they should be optional; remove the `@NotNull` annotation from the method
parameters (startDate, endDate) in Fido2MetricsResource and the same pattern in
the other metrics handlers so Bean Validation won't reject missing query params,
and if your codebase uses nullability annotations add `@Nullable` (or leave
unannotated) and ensure the existing handler logic checks for null/empty values
and applies defaults as needed.
- Around line 181-185: The info log in Fido2MetricsResource (the block that
builds "Fido2MetricsEntry for user search param") currently emits
caller-supplied userId and may leak PII; change it so userId is not logged in
cleartext at info level—either remove it from the info message or replace it
with a non-reversible identifier (e.g., a truncated/hashed value such as SHA-256
hex or a stable prefix) and keep the full userId only under debug (logger.debug)
if needed; update the logging call that currently uses escapeLog(userId)
accordingly and ensure any hashing/obfuscation uses a utility available in the
class or a new helper so tests and other callers (e.g., the method in
Fido2MetricsResource) reference the hashed/redacted symbol instead of the raw
userId.
- Around line 1012-1040: The parseDate method currently calls
LocalDateTime.parse for date-only inputs, causing DateTimeParseException for
values like "2026-03-12" and "12-03-2026"; update parseDate so that for the ISO
date-only branch you parse into a LocalDate (LocalDate.parse(trimmed,
DateTimeFormatter.ISO_LOCAL_DATE)) and return date.atStartOfDay(), and likewise
for the legacy METRICS_DATE_FORMAT branch parse into LocalDate using
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT) and return atStartOfDay(); keep
the existing ZonedDateTime and ISO_LOCAL_DATE_TIME branches unchanged.
- Around line 1042-1120: The code treats the documented 1-based startIndex as
0-based when slicing; convert the incoming 1-based startIndex to a 0-based index
before validating and calling subList in both getFido2MetricsEntryPagedResult
and getFido2MetricsAggregationPagedResult: create a new variable (e.g.
zeroBasedStart = Math.max(0, startIndex - 1)), call getStartIndex(entriesList,
zeroBasedStart), use zeroBasedStart for toIndex and subList calculations, and
use the original startIndex when populating the paged result's setStart if the
API contract requires returning the 1-based value; update getStartIndex
signature/usages only if necessary to accept the zeroBasedStart.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`:
- Around line 449-453: The code in Fido2MetricsService is sending the comparison
window under the wrong query key ("startTime") so the upstream comparison
endpoint doesn't receive the periods value; change the Map key for the variable
periods to the documented query parameter name (use "periods" instead of
"startTime") before calling
getMetricsData(this.getPeriodOverPeriodComparisonUrl() + "/" + aggregationType,
headers, data), ensuring the Map<String,String> data contains
data.put("periods", String.valueOf(periods)).
- Around line 493-518: getFido2MetricsPagedResult is deserializing the entire
response as List<T>, which ignores the paged wrapper; change it to first
deserialize the JSON into the PagedResult wrapper (or a JsonNode), extract
paging fields (start, entriesCount, totalEntriesCount) and then deserialize the
"entries" sub-node into List<T> using Jackson.readList(..., type); populate a
new PagedResult<T> with the extracted paging metadata and the deserialized
entries, handling nulls/empty lists safely so the REST layer returns correct
paging info.
- Around line 162-163: The code is producing duplicate slashes when building
forwarded URLs (e.g., in the call to getFido2MetricsPagedResult using
getFido2UserMetricsEntriesUrl() + "/" + userId). Fix by normalizing
concatenation: check if getFido2UserMetricsEntriesUrl() endsWith("/") and append
userId without adding an extra "/" (or otherwise ensure a single separator), and
apply the same fix to every similar concatenation that appends "/" + userId or
other path segments before calling getFido2MetricsPagedResult; update the call
sites that use getFido2UserMetricsEntriesUrl(), getFido2MetricsEntriesUrl() (and
any similar URL-returning methods) so they conditionally add the slash or use a
small helper to join URL segments safely.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java`:
- Around line 115-124: The method getResponseJsonNode currently always extracts
the "response" child regardless of the requested key; update it so that after
parsing (in getResponseJsonNode) you use the nodeName parameter (not the
hard-coded "response") when calling jsonNode.get(...), and return that child
when present; ensure you still handle blank jsonSring and null jsonNode cases
and use the existing variable names (jsonSring, nodeName) so callers receive the
correct subtree.
- Around line 71-112: getResponseJsonNode and getResponseEntityString currently
treat a null HttpServiceResponse (and null inner HttpResponse/entity) as “no
data” and return null, which hides upstream transport failures; change both
methods so that when serviceResponse is null (or when
serviceResponse.getHttpResponse() is null) you throw a WebApplicationException
(e.g., Bad Gateway/502) containing a descriptive message (and any available
error text) instead of returning null, and likewise ensure
getResponseEntityString throws a WebApplicationException with the upstream
status code when httpResponse is present but non-200; update thrown messages to
include the response body or cause to aid debugging.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`:
- Line 24: The test currently logs the raw bearer token via log.error (see the
log.error(...) call referencing accessToken in Fido2MetricsTest.java and similar
calls at lines 39 and 54); remove the raw accessToken from all test log messages
and either omit it entirely or replace it with a safe/masked representation
(e.g., show only last N chars or a fixed placeholder) in the log statements that
reference accessToken so that no full bearer token is ever written to test logs.

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml`:
- Around line 18-22: The second TestNG <test> entry reuses name="Fido2" which
conflicts with the other suite; update the name attribute to a unique value
(e.g., "Fido2Metrics" or similar) for the test that runs
io.jans.configapi.plugin.fido2.test.Fido2MetricsTest so reports and name-based
filtering can distinguish it from the Fido2ResourceTest suite.

In
`@jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json`:
- Around line 1-6: The example JSON for the MetricsErrorAnalytics response is
missing the required numeric fields completionRate and dropOffRate; update the
example object that currently contains errorCategories, failureRate,
successRate, and topErrors by adding completionRate and dropOffRate (as numeric
values consistent with failureRate/successRate) so the example matches the
MetricsErrorAnalytics schema (referencing the MetricsErrorAnalytics response and
the fields completionRate and dropOffRate).

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java`:
- Around line 294-304: The getRegexPattern method appends the caller-supplied
pattern verbatim into a regex, which can break or change semantics for
metacharacters; fix it by quoting the user fragment with
java.util.regex.Pattern.quote(...) (i.e., call Pattern.quote(pattern) before
wrapping with "^.*" and ".*$"), keep the existing blank-check logic, and add the
necessary import for java.util.regex.Pattern so compiled regexes treat the
fragment as a literal.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`:
- Around line 170-180: The readList overload should keep returning List<T> and
preserve JsonProcessingException instead of wrapping IOExceptions in
RuntimeException: change the signature of readList(String, Class<? extends
Collection>, Class<T>) to declare throws JsonProcessingException (or let
JsonProcessingException propagate) and remove the catch that throws new
RuntimeException(e); ensure the first overload still delegates to this method
using ArrayList.class and returns List<T>; use mapper.readValue(str,
mapper.getTypeFactory().constructCollectionType(type, elementType)) and
cast/return it as List<T> so callers keep the List<T> return type and can handle
JsonProcessingException themselves (identify methods: readList(String,
Class<T>), readList(String, Class<? extends Collection>, Class<T>),
mapper.readValue, constructCollectionType).
- Around line 164-167: readListValue currently ignores the provided clazz and
deserializes elements as generic maps because TypeReference<List<T>> loses T at
runtime; fix it by building a concrete JavaType for a List of the given element
class using the ObjectMapper's TypeFactory (e.g.,
constructCollectionType/ListType) and pass that JavaType into mapper.readValue
so elements are deserialized as clazz; update the method readListValue and the
ObjectMapper usage to use
mapper.getTypeFactory().constructCollectionType(List.class, clazz) (or
equivalent) when calling readValue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 15b3c1e2-447b-4415-af86-127ca3340fbd

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa4d67 and 78c72d2.

📒 Files selected for processing (33)
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/docs/scim-plugin-swagger.yaml
  • jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/ClientFactory.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Constants.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java
  • jans-config-api/plugins/fido2-plugin/src/test/resources/test.properties
  • jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml
  • jans-config-api/profiles/default/config-api-test.properties
  • jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties
  • jans-config-api/profiles/local/test.properties
  • jans-config-api/server/src/main/resources/config-api-rs-protect.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-adoption.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregation-summary.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregations-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-devices.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-operation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-user.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-configuration.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-data.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-health-check.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-period-over-period-comparison.json
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java

pujavs added 4 commits March 12, 2026 20:40
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
@pujavs pujavs requested a review from yuriyzz as a code owner March 12, 2026 17:59
@pujavs
Copy link
Contributor Author

pujavs commented Mar 12, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mo-auto mo-auto added the comp-jans-auth-server Component affected by issue or PR label Mar 12, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (9)
jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java (1)

169-180: ⚠️ Potential issue | 🟠 Major

Preserve JsonProcessingException in readList() instead of wrapping it.

Line 178 converts Jackson parse failures into RuntimeException, which weakens the contract for callers that already declare throws JsonProcessingException. In particular, jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java:494-516 expects Jackson.readList(...) failures to stay typed, so malformed upstream metrics payloads now surface as generic 500s instead of the declared parse error.

Proposed fix
-    public static <T> List<T> readList(String str, Class<T> type) {
+    public static <T> List<T> readList(String str, Class<T> type) throws JsonProcessingException {
         return readList(str, ArrayList.class, type);
     }

-    public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType) {
+    public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType)
+            throws JsonProcessingException {
         final ObjectMapper mapper = new ObjectMapper();
-        try {
-            return mapper.readValue(str,
-                    mapper.getTypeFactory().constructCollectionType(type.asSubclass(Collection.class), elementType));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        return mapper.readValue(str,
+                mapper.getTypeFactory().constructCollectionType(type.asSubclass(Collection.class), elementType));
     }
#!/bin/bash
set -euo pipefail

echo "== Jackson.readList implementation =="
sed -n '169,180p' jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java

echo
echo "== Fido2MetricsService caller and declared exception contract =="
sed -n '494,516p' jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`
around lines 169 - 180, The readList(...) methods in Jackson currently catch
IOException and wrap it in RuntimeException, losing JsonProcessingException
typing expected by callers like Fido2MetricsService; update both overloads
(readList(String, Class<T>) and readList(String, Class<? extends List>,
Class<T>)) to declare "throws JsonProcessingException" and change the catch
logic to rethrow JsonProcessingException (e.g., catch JsonProcessingException
and throw it) while keeping a broader catch for other IOExceptions (wrap or
convert those), so callers that declare throws JsonProcessingException continue
to receive the typed exception.
jans-config-api/docs/jans-config-api-swagger.yaml (2)

18350-18351: ⚠️ Potential issue | 🟠 Major

persisted should not be documented unless the owning model actually exposes it.

Lines 18350-18351 add persisted to the schema, but jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java:93-101 marks that member @Hidden, @Transient, and transient. That creates a spec/runtime mismatch unless the owning component explicitly serializes it or stops hiding it.

#!/bin/bash
# Verify that SessionId.persisted is hidden/transient in the source model
# while still present in the generated OpenAPI contract.
sed -n '90,102p' jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java
rg -n -C2 '^ {8}persisted:$|^ {10}type: boolean$' jans-config-api/docs/jans-config-api-swagger.yaml

Based on learnings, jans-config-api/docs/jans-config-api-swagger.yaml is auto-generated from upstream source components, so the fix belongs in the owning model/generator path rather than in this generated YAML.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 18350 -
18351, The OpenAPI schema incorrectly documents the SessionId.persisted property
even though the owning model (SessionId class) marks it `@Hidden`, `@Transient` and
transient; update the source model or generator so the property is not emitted
into the generated spec: either remove/annotate persisted for serialization
(undo transient/Hidden) if it should be exposed, or change the
generator/model-to-OpenAPI mapping to skip fields annotated with
`@Hidden/`@Transient (affecting SessionId.persisted) so the swagger.yaml no longer
contains the persisted boolean.

1855-1862: ⚠️ Potential issue | 🟡 Minor

Use concrete sample values for these date parameters.

Lines 1855-1862 still show format tokens (dd-MM-yyyy, yyyy-MM-ddTHH:mm:ssZ) instead of actual examples, so callers do not see a concrete valid value to send. Please publish sample values like 31-12-2025 and 2025-12-31T23:59:59Z in the upstream endpoint annotations that generate this YAML.

Based on learnings, jans-config-api/docs/jans-config-api-swagger.yaml is auto-generated from upstream source components, so this should be corrected in the owning endpoint annotations rather than in the generated YAML.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 1855 - 1862,
The generated Swagger shows format tokens rather than concrete examples for the
query parameters start_date and end_date; update the upstream endpoint parameter
annotations (the controller/handler parameter annotations that generate these
OpenAPI params for start_date and end_date) to include concrete example values
and/or an example field instead of format tokens — e.g. example: "31-12-2025"
for the dd-MM-yyyy form and example: "2025-12-31T23:59:59Z" for the ISO-8601
form — so the generated jans-config-api-swagger.yaml emits those sample values
for the start_date and end_date parameters.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java (2)

449-453: ⚠️ Potential issue | 🟠 Major

Query parameter key startTime should be periods.

The upstream endpoint expects periods parameter, but this method sends it under startTime, so the comparison window is never received.

Proposed fix
         // Query Parameter
         Map<String, String> data = new HashMap<>();
-        data.put("startTime", String.valueOf(periods));
+        data.put("periods", String.valueOf(periods));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 449 - 453, In Fido2MetricsService, the request builds query params
using the wrong key "startTime" so the downstream comparison endpoint never
receives the window; update the Map in the method that calls getMetricsData()
(the block returning getMetricsData(this.getPeriodOverPeriodComparisonUrl() +
"/" + aggregationType, headers, data)) to use data.put("periods",
String.valueOf(periods)) instead of "startTime", ensuring the periods parameter
name matches the upstream API.

52-54: ⚠️ Potential issue | 🟠 Major

URL helper methods end with trailing slashes causing double-slash URLs downstream.

All URL helper methods return paths ending with /. When callers concatenate "/" + variable, this produces URLs like /entries/user//userId which will 404 on servers that don't normalize duplicate separators.

Affected call sites: lines 162, 198, 233, 267, 299, 427, 453.

Proposed fix - remove trailing slashes from helpers
 public String getFido2UserMetricsEntriesUrl() {
-    return getFido2MetricsUrl() + "/entries/user/";
+    return getFido2MetricsUrl() + "/entries/user";
 }

 public String getMetricsEntriesByOperationUrl() {
-    return getFido2MetricsUrl() + "/entries/operation/";
+    return getFido2MetricsUrl() + "/entries/operation";
 }

 public String getMetricsAggregationsUrl() {
-    return getFido2MetricsUrl() + "/aggregations/";
+    return getFido2MetricsUrl() + "/aggregations";
 }

 public String getMetricsAnalyticsUrl() {
-    return getFido2MetricsUrl() + "/analytics/";
+    return getFido2MetricsUrl() + "/analytics";
 }

 public String getMetricsAnalyticsPerformanceUrl() {
-    return getFido2MetricsUrl() + "/analytics/performance/";
+    return getFido2MetricsUrl() + "/analytics/performance";
 }

 public String getDeviceAnalyticsUrl() {
-    return getFido2MetricsUrl() + "/analytics/devices/";
+    return getFido2MetricsUrl() + "/analytics/devices";
 }

 public String getTrendAnalysisUrl() {
-    return getFido2MetricsUrl() + "/analytics/trends/";
+    return getFido2MetricsUrl() + "/analytics/trends";
 }

 public String getPeriodOverPeriodComparisonUrl() {
-    return getFido2MetricsUrl() + "/analytics/comparison/";
+    return getFido2MetricsUrl() + "/analytics/comparison";
 }

 public String getMetricsConfigUrl() {
-    return getFido2MetricsUrl() + "/config/";
+    return getFido2MetricsUrl() + "/config";
 }

 public String getMetricsHealthUrl() {
-    return getFido2MetricsUrl() + "/health/";
+    return getFido2MetricsUrl() + "/health";
 }

Also applies to: 56-58, 60-62, 64-66, 68-70, 72-74, 76-78, 80-82, 84-86, 88-90, 92-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 52 - 54, The helper methods (e.g., getFido2UserMetricsEntriesUrl,
and the other getFido2* URL helpers in Fido2MetricsService) currently return
paths with a trailing "/" which causes double slashes when callers append "/" +
variable; remove the trailing slash from these returned strings (e.g., return
getFido2MetricsUrl() + "/entries/user" instead of "/entries/user/") so callers
that concatenate "/" + id produce a single separator, and update all similar
helpers listed in the review to follow the same pattern; alternatively,
normalize by documenting/using a URI join strategy, but the minimal fix is to
strip trailing slashes in the helper return values.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (4)

1031-1039: ⚠️ Potential issue | 🟠 Major

parseDate() cannot parse date-only formats into LocalDateTime.

LocalDateTime.parse() with ISO_LOCAL_DATE or dd-MM-yyyy pattern will throw DateTimeParseException because these formatters produce LocalDate, not LocalDateTime. Parse to LocalDate first, then convert via atStartOfDay().

Proposed fix
         // Try ISO date only (e.g. 2024-02-13)
         try {
-            return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE);
+            return java.time.LocalDate.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
         } catch (DateTimeParseException ignored) {
             // continue
         }

         // Fallback to legacy dd-MM-yyyy
-        return LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT));
+        return java.time.LocalDate.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT)).atStartOfDay();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1031 - 1039, The parseDate() method currently calls
LocalDateTime.parse() with DateTimeFormatter.ISO_LOCAL_DATE and
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT), which produce LocalDate and
therefore fail; change the logic to first parse trimmed into a LocalDate when
using ISO_LOCAL_DATE or the dd-MM-yyyy pattern (METRICS_DATE_FORMAT) and then
convert it to LocalDateTime via atStartOfDay(), returning that LocalDateTime;
keep existing try/catch flow for DateTimeParseException and only fall back to
the legacy pattern if ISO parse fails, and ensure the final return type is
LocalDateTime.

134-137: ⚠️ Potential issue | 🟠 Major

Preserve HTTP status codes from WebApplicationException.

The catch-all catch (Exception ex) rewrites 400s from validateDate() and 4xx/5xx from upstream service calls into 500s. Re-throw WebApplicationException to preserve the intended status.

Proposed fix
-        } catch (Exception ex) {
+        } catch (WebApplicationException wae) {
+            throw wae;
+        } catch (Exception ex) {
             logger.error(ERR_MSG, ex);
             throwInternalServerException(ex);
         }

Add the import:

import jakarta.ws.rs.WebApplicationException;

Also applies to: 215-218, 298-301, 380-383, 457-460, 528-531, 599-602, 669-672, 741-744, 815-818, 868-871, 912-915, 955-958

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 134 - 137, In Fido2MetricsResource update the generic Exception
handlers to preserve HTTP status codes by re-throwing any caught
jakarta.ws.rs.WebApplicationException instead of wrapping it as a 500: import
jakarta.ws.rs.WebApplicationException and change the catch (Exception ex) blocks
(e.g., the ones in class Fido2MetricsResource around the logger.error(ERR_MSG,
ex); throwInternalServerException(ex); sites) to if (ex instanceof
WebApplicationException) throw (WebApplicationException) ex; else log and call
throwInternalServerException(ex); apply this same change to all similar catch
blocks listed in the review.

97-100: ⚠️ Potential issue | 🟠 Major

Remove @NotNull from optional date query parameters.

The date parameters are documented as optional and the code checks StringUtils.isNotBlank() before use. However, @NotNull triggers Bean Validation rejection before the method executes, preventing the intended optional behavior. This pattern appears across all endpoints with date parameters.

Proposed fix for getFido2MetricsEntry (apply similarly to other endpoints)
-    `@Parameter`(description = "Start date/time for "
-            + " entries report. Accepted format dd-MM-yyyy or ISO-8601 date-time like yyyy-MM-ddTHH:mm:ssZ, for example, 31-12-2025 and 2025-12-31T23:59:59Z.", schema = `@Schema`(type = "string")) `@QueryParam`(value = "start_date") `@NotNull` String startDate,
-    `@Parameter`(description = "End date/time for the log entries. Accepted format dd-MM-yyyy or ISO-8601 date-time like yyyy-MM-ddTHH:mm:ssZ, for example, 31-12-2025 and 2025-12-31T23:59:59Z.", schema = `@Schema`(type = "string")) `@QueryParam`(value = "end_date") `@NotNull` String endDate)
+    `@Parameter`(description = "Start date/time for "
+            + " entries report. Accepted format dd-MM-yyyy or ISO-8601 date-time like yyyy-MM-ddTHH:mm:ssZ, for example, 31-12-2025 and 2025-12-31T23:59:59Z.", schema = `@Schema`(type = "string")) `@QueryParam`(value = "start_date") String startDate,
+    `@Parameter`(description = "End date/time for the log entries. Accepted format dd-MM-yyyy or ISO-8601 date-time like yyyy-MM-ddTHH:mm:ssZ, for example, 31-12-2025 and 2025-12-31T23:59:59Z.", schema = `@Schema`(type = "string")) `@QueryParam`(value = "end_date") String endDate)

Also applies to: 176-178, 259-260, 342-343, 422-423, 495-496, 566-567, 637-638, 708-709, 780-781

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 97 - 100, The query parameters startDate and endDate in
Fido2MetricsResource methods (e.g., getFido2MetricsEntry) are marked `@NotNull`
but are intended to be optional and are already guarded with
StringUtils.isNotBlank(); remove the `@NotNull` annotation from the startDate and
endDate `@QueryParam` parameters (and any other date query params in the same
class) so Bean Validation does not reject null/empty values before the method
runs, leaving the existing runtime checks in place to handle blank values.

1105-1120: ⚠️ Potential issue | 🟠 Major

validateStartIndex does not guard empty lists with non-zero startIndex.

When the entries list is null or empty, validation is skipped. If startIndex > 0, the subsequent subList(startIndex, toIndex) call throws IndexOutOfBoundsException because toIndex will be 0 (clamped to list size) and fromIndex > toIndex.

Proposed fix
     private void validateStartIndex(List<?> entriesList, int startIndex) {
         if (logger.isDebugEnabled()) {
             logger.debug("Validate startIndex entriesList:{}, startIndex:{}", entriesList, escapeLog(startIndex));
         }
 
-        if (entriesList != null && !entriesList.isEmpty()) {
-            logger.debug("Validate startIndex  entriesList.size():{}", entriesList.size());
-            try {
-                entriesList.get(startIndex);
-            } catch (IndexOutOfBoundsException ioe) {
-                logger.error("Error while getting data startIndex:{}", startIndex, ioe);
-                throwBadRequestException("Page start index incorrect, total entries:{" + entriesList.size()
-                        + "}, but provided:{" + startIndex + "} ");
-            }
+        int listSize = (entriesList == null) ? 0 : entriesList.size();
+        if (startIndex < 0 || startIndex > listSize) {
+            throwBadRequestException("Page start index incorrect, total entries:{" + listSize
+                    + "}, but provided:{" + startIndex + "} ");
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1105 - 1120, The validateStartIndex method currently skips
validation when entriesList is null or empty, which allows a later
subList(fromIndex,toIndex) to fail when startIndex > 0; update
validateStartIndex (method name: validateStartIndex) to explicitly guard this
case: if startIndex > 0 and (entriesList == null || entriesList.isEmpty()) then
throwBadRequestException with a message indicating total entries:0 and the
provided startIndex, otherwise proceed with the existing bounds check (including
the existing entriesList.get(startIndex) try/catch) so empty lists with non-zero
startIndex are rejected early.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java`:
- Line 14: The SessionId class is exposing the internal boolean persisted in the
OpenAPI schema despite `@Hidden`; replace the Swagger `@Hidden` usage with Jackson
exclusion by importing com.fasterxml.jackson.annotation.JsonIgnore and annotate
the persisted field (and its getPersisted/setPersisted methods if present) with
`@JsonIgnore` so the property is omitted from JSON serialization and therefore
from the generated OpenAPI docs; update SessionId (the persisted field and its
accessor methods) accordingly or alternatively return a DTO that omits persisted
from REST endpoints like getSessionById.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 900-907: The OpenAPI parameter "periods" currently sets required:
true while also providing default: 2, which is contradictory; update the
"periods" parameter definition to be consistent by either removing required:
true if you want the default 2 to be applied when omitted, or removing default:
2 if you require clients to always supply "periods" (adjust the schema under the
parameter named "periods" accordingly).

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java`:
- Line 20: The OpenAPI definition in ApiApplication declares only "Fido2 -
Configuration" and "Fido2 - Registration" tags while Fido2MetricsResource uses
"Fido2 - Metrics"; update the `@OpenAPIDefinition.tags` on the ApiApplication
class to include a Tag entry for "Fido2 - Metrics" so the generated docs group
the metrics endpoints consistently with Fido2MetricsResource.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`:
- Around line 51-64: Update the incorrect log statements in the test method
getFido2MetricsConfig: change the log message that currently says
"getFido2Configuration()" to "getFido2MetricsConfig()" and update the
placeholder name from "fido2UserMetrics:{}" to "fido2MetricsConfig:{}" so the
debug log correctly references the method and the actual variable
fido2MetricsConfig used in the method.

---

Duplicate comments:
In `@jans-config-api/docs/jans-config-api-swagger.yaml`:
- Around line 18350-18351: The OpenAPI schema incorrectly documents the
SessionId.persisted property even though the owning model (SessionId class)
marks it `@Hidden`, `@Transient` and transient; update the source model or generator
so the property is not emitted into the generated spec: either remove/annotate
persisted for serialization (undo transient/Hidden) if it should be exposed, or
change the generator/model-to-OpenAPI mapping to skip fields annotated with
`@Hidden/`@Transient (affecting SessionId.persisted) so the swagger.yaml no longer
contains the persisted boolean.
- Around line 1855-1862: The generated Swagger shows format tokens rather than
concrete examples for the query parameters start_date and end_date; update the
upstream endpoint parameter annotations (the controller/handler parameter
annotations that generate these OpenAPI params for start_date and end_date) to
include concrete example values and/or an example field instead of format tokens
— e.g. example: "31-12-2025" for the dd-MM-yyyy form and example:
"2025-12-31T23:59:59Z" for the ISO-8601 form — so the generated
jans-config-api-swagger.yaml emits those sample values for the start_date and
end_date parameters.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 1031-1039: The parseDate() method currently calls
LocalDateTime.parse() with DateTimeFormatter.ISO_LOCAL_DATE and
DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT), which produce LocalDate and
therefore fail; change the logic to first parse trimmed into a LocalDate when
using ISO_LOCAL_DATE or the dd-MM-yyyy pattern (METRICS_DATE_FORMAT) and then
convert it to LocalDateTime via atStartOfDay(), returning that LocalDateTime;
keep existing try/catch flow for DateTimeParseException and only fall back to
the legacy pattern if ISO parse fails, and ensure the final return type is
LocalDateTime.
- Around line 134-137: In Fido2MetricsResource update the generic Exception
handlers to preserve HTTP status codes by re-throwing any caught
jakarta.ws.rs.WebApplicationException instead of wrapping it as a 500: import
jakarta.ws.rs.WebApplicationException and change the catch (Exception ex) blocks
(e.g., the ones in class Fido2MetricsResource around the logger.error(ERR_MSG,
ex); throwInternalServerException(ex); sites) to if (ex instanceof
WebApplicationException) throw (WebApplicationException) ex; else log and call
throwInternalServerException(ex); apply this same change to all similar catch
blocks listed in the review.
- Around line 97-100: The query parameters startDate and endDate in
Fido2MetricsResource methods (e.g., getFido2MetricsEntry) are marked `@NotNull`
but are intended to be optional and are already guarded with
StringUtils.isNotBlank(); remove the `@NotNull` annotation from the startDate and
endDate `@QueryParam` parameters (and any other date query params in the same
class) so Bean Validation does not reject null/empty values before the method
runs, leaving the existing runtime checks in place to handle blank values.
- Around line 1105-1120: The validateStartIndex method currently skips
validation when entriesList is null or empty, which allows a later
subList(fromIndex,toIndex) to fail when startIndex > 0; update
validateStartIndex (method name: validateStartIndex) to explicitly guard this
case: if startIndex > 0 and (entriesList == null || entriesList.isEmpty()) then
throwBadRequestException with a message indicating total entries:0 and the
provided startIndex, otherwise proceed with the existing bounds check (including
the existing entriesList.get(startIndex) try/catch) so empty lists with non-zero
startIndex are rejected early.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`:
- Around line 449-453: In Fido2MetricsService, the request builds query params
using the wrong key "startTime" so the downstream comparison endpoint never
receives the window; update the Map in the method that calls getMetricsData()
(the block returning getMetricsData(this.getPeriodOverPeriodComparisonUrl() +
"/" + aggregationType, headers, data)) to use data.put("periods",
String.valueOf(periods)) instead of "startTime", ensuring the periods parameter
name matches the upstream API.
- Around line 52-54: The helper methods (e.g., getFido2UserMetricsEntriesUrl,
and the other getFido2* URL helpers in Fido2MetricsService) currently return
paths with a trailing "/" which causes double slashes when callers append "/" +
variable; remove the trailing slash from these returned strings (e.g., return
getFido2MetricsUrl() + "/entries/user" instead of "/entries/user/") so callers
that concatenate "/" + id produce a single separator, and update all similar
helpers listed in the review to follow the same pattern; alternatively,
normalize by documenting/using a URI join strategy, but the minimal fix is to
strip trailing slashes in the helper return values.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`:
- Around line 169-180: The readList(...) methods in Jackson currently catch
IOException and wrap it in RuntimeException, losing JsonProcessingException
typing expected by callers like Fido2MetricsService; update both overloads
(readList(String, Class<T>) and readList(String, Class<? extends List>,
Class<T>)) to declare "throws JsonProcessingException" and change the catch
logic to rethrow JsonProcessingException (e.g., catch JsonProcessingException
and throw it) while keeping a broader catch for other IOExceptions (wrap or
convert those), so callers that declare throws JsonProcessingException continue
to receive the typed exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a2b93b3-ecf0-4dab-9299-311e34624780

📥 Commits

Reviewing files that changed from the base of the PR and between 78c72d2 and 4f90d10.

📒 Files selected for processing (11)
  • jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java
  • jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java
  • jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java
💤 Files with no reviewable changes (1)
  • jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java

Signed-off-by: pujavs <pujas.works@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
jans-config-api/plugins/docs/fido2-plugin-swagger.yaml (2)

900-907: ⚠️ Potential issue | 🟡 Minor

Drop either required: true or default: 2 for periods.

A required query parameter never falls back to its default, so this definition is contradictory and generators may ignore one side or the other. Keep the default only if the server applies 2 when the client omits the parameter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 900 -
907, The OpenAPI parameter definition for the query parameter named "periods" is
contradictory because it is marked required: true while also providing a
default: 2; remove one of these to resolve the conflict. If the server must
accept requests without the client sending "periods" and should use 2 in that
case, remove required: true and keep default: 2; if the parameter must always be
provided by clients, remove the default: 2 and keep required: true. Update the
parameter block for "periods" accordingly so generators and clients behave
consistently.

261-267: ⚠️ Potential issue | 🟡 Minor

Make the new startIndex contract internally consistent.

These parameter blocks still describe startIndex as 1-based while defaulting it to 0. Clients now get conflicting pagination semantics; either describe it as 0-based or change the default to 1.

Also applies to: 441-447, 537-542, 683-688

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 261 -
267, The startIndex parameter blocks are inconsistent (description says 1-based
while default is 0); update the description for the startIndex query parameter
to read "The 0-based index of the first query result" and keep the schema
default as 0 for the startIndex parameter entries (apply the same change to all
startIndex parameter blocks in the file so they are consistent).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 107-116: The example blocks currently use block-scalar strings
(value: |) which serialize the JSON as a string and conflict with the declared
JsonNode object schemas; update each example (e.g., the "Response example" under
the examples section) by replacing the block-scalar string payload with a native
YAML mapping/object that mirrors the JSON structure and types (numeric fields as
numbers, booleans as booleans, objects/arrays as YAML maps/lists) so the example
value is a real object rather than a string; apply the same replacement to all
other occurrences noted (lines referenced in the review) so OpenAPI validators
and generators see correctly-typed example values.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`:
- Line 18: Remove the unused import
com.fasterxml.jackson.core.type.TypeReference from the Jackson.java file — it’s
no longer referenced after refactoring readListValue to use
objectMapper.getTypeFactory().constructCollectionType; simply delete the import
line to clean up the class and avoid the unused-import warning.

---

Duplicate comments:
In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 900-907: The OpenAPI parameter definition for the query parameter
named "periods" is contradictory because it is marked required: true while also
providing a default: 2; remove one of these to resolve the conflict. If the
server must accept requests without the client sending "periods" and should use
2 in that case, remove required: true and keep default: 2; if the parameter must
always be provided by clients, remove the default: 2 and keep required: true.
Update the parameter block for "periods" accordingly so generators and clients
behave consistently.
- Around line 261-267: The startIndex parameter blocks are inconsistent
(description says 1-based while default is 0); update the description for the
startIndex query parameter to read "The 0-based index of the first query result"
and keep the schema default as 0 for the startIndex parameter entries (apply the
same change to all startIndex parameter blocks in the file so they are
consistent).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9a2aaa94-d2ea-4745-a231-e6e842da559d

📥 Commits

Reviewing files that changed from the base of the PR and between 4f90d10 and c984ab7.

📒 Files selected for processing (4)
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java

pujavs added 2 commits March 12, 2026 23:59
Signed-off-by: pujavs <pujas.works@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
@pujavs
Copy link
Contributor Author

pujavs commented Mar 13, 2026

@coderabbitai request you to please review again

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

@pujavs Sure! I'll kick off a full review of the PR right away.


🧠 Learnings used
Learnt from: moabu
Repo: JanssenProject/jans PR: 13060
File: CHANGELOG.md:25-26
Timestamp: 2026-01-22T14:33:53.554Z
Learning: Repo: JanssenProject/jans — For Release Please-generated PRs (chore(main): release …), CHANGELOG.md is auto-generated; avoid suggesting manual edits (e.g., duplicate/formatting cleanups) and ignore minor nits in that file.

Learnt from: pujavs
Repo: JanssenProject/jans PR: 13448
File: jans-config-api/plugins/docs/fido2-plugin-swagger.yaml:1823-1826
Timestamp: 2026-03-11T12:57:40.247Z
Learning: In the JanssenProject/jans repository, the `metricsData` property within the `Fido2MetricsAggregation` schema in `jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` is a dependency object that is out of scope of the jans-config-api module. Issues about its schema definition (e.g., `additionalProperties` type restrictions) should not be flagged during reviews of jans-config-api.

Learnt from: pujavs
Repo: JanssenProject/jans PR: 13448
File: jans-config-api/plugins/docs/fido2-plugin-swagger.yaml:262-268
Timestamp: 2026-03-12T15:12:20.706Z
Learning: In the JanssenProject/jans repository, the `aggregationType` path parameter schema (including `default: ""`) in the FIDO2 metrics endpoint definitions within `jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` is a dependency object from an upstream component (jans-fido2) and is out of scope of jans-config-api. Issues about its schema definition (e.g., empty string defaults on required path parameters) should not be flagged during reviews of jans-config-api.

Learnt from: pujavs
Repo: JanssenProject/jans PR: 12847
File: jans-config-api/docs/jans-config-api-swagger.yaml:15497-15498
Timestamp: 2025-12-17T13:07:08.750Z
Learning: When reviewing components like `jans-auth`, `jans-fido2`, `jans-lock` in the JanssenProject/jans repository, raise issues about missing property descriptions in API model classes, as these descriptions will be reflected in the auto-generated Swagger specifications.
✅ Actions performed

Full review triggered.

@sonarqubecloud
Copy link

Quality Gate Passed Quality Gate passed for 'Jans authentication server parent'

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (7)
jans-config-api/docs/jans-config-api-swagger.yaml (1)

1855-1862: ⚠️ Potential issue | 🟡 Minor

Use concrete sample values in date parameter descriptions.

Line 1855 and Line 1861 still present format tokens (dd-MM-yyyy, yyyy-MM-ddTHH:mm:ssZ) as examples, which remains ambiguous for API consumers. Please publish concrete examples in the upstream endpoint annotations (then regenerate this YAML), e.g., 31-12-2025 and 2025-12-31T23:59:59Z.

Based on learnings, jans-config-api/docs/jans-config-api-swagger.yaml is auto-generated, so this should be fixed in source annotations and regenerated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/docs/jans-config-api-swagger.yaml` around lines 1855 - 1862,
Update the source API endpoint annotations that generate the start_date and
end_date query parameter docs so they use concrete example values instead of
format tokens: replace the placeholders `dd-MM-yyyy` and `yyyy-MM-ddTHH:mm:ssZ`
for the parameters named start_date and end_date with explicit examples (e.g.,
start_date: "31-12-2025" and start_date/end_date ISO example:
"2025-12-31T23:59:59Z") in the annotation strings, then regenerate the swagger
YAML so jans-config-api/docs/jans-config-api-swagger.yaml reflects those
concrete examples.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (5)

97-100: ⚠️ Potential issue | 🟠 Major

Remove @NotNull from optional date query params.

start_date/end_date are handled as optional in method logic (StringUtils.isNotBlank) but @NotNull makes them required before handler execution.

Suggested fix
- `@QueryParam`(value = "start_date") `@NotNull` String startDate,
- `@QueryParam`(value = "end_date") `@NotNull` String endDate)
+ `@QueryParam`(value = "start_date") String startDate,
+ `@QueryParam`(value = "end_date") String endDate)

Also applies to: 176-178, 258-260, 342-343, 422-423, 495-496, 566-567, 637-638, 708-709, 780-781

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 97 - 100, The request parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` but treated as optional
(checked via StringUtils.isNotBlank), so remove the `@NotNull` annotation from the
start_date/end_date `@QueryParam` declarations across the class (e.g., in the
method signatures that declare startDate and endDate in Fido2MetricsResource)
and for the other duplicate occurrences noted (the additional method parameter
declarations referenced in the comment). Ensure only the `@QueryParam/`@Parameter
annotations remain so the parameters are truly optional at runtime.

1031-1039: ⚠️ Potential issue | 🟠 Major

parseDate() cannot parse date-only strings into LocalDateTime.

ISO_LOCAL_DATE and dd-MM-yyyy are date-only formats; parsing them directly as LocalDateTime throws DateTimeParseException.

Suggested fix
+import java.time.LocalDate;
@@
-        // Try ISO date only (e.g. 2024-02-13)
+        // Try ISO date only (e.g. 2024-02-13)
         try {
-            return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE);
+            return LocalDate.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
         } catch (DateTimeParseException ignored) {
             // continue
         }
 
         // Fallback to legacy dd-MM-yyyy
-        return LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT));
+        return LocalDate.parse(trimmed, fallbackFormatter).atStartOfDay();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1031 - 1039, The parseDate() logic attempts to parse date-only
strings with DateTimeFormatter.ISO_LOCAL_DATE and the dd-MM-yyyy pattern
directly into LocalDateTime, which fails; update the ISO_LOCAL_DATE and
METRICS_DATE_FORMAT branches to first parse into LocalDate (e.g.,
LocalDate.parse(trimmed, ...)) and then convert to LocalDateTime by calling
atStartOfDay() before returning, keeping the method return type LocalDateTime
and retaining the final fallback LocalDateTime.parse usage for full date-time
strings.

181-185: ⚠️ Potential issue | 🟠 Major

Avoid logging raw userId at info level.

This logs caller-provided identifier data into standard logs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 181 - 185, The info-level log in Fido2MetricsResource (logger.info
call that currently includes userId) is logging a caller-provided identifier;
remove or redact the raw userId from that info log to avoid sensitive data
exposure—locate the logger.info in Fido2MetricsResource (the "Fido2MetricsEntry
for user search param ..." statement) and either omit userId from the parameter
list or replace it with a masked/hashed value (or move full userId logging to a
debug-level log after applying a redaction function) while keeping other fields
and using escapeLog as needed.

1048-1058: ⚠️ Potential issue | 🟠 Major

Paging bounds are still unsafe and can throw on empty/out-of-range inputs.

validateStartIndex() skips empty lists, and subList(startIndex, toIndex) can still execute with invalid bounds (e.g., empty list + non-zero startIndex). Also, validation via entriesList.get(startIndex) rejects startIndex == size, which should be valid for an empty page window.

Suggested fix
-            validateStartIndex(identityProviderList, startIndex);
-            int toIndex = (startIndex + limit <= listSize) ? startIndex + limit : listSize;
+            startIndex = validateStartIndex(identityProviderList, startIndex);
+            int safeLimit = Math.max(0, limit);
+            int toIndex = Math.min(startIndex + safeLimit, listSize);
@@
-            List<Fido2MetricsEntry> sublist = (identityProviderList == null) ? null
-                    : identityProviderList.subList(startIndex, toIndex);
+            List<Fido2MetricsEntry> sublist = (identityProviderList == null || startIndex >= toIndex)
+                    ? Collections.emptyList()
+                    : identityProviderList.subList(startIndex, toIndex);
@@
-    private void validateStartIndex(List<?> entriesList, int startIndex) {
+    private int validateStartIndex(List<?> entriesList, int startIndex) {
@@
-        if (entriesList != null && !entriesList.isEmpty()) {
-            logger.debug("Validate startIndex  entriesList.size():{}", entriesList.size());
-            try {
-                entriesList.get(startIndex);
-            } catch (IndexOutOfBoundsException ioe) {
-                logger.error("Error while getting data startIndex:{}", startIndex, ioe);
-                throwBadRequestException("Page start index incorrect, total entries:{" + entriesList.size()
-                        + "}, but provided:{" + startIndex + "} ");
-            }
+        int size = entriesList == null ? 0 : entriesList.size();
+        if (startIndex < 0 || startIndex > size) {
+            throwBadRequestException("Page start index incorrect, total entries:{" + size
+                    + "}, but provided:{" + startIndex + "} ");
         }
+        return startIndex;
     }

Also applies to: 1079-1089, 1105-1118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1048 - 1058, The paging code can still throw IndexOutOfBounds for
empty or boundary cases; update validateStartIndex and the pagination logic in
Fido2MetricsResource so it treats a null/empty identityProviderList as returning
an empty page and allows startIndex == listSize (empty page) but rejects
startIndex > listSize, compute toIndex = Math.min(startIndex + limit, listSize)
after that check, and only call identityProviderList.subList(startIndex,
toIndex) when identityProviderList is non-null and startIndex <= toIndex;
reference validateStartIndex(...), identityProviderList, startIndex, limit,
toIndex, and the subList call to locate and fix the logic (apply same fix for
the other blocks around the class).

134-137: ⚠️ Potential issue | 🟠 Major

Preserve upstream/client HTTP statuses instead of rewriting all errors to 500.

The catch-all Exception path masks WebApplicationException (e.g., validation 400 and upstream 4xx/5xx) and always returns 500.

Suggested fix
-        } catch (Exception ex) {
+        } catch (WebApplicationException ex) {
+            throw ex;
+        } catch (Exception ex) {
             logger.error(ERR_MSG, ex);
             throwInternalServerException(ex);
         }

Also applies to: 215-218, 298-301, 380-383, 457-460, 528-531, 599-602, 670-672, 741-744, 815-818, 868-871, 912-915, 955-958

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 134 - 137, The generic catch blocks in Fido2MetricsResource
currently convert all exceptions to a 500 via throwInternalServerException;
update each catch (Exception ex) block to first check if ex is an instance of
javax.ws.rs.WebApplicationException and, if so, rethrow it (throw
(WebApplicationException) ex) so the original HTTP status and response are
preserved; otherwise log the error as before and call
throwInternalServerException(ex). Apply this change to all catch blocks in the
Fido2MetricsResource class (the blocks using logger.error(ERR_MSG, ex) and
throwInternalServerException(ex)).
jans-config-api/plugins/docs/fido2-plugin-swagger.yaml (1)

262-268: ⚠️ Potential issue | 🟡 Minor

Make startIndex semantics consistent (description vs default).

These parameters are documented as 1-based while default remains 0. Align by either changing description to 0-based or default to 1.

Also applies to: 442-448, 537-543, 683-689

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml` around lines 262 -
268, The startIndex query parameter is documented as 1-based but has a default
of 0; make the semantics consistent by updating the schema default to 1 for
every parameter block named startIndex (replace schema.default: 0 with
schema.default: 1) so the description ("The 1-based index of the first query
result") matches the default; apply the same fix to all other startIndex
parameter occurrences in the file (all blocks with name: startIndex).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 49-50: The PARAM log template in Fido2MetricsResource currently
has four placeholders but the logging calls only pass two values; either update
the PARAM constant to match the actual arguments (e.g., change to "Search param
- limit:{}, startIndex:{}") or update each logging call to supply the missing
startDate and endDate values; locate the PARAM constant and all uses in
Fido2MetricsResource (the logging statements around where metrics queries are
built — the same spots noted in the review) and make the template and arguments
consistent so the number of {} placeholders matches the number of provided
values.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`:
- Around line 21-64: Add tests to cover the newly added metrics handlers and
non-200 scenarios in addition to the existing happy-paths: extend the test class
by creating new parameterized test methods (mirroring getFido2MetricsEntry,
getFido2UserMetrics, getFido2MetricsConfig) that call each new endpoint handler
added in the PR, and add negative-case tests that simulate and assert
401/403/404/500 responses; reuse the same Builder flow
(getResteasyService().getClientBuilder(...), set AUTHORIZATION and CONTENT_TYPE
headers, call request.get()) but assert non-OK Status codes and validate error
payloads or headers, and add tests for empty/invalid input returns where
applicable to ensure parity and regression safety.
- Around line 30-33: The Response returned by request.get() in the
Fido2MetricsTest tests (e.g., the getFido2MetricsEntry() test where you call
Response response = request.get()) isn’t closed; wrap the request.get() call in
a try-with-resources (try (Response response = request.get()) { ... }) in
getFido2MetricsEntry() and the two other test methods that call request.get() so
the jakarta.ws.rs.core.Response is automatically closed and avoids test-time
resource leaks.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`:
- Around line 159-177: The three deserialization helpers (readStringValue,
readListValue, readList) currently instantiate new ObjectMapper() without
configuration; update them to use the same configured mapper as the rest of the
class (i.e., an ObjectMapper with
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES disabled or the existing
configured/shared mapper used by applyPatch/getObject) so unknown properties are
ignored during reads; modify the mapper instantiation in readStringValue,
readListValue, readList (and the overloaded readList with elementType) to
obtain/configure the mapper consistently rather than calling new ObjectMapper()
directly.

---

Duplicate comments:
In `@jans-config-api/docs/jans-config-api-swagger.yaml`:
- Around line 1855-1862: Update the source API endpoint annotations that
generate the start_date and end_date query parameter docs so they use concrete
example values instead of format tokens: replace the placeholders `dd-MM-yyyy`
and `yyyy-MM-ddTHH:mm:ssZ` for the parameters named start_date and end_date with
explicit examples (e.g., start_date: "31-12-2025" and start_date/end_date ISO
example: "2025-12-31T23:59:59Z") in the annotation strings, then regenerate the
swagger YAML so jans-config-api/docs/jans-config-api-swagger.yaml reflects those
concrete examples.

In `@jans-config-api/plugins/docs/fido2-plugin-swagger.yaml`:
- Around line 262-268: The startIndex query parameter is documented as 1-based
but has a default of 0; make the semantics consistent by updating the schema
default to 1 for every parameter block named startIndex (replace schema.default:
0 with schema.default: 1) so the description ("The 1-based index of the first
query result") matches the default; apply the same fix to all other startIndex
parameter occurrences in the file (all blocks with name: startIndex).

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 97-100: The request parameters startDate and endDate in
Fido2MetricsResource are annotated with `@NotNull` but treated as optional
(checked via StringUtils.isNotBlank), so remove the `@NotNull` annotation from the
start_date/end_date `@QueryParam` declarations across the class (e.g., in the
method signatures that declare startDate and endDate in Fido2MetricsResource)
and for the other duplicate occurrences noted (the additional method parameter
declarations referenced in the comment). Ensure only the `@QueryParam/`@Parameter
annotations remain so the parameters are truly optional at runtime.
- Around line 1031-1039: The parseDate() logic attempts to parse date-only
strings with DateTimeFormatter.ISO_LOCAL_DATE and the dd-MM-yyyy pattern
directly into LocalDateTime, which fails; update the ISO_LOCAL_DATE and
METRICS_DATE_FORMAT branches to first parse into LocalDate (e.g.,
LocalDate.parse(trimmed, ...)) and then convert to LocalDateTime by calling
atStartOfDay() before returning, keeping the method return type LocalDateTime
and retaining the final fallback LocalDateTime.parse usage for full date-time
strings.
- Around line 181-185: The info-level log in Fido2MetricsResource (logger.info
call that currently includes userId) is logging a caller-provided identifier;
remove or redact the raw userId from that info log to avoid sensitive data
exposure—locate the logger.info in Fido2MetricsResource (the "Fido2MetricsEntry
for user search param ..." statement) and either omit userId from the parameter
list or replace it with a masked/hashed value (or move full userId logging to a
debug-level log after applying a redaction function) while keeping other fields
and using escapeLog as needed.
- Around line 1048-1058: The paging code can still throw IndexOutOfBounds for
empty or boundary cases; update validateStartIndex and the pagination logic in
Fido2MetricsResource so it treats a null/empty identityProviderList as returning
an empty page and allows startIndex == listSize (empty page) but rejects
startIndex > listSize, compute toIndex = Math.min(startIndex + limit, listSize)
after that check, and only call identityProviderList.subList(startIndex,
toIndex) when identityProviderList is non-null and startIndex <= toIndex;
reference validateStartIndex(...), identityProviderList, startIndex, limit,
toIndex, and the subList call to locate and fix the logic (apply same fix for
the other blocks around the class).
- Around line 134-137: The generic catch blocks in Fido2MetricsResource
currently convert all exceptions to a 500 via throwInternalServerException;
update each catch (Exception ex) block to first check if ex is an instance of
javax.ws.rs.WebApplicationException and, if so, rethrow it (throw
(WebApplicationException) ex) so the original HTTP status and response are
preserved; otherwise log the error as before and call
throwInternalServerException(ex). Apply this change to all catch blocks in the
Fido2MetricsResource class (the blocks using logger.error(ERR_MSG, ex) and
throwInternalServerException(ex)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0170b9db-ae20-4248-a930-5a3b07bc7c13

📥 Commits

Reviewing files that changed from the base of the PR and between c984ab7 and dd72fe1.

📒 Files selected for processing (7)
  • jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java

Comment on lines +49 to +50
private static final String PARAM = "Search param - limit:{}, startIndex:{}, startDate:{}, endDate:{}";
private static final String ERR_MSG = "Exception while getting metric data is - ";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix log template/argument mismatch for PARAM.

PARAM expects 4 placeholders, but these calls provide only 2 values, which produces misleading logs.

Also applies to: 499-501, 570-572, 641-643, 712-714

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 49 - 50, The PARAM log template in Fido2MetricsResource currently
has four placeholders but the logging calls only pass two values; either update
the PARAM constant to match the actual arguments (e.g., change to "Search param
- limit:{}, startIndex:{}") or update each logging call to supply the missing
startDate and endDate values; locate the PARAM constant and all uses in
Fido2MetricsResource (the logging statements around where metrics queries are
built — the same spots noted in the review) and make the template and arguments
consistent so the number of {} placeholders matches the number of provided
values.

Comment on lines +21 to +64
@Parameters({ "test.issuer", "fido2MetricsUrl" , "fido2MetricsEntries"})
@Test
public void getFido2MetricsEntry(final String issuer, final String fido2MetricsUrl, final String fido2MetricsEntries) {
log.debug(" getFido2MetricsEntry() - issuer:{}, fido2MetricsUrl:{}, fido2MetricsEntries:{}", issuer, fido2MetricsUrl, fido2MetricsEntries);

Builder request = getResteasyService().getClientBuilder(issuer + fido2MetricsUrl + fido2MetricsEntries);
request.header(AUTHORIZATION, AUTHORIZATION_TYPE + " " + accessToken);
request.header(CONTENT_TYPE, MediaType.APPLICATION_JSON);

Response response = request.get();
log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
assertEquals(response.getStatus(), Status.OK.getStatusCode());

}

@Parameters({ "test.issuer", "fido2MetricsUrl" , "fido2UserMetrics"})
@Test
public void getFido2UserMetrics(final String issuer, final String fido2MetricsUrl, final String fido2UserMetrics) {
log.debug("\n\n getFido2UserMetrics() - issuer:{}, fido2MetricsUrl:{}, fido2UserMetrics:{}", issuer, fido2MetricsUrl, fido2UserMetrics);

Builder request = getResteasyService().getClientBuilder(issuer + fido2MetricsUrl + fido2UserMetrics);
request.header(AUTHORIZATION, AUTHORIZATION_TYPE + " " + accessToken);
request.header(CONTENT_TYPE, MediaType.APPLICATION_JSON);

Response response = request.get();
log.info("getFido2UserMetrics() - response:{}, response.getStatus():{}", response, response.getStatus());
assertEquals(response.getStatus(), Status.OK.getStatusCode());

}

@Parameters({ "test.issuer", "fido2MetricsUrl" , "fido2MetricsConfig"})
@Test
public void getFido2MetricsConfig(final String issuer, final String fido2MetricsUrl, final String fido2MetricsConfig) {
log.debug("getFido2MetricsConfig() - issuer:{}, fido2MetricsUrl:{}, fido2MetricsConfig:{}", issuer, fido2MetricsUrl, fido2MetricsConfig);

Builder request = getResteasyService().getClientBuilder(issuer + fido2MetricsUrl + fido2MetricsConfig);
request.header(AUTHORIZATION, AUTHORIZATION_TYPE + " " + accessToken);
request.header(CONTENT_TYPE, MediaType.APPLICATION_JSON);

Response response = request.get();
log.info("getFido2MetricsConfig() - response:{}, response.getStatus():{}", response, response.getStatus());
assertEquals(response.getStatus(), Status.OK.getStatusCode());

}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Expand tests for the newly added metrics endpoints and non-200 scenarios.

Current coverage validates only 3 happy-path endpoints; this PR adds many additional handlers that should be exercised for parity and regression safety.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`
around lines 21 - 64, Add tests to cover the newly added metrics handlers and
non-200 scenarios in addition to the existing happy-paths: extend the test class
by creating new parameterized test methods (mirroring getFido2MetricsEntry,
getFido2UserMetrics, getFido2MetricsConfig) that call each new endpoint handler
added in the PR, and add negative-case tests that simulate and assert
401/403/404/500 responses; reuse the same Builder flow
(getResteasyService().getClientBuilder(...), set AUTHORIZATION and CONTENT_TYPE
headers, call request.get()) but assert non-OK Status codes and validate error
payloads or headers, and add tests for empty/invalid input returns where
applicable to ensure parity and regression safety.

Comment on lines +30 to +33
Response response = request.get();
log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
assertEquals(response.getStatus(), Status.OK.getStatusCode());

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and check the file structure
find . -path "*/jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java" -type f

Repository: JanssenProject/jans

Length of output: 173


🏁 Script executed:

# Check the file content around the mentioned lines
if [ -f "jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java" ]; then
  echo "=== File exists, checking line count ==="
  wc -l "jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java"
  echo ""
  echo "=== Lines 1-70 with content ==="
  head -70 "jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java" | cat -n
fi

Repository: JanssenProject/jans

Length of output: 3915


🏁 Script executed:

# Check imports to see what Response class is being used
rg "import.*Response" "jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java"

Repository: JanssenProject/jans

Length of output: 141


🏁 Script executed:

# Check if there are other tests in the codebase that show how Response is handled
rg -A 2 "Response response = request\.(get|post|put|delete)" \
  "jans-config-api/plugins/fido2-plugin/src/test" \
  -c

Repository: JanssenProject/jans

Length of output: 285


🏁 Script executed:

# Let's look for patterns of Response handling across the test suite
rg "try.*Response" "jans-config-api/plugins/fido2-plugin/src/test" | head -20

Repository: JanssenProject/jans

Length of output: 45


🏁 Script executed:

# Check base test class for any resource management patterns
find "jans-config-api/plugins/fido2-plugin/src/test" -name "*BaseTest*" -type f

Repository: JanssenProject/jans

Length of output: 163


🏁 Script executed:

# Let's also check if Response.close() is called anywhere in tests
rg "response\.close\(\)" "jans-config-api/plugins/fido2-plugin/src/test"

Repository: JanssenProject/jans

Length of output: 45


🏁 Script executed:

# Check the base test class
cat "jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/Fido2BaseTest.java" | head -100

Repository: JanssenProject/jans

Length of output: 1176


🏁 Script executed:

# Search for how Response is typically used in other test files
rg "Response response" "jans-config-api/plugins/fido2-plugin/src/test" -B 2 -A 5 | head -80

Repository: JanssenProject/jans

Length of output: 4872


🌐 Web query:

jakarta.ws.rs.core.Response AutoCloseable close resource management

💡 Result:

jakarta.ws.rs.core.Response is AutoCloseable, so you should manage it like any other closeable resource—typically with try-with-resources—to ensure the underlying entity input stream / HTTP connection gets released back to the client runtime/pool. The JAX-RS Response.close() contract also makes further entity access illegal after closing. [1]

Recommended pattern (client side)

try (Response r = target.request().get()) {        // Response is AutoCloseable
  if (r.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
    String body = r.readEntity(String.class);      // usually consumes + closes entity stream
  }
} // r.close() called automatically

Try-with-resources applies because the resource implements AutoCloseable. [2]

When is close() especially important?

  • If you don’t fully consume the entity stream, you should still close the Response to avoid leaking the connection/resources. Many guides explicitly state that Response.close() must be used to release the connection when the invocation returns a Response. [3]
  • If you call readEntity(InputStream.class) (or otherwise take ownership of a stream), you must close the stream/response yourself; otherwise the connection may remain held. (By contrast, for non-stream entity types, readEntity(...) typically consumes the stream and “automatically closes” the unconsumed original entity data stream.) [1]

Server side note

On the server, when you return a Response from a resource method, the JAX-RS runtime handles writing the entity; you generally don’t manually close that Response instance—this guidance is mainly about client-side Response objects.

Sources: Jakarta Response Javadoc (close/readEntity behavior) [1]; Java try-with-resources / AutoCloseable [2]; Red Hat JAX-RS client guidance on Response.close() releasing the connection [3].


Close Response objects to avoid test-time resource leaks.

All three test methods in this file obtain Response objects via request.get() without releasing them. Since jakarta.ws.rs.core.Response implements AutoCloseable, use try-with-resources to ensure proper resource cleanup.

Suggested fix (applies to all 3 test methods)
-        Response response = request.get();
-        log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
-        assertEquals(response.getStatus(), Status.OK.getStatusCode());
+        try (Response response = request.get()) {
+            log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
+            assertEquals(response.getStatus(), Status.OK.getStatusCode());
+        }

Also applies to: lines 45–48, 60–63

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`
around lines 30 - 33, The Response returned by request.get() in the
Fido2MetricsTest tests (e.g., the getFido2MetricsEntry() test where you call
Response response = request.get()) isn’t closed; wrap the request.get() call in
a try-with-resources (try (Response response = request.get()) { ... }) in
getFido2MetricsEntry() and the two other test methods that call request.get() so
the jakarta.ws.rs.core.Response is automatically closed and avoids test-time
resource leaks.

Comment on lines +159 to +177
public static <T> T readStringValue(String content, Class<T> clazz) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(content, clazz);
}

public static <T> List<T> readListValue(String content, Class<T> clazz) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
}

public static <T> List<T> readList(String str, Class<T> type) throws JsonProcessingException{
return readList(str, ArrayList.class, type);
}

public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType)
throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(str,
mapper.getTypeFactory().constructCollectionType(type.asSubclass(Collection.class), elementType));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "Jackson.java" | grep -E "jans-config-api/shared"

Repository: JanssenProject/jans

Length of output: 142


🏁 Script executed:

cat -n jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java

Repository: JanssenProject/jans

Length of output: 10346


🏁 Script executed:

# Also search for JacksonUtils to understand the newMapper() method
find . -type f -name "JacksonUtils.java"

Repository: JanssenProject/jans

Length of output: 45


🏁 Script executed:

# Search for usages of the reviewed methods
rg -i "readStringValue|readListValue|readList" --type java -B 2 -A 2 | head -100

Repository: JanssenProject/jans

Length of output: 6756


🏁 Script executed:

# Also check if there are any test cases or comments about unknown properties handling
rg -i "unknown.*propert|fail_on_unknown" --type java -B 2 -A 2 | head -100

Repository: JanssenProject/jans

Length of output: 5379


Use the configured mapper in these deserialization helpers to handle payloads with unknown properties.

Methods readStringValue, readListValue, and readList use new ObjectMapper() without configuration, unlike similar methods in this class (applyPatch and getObject) that configure FAIL_ON_UNKNOWN_PROPERTIES=false. The codebase consistently disables this feature across deserialization operations to tolerate upstream schema evolution. These methods should follow the same pattern to prevent parse failures.

Suggested fix
+    private static ObjectMapper readMapper() {
+        return JacksonUtils.newMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
     public static <T> T readStringValue(String content, Class<T> clazz) throws JsonProcessingException {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = readMapper();
         return mapper.readValue(content, clazz);
     }

     public static <T> List<T> readListValue(String content, Class<T> clazz) throws JsonProcessingException {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = readMapper();
         return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
     }

     public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType)
             throws JsonProcessingException {
-        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectMapper mapper = readMapper();
         return mapper.readValue(str,
                 mapper.getTypeFactory().constructCollectionType(type.asSubclass(Collection.class), elementType));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`
around lines 159 - 177, The three deserialization helpers (readStringValue,
readListValue, readList) currently instantiate new ObjectMapper() without
configuration; update them to use the same configured mapper as the rest of the
class (i.e., an ObjectMapper with
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES disabled or the existing
configured/shared mapper used by applyPatch/getObject) so unknown properties are
ignored during reads; modify the mapper instantiation in readStringValue,
readListValue, readList (and the overloaded readList with elementType) to
obtain/configure the mapper consistently rather than calling new ObjectMapper()
directly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (14)
jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java (1)

24-24: ⚠️ Potential issue | 🟠 Major

Remove bearer token from logs (security leak).

Line 24 logs accessToken in plaintext and at error level. This can leak live credentials through CI/test logs. Remove or redact the token and lower log level for routine tracing.

Suggested fix
-        log.error("\n\n getFido2Configuration() - accessToken:{}, issuer:{}, fido2ConfigUrl:{}", accessToken, issuer, fido2ConfigUrl);
+        log.info("\n\n getFido2Configuration() - issuer:{}, fido2ConfigUrl:{}", issuer, fido2ConfigUrl);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java`
at line 24, The test currently logs the raw bearer token at error level in
Fido2ResourceTest (see the log.error call in getFido2Configuration() test);
remove the plaintext accessToken from the message (or replace it with a redacted
placeholder like "<REDACTED_TOKEN>") and lower the severity from error to a less
noisy level (debug or info) so credentials are not exposed in CI logs; update
the log statement in Fido2ResourceTest.java accordingly while keeping issuer and
fido2ConfigUrl as needed.
jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java (1)

159-177: ⚠️ Potential issue | 🟠 Major

Use the same configured mapper in new deserialization helpers.

Line 160, Line 165, and Line 175 create raw ObjectMapper instances, while this class already uses tolerant deserialization (FAIL_ON_UNKNOWN_PROPERTIES=false) elsewhere. This inconsistency can break reads on forward-compatible payloads.

🔧 Proposed refactor
+    private static ObjectMapper readMapper() {
+        return JacksonUtils.newMapper()
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
     public static <T> T readStringValue(String content, Class<T> clazz) throws JsonProcessingException {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = readMapper();
         return mapper.readValue(content, clazz);
     }

     public static <T> List<T> readListValue(String content, Class<T> clazz) throws JsonProcessingException {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = readMapper();
         return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
     }
@@
     public static <T> List<T> readList(String str, Class<? extends List> type, Class<T> elementType)
             throws JsonProcessingException {
-        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectMapper mapper = readMapper();
         return mapper.readValue(str,
                 mapper.getTypeFactory().constructCollectionType(type.asSubclass(Collection.class), elementType));
     }
#!/bin/bash
set -euo pipefail

echo "Verify mapper construction in Jackson helper methods:"
fd 'Jackson.java' | rg 'jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java' | while read -r f; do
  sed -n '150,190p' "$f"
done

echo
echo "Find all direct ObjectMapper constructions in this utility:"
rg -n --type java 'new ObjectMapper\(' -C2

echo
echo "Find call sites that depend on these helpers:"
rg -n --type java 'Jackson\.readStringValue\(|Jackson\.readListValue\(|Jackson\.readList\(' -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`
around lines 159 - 177, The three helpers readStringValue, readListValue and
both readList overloads currently instantiate raw ObjectMapper() instances which
bypass the class's configured tolerant mapper; replace each new ObjectMapper()
with the shared/configured mapper used elsewhere in this Jackson utility (e.g.,
the static mapper instance or a getMapper() method), and ensure the same
TypeFactory calls (mapper.getTypeFactory().constructCollectionType(...)) and
deserialization features (FAIL_ON_UNKNOWN_PROPERTIES=false) are applied so all
deserialization uses the consistent, tolerant configuration.
jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml (1)

18-22: ⚠️ Potential issue | 🟡 Minor

Rename duplicate TestNG test block for clarity.

Both <test> elements are named Fido2 (lines 12 and 18), which creates ambiguity in TestNG reports and name-based filtering. Rename this block to match its test class.

🔧 Suggested fix
-	<test name="Fido2" enabled="true">
+	<test name="Fido2Metrics" enabled="true">
         <classes>
             <class name="io.jans.configapi.plugin.fido2.test.Fido2MetricsTest" />
         </classes>
     </test>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml` around
lines 18 - 22, Rename the duplicate TestNG <test> block named "Fido2" to a
unique, descriptive name matching its test class; update the test element that
contains the class io.jans.configapi.plugin.fido2.test.Fido2MetricsTest so its
name attribute is e.g. "Fido2MetricsTest" (or similar unique identifier) to
avoid ambiguity in TestNG reports and name-based filtering.
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java (6)

183-185: ⚠️ Potential issue | 🟠 Major

Avoid logging raw userId at info level.

Line 183-185 records caller user identifiers in standard logs; prefer redaction/hash or debug-only logging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 183 - 185, The info-level log in Fido2MetricsResource that prints
"Fido2MetricsEntry for user search param..." currently includes the raw userId
variable; change this to avoid logging identifiable user IDs at info level by
either substituting a redacted or hashed value for userId (e.g., mask or hash
before passing to the logger) or by moving the full userId logging to
debug-only; update the logging call that passes escapeLog(userId) (alongside
escapeLog(limit), escapeLog(startIndex), escapeLog(startDate),
escapeLog(endDate)) to use the redacted/hashed value or conditionally log the
raw userId at debug level instead.

49-50: ⚠️ Potential issue | 🟡 Minor

Fix PARAM placeholder/argument mismatch.

Line 49 defines four placeholders, but these calls pass only two values.

Also applies to: 499-501, 570-572, 641-643, 712-714

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 49 - 50, The PARAM log template in Fido2MetricsResource currently
has four placeholders but calling sites supply only two arguments; update the
PARAM constant (and any duplicate constants at the other occurrences) to match
the actual arguments — e.g., change PARAM to "Search param - limit:{},
startIndex:{}" (or alternatively update the calling sites to pass startDate and
endDate as the 3rd and 4th args) so the placeholder/argument counts match; keep
ERR_MSG unchanged. Ensure you update the PARAM constant used by methods that log
search parameters so no placeholder mismatch remains.

1051-1053: ⚠️ Potential issue | 🟠 Major

Pagination still applies startIndex with wrong index base.

startIndex is documented 1-based, but subList/validation treat it as 0-based, producing off-by-one failures and incorrect page windows.

♻️ Suggested change
-            validateStartIndex(identityProviderList, startIndex);
-            int toIndex = (startIndex + limit <= listSize) ? startIndex + limit : listSize;
+            int zeroBasedStart = validateStartIndex(identityProviderList, startIndex);
+            int toIndex = Math.min(zeroBasedStart + limit, listSize);
@@
-            List<Fido2MetricsEntry> sublist = (identityProviderList == null) ? null
-                    : identityProviderList.subList(startIndex, toIndex);
+            List<Fido2MetricsEntry> sublist = (identityProviderList == null) ? null
+                    : identityProviderList.subList(zeroBasedStart, toIndex);
@@
-            fido2MetricsEntryPagedResult.setStart(startIndex);
+            fido2MetricsEntryPagedResult.setStart(startIndex);
@@
-    private void validateStartIndex(List<?> entriesList, int startIndex) {
+    private int validateStartIndex(List<?> entriesList, int startIndex) {
@@
-        if (entriesList != null && !entriesList.isEmpty()) {
-            logger.debug("Validate startIndex  entriesList.size():{}", entriesList.size());
-            try {
-                entriesList.get(startIndex);
-            } catch (IndexOutOfBoundsException ioe) {
-                logger.error("Error while getting data startIndex:{}", startIndex, ioe);
-                throwBadRequestException("Page start index incorrect, total entries:{" + entriesList.size()
-                        + "}, but provided:{" + startIndex + "} ");
-            }
+        int listSize = entriesList == null ? 0 : entriesList.size();
+        if (startIndex < 1 || startIndex > Math.max(1, listSize)) {
+            throwBadRequestException("Page start index incorrect, total entries:{" + listSize
+                    + "}, but provided:{" + startIndex + "} ");
         }
+        return startIndex - 1;
     }

Also applies to: 1057-1057, 1082-1084, 1088-1088, 1110-1117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1051 - 1053, The pagination logic treats the documented 1-based
startIndex as 0-based when validating and slicing lists (see validateStartIndex,
calculation of toIndex, and subList usages), causing off-by-one windows; update
all places (including the blocks around validateStartIndex, toIndex calculation
and any list.subList(...) calls at the mentioned locations) to convert the
incoming 1-based startIndex to a 0-based internal index (e.g., internalIndex =
startIndex - 1) before validation and slicing, ensure boundary checks use list
size correctly, and log/return values consistently using the external 1-based
startIndex where required.

98-99: ⚠️ Potential issue | 🟠 Major

Remove @NotNull from optional start_date/end_date query params.

Line 98/99 (and repeats) conflict with the documented optional behavior and the existing blank checks.

♻️ Suggested change
-@QueryParam(value = "start_date") `@NotNull` String startDate,
-@QueryParam(value = "end_date") `@NotNull` String endDate
+@QueryParam(value = "start_date") String startDate,
+@QueryParam(value = "end_date") String endDate

Apply across all metrics handlers.

#!/bin/bash
rg -n "@NotNull String startDate|@NotNull String endDate" jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java

Also applies to: 177-178, 259-260, 342-343, 422-423, 495-496, 566-567, 637-638, 708-709, 780-781

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 98 - 99, The `@NotNull` annotation is wrongly applied to optional
query parameters start_date and end_date in Fido2MetricsResource; remove
`@NotNull` from the method parameter declarations (the String startDate and String
endDate parameters) in each metrics handler method in class Fido2MetricsResource
so the parameters can be optional and rely on existing blank/null checks; apply
the same change to every duplicate occurrence listed (all handlers around the
reported lines) so the annotations match the documented optional behavior.

134-137: ⚠️ Potential issue | 🟠 Major

Do not collapse client/upstream HTTP errors into 500.

Line 134 catches all exceptions and rewrites them to internal-server errors, which can mask valid 4xx/5xx propagation.

♻️ Suggested change
-        } catch (Exception ex) {
+        } catch (WebApplicationException wae) {
+            throw wae;
+        } catch (Exception ex) {
             logger.error(ERR_MSG, ex);
             throwInternalServerException(ex);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 134 - 137, The catch-all in Fido2MetricsResource (catch (Exception
ex)) collapses upstream/client HTTP errors into 500; change the error handling
to preserve and propagate upstream status: catch specific exceptions (e.g.,
WebApplicationException, ProcessingException or the client-specific exception
type), and if ex is a WebApplicationException (or contains an upstream
Response), rethrow it or return
Response.status(((WebApplicationException)ex).getResponse().getStatus()).entity(...).
For other unexpected exceptions keep logger.error(ERR_MSG, ex) and
throwInternalServerException(ex); update the catch block around the code that
calls the HTTP client so client-originated HTTP errors are propagated instead of
always mapping to 500.

1031-1034: ⚠️ Potential issue | 🟠 Major

Date-only parsing branch is incorrect for LocalDateTime.

Line 1033 and Line 1039 parse date-only values into LocalDateTime, which causes DateTimeParseException for valid dd-MM-yyyy / yyyy-MM-dd inputs.

♻️ Suggested change
+import java.time.LocalDate;
@@
-        try {
-            return LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE);
+        try {
+            return LocalDate.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
         } catch (DateTimeParseException ignored) {
             // continue
         }
@@
-        return LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT));
+        return LocalDate.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT)).atStartOfDay();
#!/bin/bash
rg -n "LocalDateTime.parse\\(trimmed, DateTimeFormatter.ISO_LOCAL_DATE\\)|LocalDateTime.parse\\(trimmed, DateTimeFormatter.ofPattern\\(METRICS_DATE_FORMAT\\)\\)" jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java

Also applies to: 1038-1039

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`
around lines 1031 - 1034, The code is incorrectly calling
LocalDateTime.parse(...) for date-only strings; in Fido2MetricsResource replace
LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE) and
LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT))
with LocalDate.parse(...).atStartOfDay() (e.g., LocalDate.parse(trimmed,
DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay()) so date-only inputs produce a
LocalDateTime at start of day; adjust exception handling blocks around these
calls accordingly to catch DateTimeParseException from LocalDate.parse.
jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java (1)

30-33: ⚠️ Potential issue | 🟡 Minor

Close JAX-RS Response objects in tests.

Each request.get() result should be closed; otherwise test runs can leak client resources.

♻️ Suggested change
-        Response response = request.get();
-        log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
-        assertEquals(response.getStatus(), Status.OK.getStatusCode());
+        try (Response response = request.get()) {
+            log.info("getFido2MetricsEntry() - response:{}, response.getStatus():{}", response, response.getStatus());
+            assertEquals(response.getStatus(), Status.OK.getStatusCode());
+        }

Apply the same pattern to Line 45 and Line 60 blocks.

#!/bin/bash
rg -n "Response response = request.get\\(\\);" jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java
rg -n "try \\(Response response = request.get\\(\\)\\)" jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java

Also applies to: 45-47, 60-62

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`
around lines 30 - 33, Tests in Fido2MetricsTest create JAX-RS Response objects
with Response response = request.get() and fail to close them, leaking client
resources; update each occurrence (the three request.get() calls in class
Fido2MetricsTest) to use try-with-resources—e.g., replace "Response response =
request.get(); ... assertEquals(...)" with "try (Response response =
request.get()) { log.info(...); assertEquals(...); }" so responses are
automatically closed (apply same change to the blocks around the
getFido2MetricsEntry checks and the other two request.get() usages).
jans-config-api/plugins/fido2-plugin/src/test/resources/test.properties (1)

21-22: ⚠️ Potential issue | 🟠 Major

Parameterize metrics test dates and user id instead of hardcoding.

Line 21 and Line 22 lock tests to one historical day and one specific user UUID, which makes the suite environment-dependent and flaky.

♻️ Suggested change
-fido2MetricsEntries=/entries?start_date=2026-02-20T00:00:00Z&end_date=2026-02-20T23:59:59Z
-fido2UserMetrics=/entries/user/e1f32d71-2954-4248-a7c5-a2d68ce39f8d?start_date=2026-02-20T00:00:00Z&end_date=2026-02-20T23:59:59Z
+fido2MetricsStartDate=${fido2MetricsStartDate}
+fido2MetricsEndDate=${fido2MetricsEndDate}
+fido2TestUserId=${fido2TestUserId}
+fido2MetricsEntries=/entries?start_date=${fido2MetricsStartDate}&end_date=${fido2MetricsEndDate}
+fido2UserMetrics=/entries/user/${fido2TestUserId}?start_date=${fido2MetricsStartDate}&end_date=${fido2MetricsEndDate}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/test.properties`
around lines 21 - 22, The test properties hardcode a single date range and UUID
in fido2MetricsEntries and fido2UserMetrics which makes tests brittle; change
these values to parameterized placeholders (e.g., ${TEST_START_DATE},
${TEST_END_DATE}, ${TEST_USER_ID}) or reference environment/system properties
and update the test setup to inject those variables at runtime (ensure the code
that reads fido2MetricsEntries and fido2UserMetrics resolves the placeholders or
reads env vars so tests can supply different dates and a user id per run).
jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java (3)

450-453: ⚠️ Potential issue | 🟠 Major

Use periods query parameter, not startTime.

Line 451 forwards the comparison window under the wrong key, so upstream won’t receive the intended parameter.

🔧 Suggested fix
- data.put("startTime", String.valueOf(periods));
+ data.put("periods", String.valueOf(periods));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 450 - 453, Replace the incorrect query parameter key "startTime"
with "periods" when building the data map in Fido2MetricsService before calling
getMetricsData; specifically, update the Map<String, String> data population so
it uses data.put("periods", String.valueOf(periods)) (located where
getPeriodOverPeriodComparisonUrl() + "/" + aggregationType is invoked) so the
upstream receives the intended periods parameter.

162-163: ⚠️ Potential issue | 🟠 Major

Fix duplicated / in forwarded metrics URLs.

Line 162 and similar call sites append "/" + segment even though helper URLs already end with /, producing // in path segments.

🔧 Suggested fix
- return getFido2MetricsPagedResult(Fido2MetricsEntry.class, this.getFido2UserMetricsEntriesUrl() + "/" + userId,
+ return getFido2MetricsPagedResult(Fido2MetricsEntry.class, this.getFido2UserMetricsEntriesUrl() + userId,
         headers, data);

- return getFido2MetricsPagedResult(Fido2MetricsEntry.class,
-         this.getMetricsEntriesByOperationUrl() + "/" + operationType, headers, data);
+ return getFido2MetricsPagedResult(Fido2MetricsEntry.class,
+         this.getMetricsEntriesByOperationUrl() + operationType, headers, data);

- return getFido2MetricsPagedResult(Fido2MetricsAggregation.class,
-         this.getMetricsAggregationsUrl() + "/" + aggregationType, headers, data);
+ return getFido2MetricsPagedResult(Fido2MetricsAggregation.class,
+         this.getMetricsAggregationsUrl() + aggregationType, headers, data);

- return getMetricsData(this.getMetricsAggregationsUrl() + "/" + aggregationType + "/summary", headers, data);
+ return getMetricsData(this.getMetricsAggregationsUrl() + aggregationType + "/summary", headers, data);

- return getMetricsData(this.getMetricsAnalyticsUrl() + "/adoption", headers, data);
+ return getMetricsData(this.getMetricsAnalyticsUrl() + "adoption", headers, data);

- return getMetricsData(this.getTrendAnalysisUrl() + "/" + aggregationType, headers, data);
+ return getMetricsData(this.getTrendAnalysisUrl() + aggregationType, headers, data);

- return getMetricsData(this.getPeriodOverPeriodComparisonUrl() + "/" + aggregationType, headers, data);
+ return getMetricsData(this.getPeriodOverPeriodComparisonUrl() + aggregationType, headers, data);

Also applies to: 198-199, 233-234, 267-267, 299-299, 427-427, 453-453

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 162 - 163, The forwarded metrics URLs are being built with
duplicated slashes because callers append "/" + segment to helper methods that
already return a trailing slash (e.g., change calls to
getFido2MetricsPagedResult in Fido2MetricsService that use
this.getFido2UserMetricsEntriesUrl() + "/" + userId); instead of concatenating
an extra "/" remove the manual "/" so you pass
this.getFido2UserMetricsEntriesUrl() + userId (and likewise for other call sites
listed: getFido2UserMetricsEntriesUrl, getFido2DeviceMetricsEntriesUrl,
getFido2RpMetricsEntriesUrl, getFido2BackupCodeMetricsEntriesUrl,
getFido2CredentialsMetricsEntriesUrl, getFido2AuthenticationMetricsEntriesUrl)
into getFido2MetricsPagedResult to avoid producing "//" in the path.

503-511: ⚠️ Potential issue | 🔴 Critical

Preserve paged-response shape instead of treating payload as a raw list.

Current parsing reads the full payload as List<T> and then forces totalEntriesCount = list.size(). This drops upstream paging metadata and can misparse wrapper responses.

🔧 Suggested fix
- if (jsonNode != null) {
-     fido2MetricsList = Jackson.readList(jsonNode.toPrettyString(), type);
-     log.debug("Fido2MetricsEntry Two fido2MetricsList:{}", fido2MetricsList);
- }
+ if (jsonNode != null) {
+     JsonNode entriesNode = jsonNode.has("entries") ? jsonNode.get("entries") : jsonNode;
+     fido2MetricsList = Jackson.readList(entriesNode.toPrettyString(), type);
+     log.debug("Fido2MetricsEntry Two fido2MetricsList:{}", fido2MetricsList);
+ }

  pagedResultEntry = new PagedResult<>();
  pagedResultEntry.setEntries(fido2MetricsList);
- pagedResultEntry.setTotalEntriesCount(
-         (fido2MetricsList == null || fido2MetricsList.size() <= 0) ? 0 : fido2MetricsList.size());
+ int totalEntriesCount = (jsonNode != null && jsonNode.has("totalEntriesCount"))
+         ? jsonNode.get("totalEntriesCount").asInt()
+         : ((fido2MetricsList == null || fido2MetricsList.isEmpty()) ? 0 : fido2MetricsList.size());
+ pagedResultEntry.setTotalEntriesCount(totalEntriesCount);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 503 - 511, The code in Fido2MetricsService currently uses
Jackson.readList(jsonNode.toPrettyString(), type) to populate fido2MetricsList
and then overwrites paging by setting
pagedResultEntry.setEntries(fido2MetricsList) and
setTotalEntriesCount(fido2MetricsList.size()), which discards any upstream
paging metadata; instead, deserialize the jsonNode into a PagedResult<T> (or a
wrapper type) using Jackson (e.g., readValue into PagedResult.class with the
proper generic type), then set
pagedResultEntry.setEntries(pagedResult.getEntries()) and
pagedResultEntry.setTotalEntriesCount(pagedResult.getTotalEntriesCount()),
guarding for nulls so you preserve the original paging metadata rather than
forcing list.size(); update references around Jackson.readList,
fido2MetricsList, pagedResultEntry, and any type variable used for the element
type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`:
- Around line 111-127: Consolidate the duplicated header and time-range map
construction in Fido2MetricsService by extracting two helpers (e.g., a private
method buildHeaders(String token) that returns the Map<String,String> with
CONTENT_TYPE and optional AUTHORIZATION, and a private method
buildTimeRangeMap(Instant startTime, Instant endTime) that returns the
Map<String,String> with startTime/endTime when non-null) and replace the
repeated blocks in all methods that currently build those maps (the blocks
around lines referenced in the review). Ensure callers use these helpers where
headers or query data are constructed so the logic is centralized and DRY.
- Around line 109-110: The logs in Fido2MetricsService (e.g., the log call shown
and the similar calls across the class) print bearer tokens/headers in
cleartext; remove sensitive values from INFO logs by not including the
token/headers or by replacing them with a masked value (e.g., "<REDACTED>" or
only logging "Authorization header present"). Update each logging call (the one
shown and the others at the listed locations) to omit the raw token/header, or
if you need the value for diagnostics change the log level to DEBUG and mask the
token before logging; ensure the changes are applied to the methods in
Fido2MetricsService so no bearer tokens/authorization headers are emitted at
INFO level.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/ClientFactory.java`:
- Around line 7-23: The ClientFactory currently creates a JAX-RS Client (field
client) eagerly and never closes it; update ClientFactory to manage lifecycle by
either (A) implementing Closeable/AutoCloseable and adding a public close() that
calls client.close(), then register a JVM shutdown hook in the private
constructor to call instance().close(), or (B) convert ClientFactory into a
CDI-managed singleton (`@ApplicationScoped`) and add a `@PreDestroy` method (e.g.,
destroy() or close()) that calls client.close(); ensure getClientBuilder(String
url) and instance() keep working and reference the same client.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java`:
- Around line 91-92: The log in Fido2Util that currently calls logger.info(...)
with the HTTP entity and status code should be lowered to DEBUG to avoid
exposing potentially sensitive response contents; update the call in Fido2Util
(the logger invocation referencing entity and
httpResponse.getStatusLine().getStatusCode()) to logger.debug(...) and, if
available, wrap it with a logger.isDebugEnabled() check to avoid unnecessary
string construction when debug is off.

---

Duplicate comments:
In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java`:
- Around line 183-185: The info-level log in Fido2MetricsResource that prints
"Fido2MetricsEntry for user search param..." currently includes the raw userId
variable; change this to avoid logging identifiable user IDs at info level by
either substituting a redacted or hashed value for userId (e.g., mask or hash
before passing to the logger) or by moving the full userId logging to
debug-only; update the logging call that passes escapeLog(userId) (alongside
escapeLog(limit), escapeLog(startIndex), escapeLog(startDate),
escapeLog(endDate)) to use the redacted/hashed value or conditionally log the
raw userId at debug level instead.
- Around line 49-50: The PARAM log template in Fido2MetricsResource currently
has four placeholders but calling sites supply only two arguments; update the
PARAM constant (and any duplicate constants at the other occurrences) to match
the actual arguments — e.g., change PARAM to "Search param - limit:{},
startIndex:{}" (or alternatively update the calling sites to pass startDate and
endDate as the 3rd and 4th args) so the placeholder/argument counts match; keep
ERR_MSG unchanged. Ensure you update the PARAM constant used by methods that log
search parameters so no placeholder mismatch remains.
- Around line 1051-1053: The pagination logic treats the documented 1-based
startIndex as 0-based when validating and slicing lists (see validateStartIndex,
calculation of toIndex, and subList usages), causing off-by-one windows; update
all places (including the blocks around validateStartIndex, toIndex calculation
and any list.subList(...) calls at the mentioned locations) to convert the
incoming 1-based startIndex to a 0-based internal index (e.g., internalIndex =
startIndex - 1) before validation and slicing, ensure boundary checks use list
size correctly, and log/return values consistently using the external 1-based
startIndex where required.
- Around line 98-99: The `@NotNull` annotation is wrongly applied to optional
query parameters start_date and end_date in Fido2MetricsResource; remove
`@NotNull` from the method parameter declarations (the String startDate and String
endDate parameters) in each metrics handler method in class Fido2MetricsResource
so the parameters can be optional and rely on existing blank/null checks; apply
the same change to every duplicate occurrence listed (all handlers around the
reported lines) so the annotations match the documented optional behavior.
- Around line 134-137: The catch-all in Fido2MetricsResource (catch (Exception
ex)) collapses upstream/client HTTP errors into 500; change the error handling
to preserve and propagate upstream status: catch specific exceptions (e.g.,
WebApplicationException, ProcessingException or the client-specific exception
type), and if ex is a WebApplicationException (or contains an upstream
Response), rethrow it or return
Response.status(((WebApplicationException)ex).getResponse().getStatus()).entity(...).
For other unexpected exceptions keep logger.error(ERR_MSG, ex) and
throwInternalServerException(ex); update the catch block around the code that
calls the HTTP client so client-originated HTTP errors are propagated instead of
always mapping to 500.
- Around line 1031-1034: The code is incorrectly calling
LocalDateTime.parse(...) for date-only strings; in Fido2MetricsResource replace
LocalDateTime.parse(trimmed, DateTimeFormatter.ISO_LOCAL_DATE) and
LocalDateTime.parse(trimmed, DateTimeFormatter.ofPattern(METRICS_DATE_FORMAT))
with LocalDate.parse(...).atStartOfDay() (e.g., LocalDate.parse(trimmed,
DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay()) so date-only inputs produce a
LocalDateTime at start of day; adjust exception handling blocks around these
calls accordingly to catch DateTimeParseException from LocalDate.parse.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`:
- Around line 450-453: Replace the incorrect query parameter key "startTime"
with "periods" when building the data map in Fido2MetricsService before calling
getMetricsData; specifically, update the Map<String, String> data population so
it uses data.put("periods", String.valueOf(periods)) (located where
getPeriodOverPeriodComparisonUrl() + "/" + aggregationType is invoked) so the
upstream receives the intended periods parameter.
- Around line 162-163: The forwarded metrics URLs are being built with
duplicated slashes because callers append "/" + segment to helper methods that
already return a trailing slash (e.g., change calls to
getFido2MetricsPagedResult in Fido2MetricsService that use
this.getFido2UserMetricsEntriesUrl() + "/" + userId); instead of concatenating
an extra "/" remove the manual "/" so you pass
this.getFido2UserMetricsEntriesUrl() + userId (and likewise for other call sites
listed: getFido2UserMetricsEntriesUrl, getFido2DeviceMetricsEntriesUrl,
getFido2RpMetricsEntriesUrl, getFido2BackupCodeMetricsEntriesUrl,
getFido2CredentialsMetricsEntriesUrl, getFido2AuthenticationMetricsEntriesUrl)
into getFido2MetricsPagedResult to avoid producing "//" in the path.
- Around line 503-511: The code in Fido2MetricsService currently uses
Jackson.readList(jsonNode.toPrettyString(), type) to populate fido2MetricsList
and then overwrites paging by setting
pagedResultEntry.setEntries(fido2MetricsList) and
setTotalEntriesCount(fido2MetricsList.size()), which discards any upstream
paging metadata; instead, deserialize the jsonNode into a PagedResult<T> (or a
wrapper type) using Jackson (e.g., readValue into PagedResult.class with the
proper generic type), then set
pagedResultEntry.setEntries(pagedResult.getEntries()) and
pagedResultEntry.setTotalEntriesCount(pagedResult.getTotalEntriesCount()),
guarding for nulls so you preserve the original paging metadata rather than
forcing list.size(); update references around Jackson.readList,
fido2MetricsList, pagedResultEntry, and any type variable used for the element
type.

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java`:
- Around line 30-33: Tests in Fido2MetricsTest create JAX-RS Response objects
with Response response = request.get() and fail to close them, leaking client
resources; update each occurrence (the three request.get() calls in class
Fido2MetricsTest) to use try-with-resources—e.g., replace "Response response =
request.get(); ... assertEquals(...)" with "try (Response response =
request.get()) { log.info(...); assertEquals(...); }" so responses are
automatically closed (apply same change to the blocks around the
getFido2MetricsEntry checks and the other two request.get() usages).

In
`@jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java`:
- Line 24: The test currently logs the raw bearer token at error level in
Fido2ResourceTest (see the log.error call in getFido2Configuration() test);
remove the plaintext accessToken from the message (or replace it with a redacted
placeholder like "<REDACTED_TOKEN>") and lower the severity from error to a less
noisy level (debug or info) so credentials are not exposed in CI logs; update
the log statement in Fido2ResourceTest.java accordingly while keeping issuer and
fido2ConfigUrl as needed.

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/test.properties`:
- Around line 21-22: The test properties hardcode a single date range and UUID
in fido2MetricsEntries and fido2UserMetrics which makes tests brittle; change
these values to parameterized placeholders (e.g., ${TEST_START_DATE},
${TEST_END_DATE}, ${TEST_USER_ID}) or reference environment/system properties
and update the test setup to inject those variables at runtime (ensure the code
that reads fido2MetricsEntries and fido2UserMetrics resolves the placeholders or
reads env vars so tests can supply different dates and a user id per run).

In `@jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml`:
- Around line 18-22: Rename the duplicate TestNG <test> block named "Fido2" to a
unique, descriptive name matching its test class; update the test element that
contains the class io.jans.configapi.plugin.fido2.test.Fido2MetricsTest so its
name attribute is e.g. "Fido2MetricsTest" (or similar unique identifier) to
avoid ambiguity in TestNG reports and name-based filtering.

In
`@jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java`:
- Around line 159-177: The three helpers readStringValue, readListValue and both
readList overloads currently instantiate raw ObjectMapper() instances which
bypass the class's configured tolerant mapper; replace each new ObjectMapper()
with the shared/configured mapper used elsewhere in this Jackson utility (e.g.,
the static mapper instance or a getMapper() method), and ensure the same
TypeFactory calls (mapper.getTypeFactory().constructCollectionType(...)) and
deserialization features (FAIL_ON_UNKNOWN_PROPERTIES=false) are applied so all
deserialization uses the consistent, tolerant configuration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6c7aa076-05bc-455e-be44-eedbcf8ca8eb

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa4d67 and dd72fe1.

📒 Files selected for processing (35)
  • jans-auth-server/common/src/main/java/io/jans/as/common/model/session/SessionId.java
  • jans-config-api/docs/jans-config-api-swagger.yaml
  • jans-config-api/plugins/docs/fido2-plugin-swagger.yaml
  • jans-config-api/plugins/docs/scim-plugin-swagger.yaml
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/ApiApplication.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/rest/Fido2MetricsResource.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/ClientFactory.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Constants.java
  • jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2MetricsTest.java
  • jans-config-api/plugins/fido2-plugin/src/test/java/io/jans/configapi/plugin/fido2/test/Fido2ResourceTest.java
  • jans-config-api/plugins/fido2-plugin/src/test/resources/test.properties
  • jans-config-api/plugins/fido2-plugin/src/test/resources/testng.xml
  • jans-config-api/profiles/default/config-api-test.properties
  • jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties
  • jans-config-api/profiles/local/test.properties
  • jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java
  • jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java
  • jans-config-api/server/src/main/resources/config-api-rs-protect.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-performance-metrics.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-analytics-trends-aggregation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-adoption.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregation-summary.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-aggregations-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-devices.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-analytics-errors.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-operation-type.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-by-user.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-configuration.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-data.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-metrics-health-check.json
  • jans-config-api/server/src/main/resources/example/fido2/metrics/fido2-period-over-period-comparison.json
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/rest/BaseResource.java
  • jans-config-api/shared/src/main/java/io/jans/configapi/core/util/Jackson.java
💤 Files with no reviewable changes (1)
  • jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java

Comment on lines +109 to +110
log.info("Get Fido2 metrics entries with token:{}, startTime:{}, endTime:{}", token, startTime, endTime);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not log bearer tokens or auth headers at INFO level.

token (and headers) are logged in plain text across these methods. This leaks credentials into logs and audit pipelines.

🔧 Suggested fix
- log.info("Get Fido2 metrics entries with token:{}, startTime:{}, endTime:{}", token, startTime, endTime);
+ log.info("Get Fido2 metrics entries with startTime:{}, endTime:{}", startTime, endTime);

- log.info("Fido2MetricsEntryList Data: type:{}, url:{}, headers:{}, data:{}", type, url, headers, data);
+ log.info("Fido2MetricsEntryList Data: type:{}, url:{}, dataKeys:{}", type, url,
+         data == null ? null : data.keySet());

- log.info("Fido2Metrics Data: url:{}, headers:{}, data:{}", url, headers, data);
+ log.info("Fido2Metrics Data: url:{}, dataKeys:{}", url, data == null ? null : data.keySet());

Also applies to: 142-143, 177-178, 213-214, 247-248, 279-280, 312-313, 344-345, 375-376, 407-408, 439-440, 462-463, 481-482, 496-497, 521-522

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 109 - 110, The logs in Fido2MetricsService (e.g., the log call
shown and the similar calls across the class) print bearer tokens/headers in
cleartext; remove sensitive values from INFO logs by not including the
token/headers or by replacing them with a masked value (e.g., "<REDACTED>" or
only logging "Authorization header present"). Update each logging call (the one
shown and the others at the listed locations) to omit the raw token/header, or
if you need the value for diagnostics change the log level to DEBUG and mask the
token before logging; ensure the changes are applied to the methods in
Fido2MetricsService so no bearer tokens/authorization headers are emitted at
INFO level.

Comment on lines +111 to +127
// Request headers
Map<String, String> headers = new HashMap<>();
headers.put(CONTENT_TYPE, MediaType.APPLICATION_JSON);
if (StringUtils.isNotBlank(token)) {
headers.put(AUTHORIZATION, token);
}

// Query Parameter
Map<String, String> data = new HashMap<>();
if (startTime != null) {
data.put("startTime", startTime.toString());
}

if (endTime != null) {
data.put("endTime", endTime.toString());
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consolidate duplicated request-header and time-range map construction.

The same header/query-map logic is repeated across nearly every method, increasing drift risk.

♻️ Refactor sketch
+ private Map<String, String> buildHeaders(String token) {
+     Map<String, String> headers = new HashMap<>();
+     headers.put(CONTENT_TYPE, MediaType.APPLICATION_JSON);
+     if (StringUtils.isNotBlank(token)) {
+         headers.put(AUTHORIZATION, token);
+     }
+     return headers;
+ }
+
+ private Map<String, String> buildTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
+     Map<String, String> data = new HashMap<>();
+     if (startTime != null) data.put("startTime", startTime.toString());
+     if (endTime != null) data.put("endTime", endTime.toString());
+     return data;
+ }

- Map<String, String> headers = new HashMap<>();
- headers.put(CONTENT_TYPE, MediaType.APPLICATION_JSON);
- if (StringUtils.isNotBlank(token)) {
-     headers.put(AUTHORIZATION, token);
- }
- Map<String, String> data = new HashMap<>();
- if (startTime != null) {
-     data.put("startTime", startTime.toString());
- }
- if (endTime != null) {
-     data.put("endTime", endTime.toString());
- }
+ Map<String, String> headers = buildHeaders(token);
+ Map<String, String> data = buildTimeRange(startTime, endTime);

Also applies to: 145-160, 180-197, 216-231, 250-265, 281-297, 314-330, 346-361, 377-392, 410-425, 442-447, 464-469, 483-488

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/service/Fido2MetricsService.java`
around lines 111 - 127, Consolidate the duplicated header and time-range map
construction in Fido2MetricsService by extracting two helpers (e.g., a private
method buildHeaders(String token) that returns the Map<String,String> with
CONTENT_TYPE and optional AUTHORIZATION, and a private method
buildTimeRangeMap(Instant startTime, Instant endTime) that returns the
Map<String,String> with startTime/endTime when non-null) and replace the
repeated blocks in all methods that currently build those maps (the blocks
around lines referenced in the review). Ensure callers use these helpers where
headers or query data are constructed so the logic is centralized and DRY.

Comment on lines +7 to +23
public class ClientFactory {
private final static ClientFactory INSTANCE = new ClientFactory();

private final Client client;

private ClientFactory() {
this.client = ClientBuilder.newClient();
}

public static ClientFactory instance() {
return INSTANCE;
}

public Invocation.Builder getClientBuilder(String url) {
return client.target(url).request();
}
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding lifecycle management for the JAX-RS Client.

The Client instance is created eagerly but never explicitly closed. While many JAX-RS implementations handle cleanup on JVM shutdown, explicit lifecycle management is a best practice for resource-intensive clients.

Consider either:

  1. Implementing Closeable with a shutdown hook, or
  2. Converting to a CDI @ApplicationScoped bean with @PreDestroy cleanup
♻️ Alternative: CDI-managed singleton with lifecycle
-public class ClientFactory {
-    private final static ClientFactory INSTANCE = new ClientFactory();
-    
-    private final Client client;
-    
-    private ClientFactory() {
-        this.client = ClientBuilder.newClient();
-    }
-
-    public static ClientFactory instance() {
-        return INSTANCE;
-    }
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ClientFactory {
+    
+    private final Client client = ClientBuilder.newClient();
+
+    `@PreDestroy`
+    public void close() {
+        if (client != null) {
+            client.close();
+        }
+    }

     public Invocation.Builder getClientBuilder(String url) {
         return client.target(url).request();
     }
 }

Based on learnings: When using HTTP clients in Java services, use a reusable client at the service level, initialized and closed properly to prevent resource leaks.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public class ClientFactory {
private final static ClientFactory INSTANCE = new ClientFactory();
private final Client client;
private ClientFactory() {
this.client = ClientBuilder.newClient();
}
public static ClientFactory instance() {
return INSTANCE;
}
public Invocation.Builder getClientBuilder(String url) {
return client.target(url).request();
}
}
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
`@ApplicationScoped`
public class ClientFactory {
private final Client client = ClientBuilder.newClient();
`@PreDestroy`
public void close() {
if (client != null) {
client.close();
}
}
public Invocation.Builder getClientBuilder(String url) {
return client.target(url).request();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/ClientFactory.java`
around lines 7 - 23, The ClientFactory currently creates a JAX-RS Client (field
client) eagerly and never closes it; update ClientFactory to manage lifecycle by
either (A) implementing Closeable/AutoCloseable and adding a public close() that
calls client.close(), then register a JVM shutdown hook in the private
constructor to call instance().close(), or (B) convert ClientFactory into a
CDI-managed singleton (`@ApplicationScoped`) and add a `@PreDestroy` method (e.g.,
destroy() or close()) that calls client.close(); ensure getClientBuilder(String
url) and instance() keep working and reference the same client.

Comment on lines +91 to +92
logger.info("entity:{}, httpResponse.getStatusLine().getStatusCode():{}", entity,
httpResponse.getStatusLine().getStatusCode());
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reduce log level for potentially sensitive HTTP response details.

Logging HTTP entity and status code at INFO level in production could expose sensitive response data. Consider using DEBUG level instead.

🔧 Suggested fix
-            logger.info("entity:{}, httpResponse.getStatusLine().getStatusCode():{}", entity,
+            logger.debug("entity:{}, httpResponse.getStatusLine().getStatusCode():{}", entity,
                     httpResponse.getStatusLine().getStatusCode());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info("entity:{}, httpResponse.getStatusLine().getStatusCode():{}", entity,
httpResponse.getStatusLine().getStatusCode());
logger.debug("entity:{}, httpResponse.getStatusLine().getStatusCode():{}", entity,
httpResponse.getStatusLine().getStatusCode());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@jans-config-api/plugins/fido2-plugin/src/main/java/io/jans/configapi/plugin/fido2/util/Fido2Util.java`
around lines 91 - 92, The log in Fido2Util that currently calls logger.info(...)
with the HTTP entity and status code should be lowered to DEBUG to avoid
exposing potentially sensitive response contents; update the call in Fido2Util
(the logger invocation referencing entity and
httpResponse.getStatusLine().getStatusCode()) to logger.debug(...) and, if
available, wrap it with a logger.isDebugEnabled() check to avoid unnecessary
string construction when debug is off.

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

Labels

comp-docs Touching folder /docs comp-jans-auth-server Component affected by issue or PR comp-jans-config-api Component affected by issue or PR comp-jans-lock kind-feature Issue or PR is a new feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(jans-config-api): add endpoints to access FIDO metrics in fido2-plugin

2 participants