Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f34b2c1
CCD-7109 JWT Issuer Validation Disabled in Multiple Services
patelila Mar 17, 2026
d8e47c4
Merge branch 'master' into CCD-7109_JWT_Issuer_Validation_Disabled
patelila Mar 17, 2026
014de09
Add JWT issuer validation coverage and fix Lombok Jackson config
patelila Mar 17, 2026
a5197e6
Enforce JWT issuer validation and add repo-specific coverage
patelila Mar 17, 2026
e9774e2
Enable JWT issuer validation and add focused coverage
patelila Mar 17, 2026
bdd1563
Align preview IDAM issuer config with JWT validation
patelila Mar 17, 2026
0e8912d
Merge branch 'master' into CCD-7109_JWT_Issuer_Validation_Disabled
patelila Mar 17, 2026
ec9c535
Update preview issuer config and migrate ACR references
patelila Mar 17, 2026
56fe349
Merge remote-tracking branch 'origin/CCD-7109_JWT_Issuer_Validation_D…
patelila Mar 17, 2026
be264ca
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Mar 17, 2026
7378444
Align preview OIDC issuer with AAT token issuer
patelila Mar 18, 2026
11f01e9
Merge remote-tracking branch 'origin/CCD-7109_JWT_Issuer_Validation_D…
patelila Mar 18, 2026
b9993e5
Set preview JWT issuer to match AAT token iss
patelila Mar 18, 2026
638ca21
Empty-Commit-To-Trigger-Build
dinesh1patel Mar 18, 2026
1ca9f42
Update build.gradle
dinesh1patel Mar 18, 2026
42f1db4
Update build.gradle
dinesh1patel Mar 18, 2026
4a83abd
Add runtime Swagger guard for deployed service
patelila Mar 18, 2026
7717f6b
Harden JWT validation tests and Swagger runtime guard and Clarify JWT…
patelila Mar 19, 2026
da5d915
Add JWT issuer validation guardrails and pipeline verification
patelila Mar 19, 2026
a30007b
Export OIDC_ISSUER for pipeline JWT issuer verification
patelila Mar 19, 2026
706bf80
Strengthen repo security skill for JWT issuer workflow
patelila Mar 19, 2026
f4c2fe0
Add repo-local security skill and JWT pipeline issuer guard
patelila Mar 20, 2026
9fd1f7b
fix for Could not get unknown property 'verifyFunctionalTestJwtIssuer…
patelila Mar 20, 2026
67567be
Document repo-local security skill and tighten JWT verifier guidance
patelila Mar 20, 2026
abadadb
Make pipeline JWT issuer verifier use available test credentials
patelila Mar 20, 2026
a17ecc6
Add repo-local Codex workflow docs for security and JWT issuer work
patelila Mar 23, 2026
085edb4
Add repo-local Codex workflow docs for security and JWT issuer work
patelila Mar 23, 2026
bac5bb2
docs: add OIDC_ISSUER derivation guidance
patelila Mar 24, 2026
1d4eb13
docs: clarify JWT issuer payload decode example
patelila Mar 24, 2026
ea15e43
Merge branch 'master' into CCD-7109_JWT_Issuer_Validation_Disabled
patelila Mar 25, 2026
6b0205b
Merge branch 'master' into CCD-7109_JWT_Issuer_Validation_Disabled
patelila Mar 25, 2026
ce509db
Align OIDC issuer config and stabilize WireMock integration tests
patelila Mar 26, 2026
0507d64
Merge branch 'master' into CCD-7109_JWT_Issuer_Validation_Disabled
patelila Mar 27, 2026
fca9179
ISSUE-094 enforce PRD org boundary checks and tighten JWT docs
patelila Mar 31, 2026
22ea319
Document JWT issuer validation guidance and repo alignment
patelila Mar 31, 2026
ce090b5
Clarify OIDC issuer configuration in JWT docs
patelila Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Agent Instructions

## Available skills

- `docs/skills/security/SKILL.md`
Use for broader Spring Security, IDAM/OIDC, auth filter, and security-related regression work in this repo.
Prompt cue: `Use docs/skills/security/SKILL.md`

- `docs/skills/security-jwt-issuer/SKILL.md`
Use for JWT issuer validation, issuer mismatch diagnosis, token `iss` checks, and pipeline verifier updates.
Prompt cue: `Use docs/skills/security-jwt-issuer/SKILL.md`
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ARG JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"
ARG APP_INSIGHTS_AGENT_VERSION=3.7.7
ARG PLATFORM=""

FROM hmctspublic.azurecr.io/base/java${PLATFORM}:21-distroless
FROM hmctsprod.azurecr.io/base/java${PLATFORM}:21-distroless
USER hmcts
LABEL maintainer="https://github.com/hmcts/ccd-data-store-api"

Expand Down
6 changes: 4 additions & 2 deletions Jenkinsfile_CNP
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,15 @@ env.ROLE_ASSIGNMENT_API_GATEWAY_S2S_CLIENT_ID = "ccd_data"
env.BEFTA_TEST_STUB_SERVICE_BASE_URL = "http://ccd-test-stubs-service-aat.service.core-compute-aat.internal"
env.BEFTA_S2S_CLIENT_ID_OF_CCD_DATA = "ccd_data"
env.BEFTA_S2S_CLIENT_ID_OF_XUI_WEBAPP = "xui_webapp"
env.OIDC_ISSUER = "https://forgerock-am.service.core-compute-idam-aat2.internal:8443/openam/oauth2/realms/root/realms/hmcts"
env.VERIFY_OIDC_ISSUER = "true"
// BEFTA retry env variables
env.BEFTA_RETRY_MAX_ATTEMPTS = "3"
env.BEFTA_RETRY_STATUS_CODES = "500,502,503,504"
env.BEFTA_RETRY_MAX_DELAY = "1000"
env.BEFTA_RETRY_NON_RETRYABLE_HTTP_METHODS = "POST,PUT"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctspublic ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctspublic.azurecr.io/imported/"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctsprod ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctsprod.azurecr.io/imported/"

withPipeline(type, product, component) {
onMaster {
Expand Down
6 changes: 4 additions & 2 deletions Jenkinsfile_nightly
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,15 @@ def vaultOverrides = [
// vars needed for functional tests
// Assume a feature build branched off 'develop', with dependencies develop-to-develop.
env.TEST_URL = "http://ccd-data-store-api-aat.service.core-compute-aat.internal"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctspublic ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctspublic.azurecr.io/imported/"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctsprod ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctsprod.azurecr.io/imported/"

// Other env variables needed for BEFTA.
env.BEFTA_S2S_CLIENT_ID = "ccd_gw"
env.ROLE_ASSIGNMENT_API_GATEWAY_S2S_CLIENT_ID = "ccd_data"
env.BEFTA_S2S_CLIENT_ID_OF_CCD_DATA = "ccd_data"
env.OIDC_ISSUER = "https://forgerock-am.service.core-compute-idam-aat2.internal:8443/openam/oauth2/realms/root/realms/hmcts"
env.VERIFY_OIDC_ISSUER = "true"
env.DM_STORE_BASE_URL = "http://dm-store-aat.service.core-compute-aat.internal"
env.CASE_DOCUMENT_AM_URL = "http://ccd-case-document-am-api-aat.service.core-compute-aat.internal"
env.RD_LOCATION_REF_API_BASE_URL = "http://rd-location-ref-api-aat.service.core-compute-aat.internal"
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The following environment variables are required:
| DATA_STORE_S2S_AUTHORISED_SERVICES | ccd_gw | Authorised micro-service names for S2S calls |
| IDAM_USER_URL | - | Base URL for IdAM's User API service (idam-app). `http://localhost:4501` for the dockerised local instance or tunneled `dev` instance. |
| IDAM_S2S_URL | - | Base URL for IdAM's S2S API service (service-auth-provider). `http://localhost:4502` for the dockerised local instance or tunneled `dev` instance. |
| IDAM_OIDC_URL | - | Base URL for IdAM OIDC discovery and JWKS lookup. This is used to resolve the OpenID configuration and signing keys. |
| OIDC_ISSUER | - | Enforced JWT issuer value. This must match the `iss` claim in real access tokens accepted by this service. Do not guess it; derive it from a real token for the target environment. |
| USER_PROFILE_HOST | - | Base URL for the User Profile service. `http://localhost:4453` for the dockerised local instance. |
| DEFINITION_STORE_HOST | - | Base URL for the Definition Store service. `http://localhost:4451` for the dockerised local instance. |
| CCD_DOCUMENT_URL_PATTERN | - | URL Pattern for documents attachable to cases. |
Expand All @@ -48,6 +50,11 @@ The following environment variables are required:
| DRAFT_ENCRYPTION_KEY | - | Draft encryption key. The encryption key used by draft store to encrypt documents with. |
| DRAFT_TTL_DAYS | - | Number of days after which the saved draft will be deleted if unmodified. |

`IDAM_OIDC_URL` and `OIDC_ISSUER` are intentionally separate. Discovery and JWKS retrieval use `IDAM_OIDC_URL`, while JWT validation enforces `OIDC_ISSUER`. If these do not align with the issuer used in real caller tokens, authenticated requests will be rejected with `401`.

### Codex Workflow Docs
Repo-local workflow docs are indexed in `AGENTS.md`.

### Building

The project uses [Gradle](https://gradle.org/).
Expand Down
4 changes: 2 additions & 2 deletions acb.tpl.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 1.0-preview-1
steps:
- id: pull-base-image-amd64
cmd: docker pull --platform linux/amd64 hmctspublic.azurecr.io/base/java:21-distroless && docker tag hmctspublic.azurecr.io/base/java:21-distroless hmctspublic.azurecr.io/base/java/linux/amd64:21-distroless
cmd: docker pull --platform linux/amd64 hmctsprod.azurecr.io/base/java:21-distroless && docker tag hmctsprod.azurecr.io/base/java:21-distroless hmctsprod.azurecr.io/base/java/linux/amd64:21-distroless
when: ["-"]
retries: 3
retryDelay: 5
Expand All @@ -18,7 +18,7 @@ steps:
retryDelay: 5

- id: pull-base-image-arm64
cmd: docker pull --platform linux/arm64 hmctspublic.azurecr.io/base/java:21-distroless && docker tag hmctspublic.azurecr.io/base/java:21-distroless hmctspublic.azurecr.io/base/java/linux/arm64:21-distroless
cmd: docker pull --platform linux/arm64 hmctsprod.azurecr.io/base/java:21-distroless && docker tag hmctsprod.azurecr.io/base/java:21-distroless hmctsprod.azurecr.io/base/java/linux/arm64:21-distroless
when:
- pull-base-image-amd64
retries: 3
Expand Down
53 changes: 48 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ dependencies {
implementation group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: '5.3.3'
implementation group: 'com.github.hmcts', name: 'idam-java-client', version: '3.0.5'
implementation group: 'com.github.hmcts', name: 'ccd-case-document-am-client', version: '1.59.2'
implementation group: 'com.github.hmcts.java-logging', name: 'logging', version: '6.1.9'
implementation group: 'com.github.hmcts.java-logging', name: 'logging', version: '8.0.0'

implementation group: 'com.auth0', name: 'java-jwt', version: '4.5.1'
implementation group: 'com.google.guava', name: 'guava', version: '33.5.0-jre'
Expand Down Expand Up @@ -306,7 +306,7 @@ dependencies {
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-contract-stub-runner'
testImplementation group: 'org.testcontainers', name: 'testcontainers', version: testContainersVersion
testImplementation group: 'org.testcontainers', name: 'postgresql', version: testContainersVersion
testImplementation group: 'org.testcontainers', name: 'postgresql', version: '1.21.3'
testImplementation group: 'org.testcontainers', name: 'elasticsearch', version: testContainersVersion
testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: testContainersVersion
testImplementation group: 'org.openid4java', name: 'openid4java', version: '1.0.0' // for sonar analysis
Expand Down Expand Up @@ -728,16 +728,59 @@ idea {
}

task highLevelDataSetup(type: JavaExec) {
dependsOn aatClasses
dependsOn 'aatClasses', 'verifyFunctionalTestJwtIssuer'

mainClass = "uk.gov.hmcts.ccd.datastore.befta.HighLevelDataSetupApp"
classpath += configurations.cucumberRuntime + sourceSets.aat.runtimeClasspath
jvmArgs = ['--add-opens=java.base/java.lang.reflect=ALL-UNNAMED']
}

task verifyFunctionalTestJwtIssuer(type: JavaExec) {
description = 'Verifies the functional/smoke test token issuer matches OIDC_ISSUER'
group = 'Verification'
dependsOn aatClasses

onlyIf {
System.getenv('VERIFY_OIDC_ISSUER')?.toBoolean()
}

mainClass = 'uk.gov.hmcts.ccd.datastore.befta.JwtIssuerVerificationApp'
classpath += configurations.cucumberRuntime + sourceSets.aat.runtimeClasspath
}

task verifyOidcIssuerPolicy {
description = 'Fails if oidc.issuer is derived from discovery configuration'
group = 'Verification'

doLast {
def policyFiles = [
'src/main/resources/application.properties',
'src/test/resources/test.properties',
'src/contractTest/resources/application.properties'
]

def violations = policyFiles.findAll { path ->
def file = file(path)
file.exists() && file.readLines().any { line ->
def normalized = line.replaceAll(/\s+/, '')
normalized.startsWith('oidc.issuer=') && normalized.contains('IDAM_OIDC_URL')
|| normalized.startsWith('oidc.issuer=') && normalized.contains('idam.api.url')
}
}

if (!violations.isEmpty()) {
throw new GradleException(
"OIDC issuer policy violation. Do not derive oidc.issuer from discovery config in: ${violations.join(', ')}"
)
}
}
}

check.dependsOn verifyOidcIssuerPolicy

task smoke() {
description = 'Executes smoke tests against an the CCD Data Store API instance just deployed'
dependsOn aatClasses
dependsOn 'aatClasses', 'verifyFunctionalTestJwtIssuer'

new File("$buildDir/test-results/test").mkdirs()
copy {
Expand Down Expand Up @@ -778,7 +821,7 @@ def tags = (findProperty('tags') == null) ? 'not @Ignore' : '(' + findProperty('
task functional(type: JavaExec) {
description = "Executes functional tests against an the CCD Data Store API instance just deployed"
group = "Verification"
dependsOn aatClasses
dependsOn 'aatClasses', 'verifyFunctionalTestJwtIssuer'

group = "Verification"

Expand Down
6 changes: 3 additions & 3 deletions charts/ccd-data-store-api/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ description: Helm chart for the HMCTS CCD Data Store
name: ccd-data-store-api
apiVersion: v2
home: https://github.com/hmcts/ccd-data-store-api
version: 2.0.37
version: 2.0.38
maintainers:
- name: HMCTS CCD Dev Team
email: ccd-devops@HMCTS.NET
dependencies:
- name: java
version: 5.3.0
repository: 'oci://hmctspublic.azurecr.io/helm'
repository: 'oci://hmctsprod.azurecr.io/helm'
- name: elasticsearch
version: 7.17.3
repository: 'https://helm.elastic.co'
Expand All @@ -20,5 +20,5 @@ dependencies:
condition: elastic.enabled
- name: ccd
version: 9.2.2
repository: 'oci://hmctspublic.azurecr.io/helm'
repository: 'oci://hmctsprod.azurecr.io/helm'
condition: ccd.enabled
10 changes: 6 additions & 4 deletions charts/ccd-data-store-api/values.preview.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ java:
DEFINITION_STORE_HOST: http://${SERVICE_NAME}-ccd-definition-store
USER_PROFILE_HOST: http://${SERVICE_NAME}-ccd-user-profile-api
ROLE_ASSIGNMENT_URL: http://${SERVICE_NAME}-am-role-assignment-service
IDAM_OIDC_URL: https://idam-web-public.aat.platform.hmcts.net
OIDC_ISSUER: https://forgerock-am.service.core-compute-idam-aat2.internal:8443/openam/oauth2/realms/root/realms/hmcts
ELASTIC_SEARCH_ENABLED: true
ELASTIC_SEARCH_NODES_DISCOVERY_ENABLED: true
ELASTIC_SEARCH_HOSTS: "{{ .Release.Name }}-es-master:9200"
Expand Down Expand Up @@ -92,7 +94,7 @@ ccd:
ccd-definition-store-api:
java:
ingressHost: ccd-definition-store-${SERVICE_FQDN}
image: hmctspublic.azurecr.io/ccd/definition-store-api:latest
image: hmctsprod.azurecr.io/ccd/definition-store-api:latest
imagePullPolicy: Always
devmemoryRequests: 2048Mi
devcpuRequests: 2000m
Expand All @@ -116,7 +118,7 @@ ccd:
ccd-user-profile-api:
java:
ingressHost: ccd-user-profile-api-${SERVICE_FQDN}
image: hmctspublic.azurecr.io/ccd/user-profile-api:latest
image: hmctsprod.azurecr.io/ccd/user-profile-api:latest
imagePullPolicy: Always
environment:
USER_PROFILE_DB_HOST: "{{ .Release.Name }}-postgresql"
Expand Down Expand Up @@ -178,13 +180,13 @@ elasticsearch:
# paths:
# - path: /
logstash:
image: "hmctspublic.azurecr.io/imported/logstash/logstash"
image: "hmctsprod.azurecr.io/imported/logstash/logstash"
imageTag: "7.16.1"
imagePullPolicy: "IfNotPresent"
logstashJavaOpts: "-Xmx1g -Xms512M"
extraInitContainers: |
- name: download-postgres-jdbc
image: hmctspublic.azurecr.io/curl:7.70.0
image: hmctsprod.azurecr.io/curl:7.70.0
command: ['curl', '-L', 'https://jdbc.postgresql.org/download/postgresql-42.2.18.jar', '-o', '/logstash-lib/postgresql.jar']
volumeMounts:
- name: logstash-lib
Expand Down
4 changes: 2 additions & 2 deletions charts/ccd-data-store-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ccd:
enabled: false

java:
image: 'hmctspublic.azurecr.io/ccd/data-store-api:latest'
image: 'hmctsprod.azurecr.io/ccd/data-store-api:latest'
ingressHost: ccd-data-store-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal
applicationPort: 4452
aadIdentityName: ccd
Expand Down Expand Up @@ -76,7 +76,7 @@ java:
CCD_DOCUMENT_URL_PATTERN: ^https?://(((?:api-gateway\.preprod\.dm\.reform\.hmcts\.net|dm-store-{{ .Values.global.environment }}\.service\.core-compute-{{ .Values.global.environment }}\.internal(?::\d+)?)\/documents\/[A-Za-z0-9-]+(?:\/binary)?)|(em-hrs-api-{{ .Values.global.environment }}\.service\.core-compute-{{ .Values.global.environment }}\.internal(?::\d+)?\/hearing-recordings\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\/((segments\/[0-9]+)|(file/\S+))))
IDAM_API_BASE_URL: https://idam-api.{{ .Values.global.environment }}.platform.hmcts.net
IDAM_OIDC_URL: https://idam-web-public.{{ .Values.global.environment }}.platform.hmcts.net
OIDC_ISSUER: https://forgerock-am.service.core-compute-idam-{{ .Values.global.environment }}.internal:8443/openam/oauth2/hmcts
OIDC_ISSUER: https://forgerock-am.service.core-compute-idam-{{ .Values.global.environment }}.internal:8443/openam/oauth2/realms/root/realms/hmcts
CCD_DRAFT_STORE_URL: http://draft-store-service-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal
CCD_DEFAULTPRINTURL: https://return-case-doc-ccd.nonprod.platform.hmcts.net/jurisdictions/:jid/case-types/:ctid/cases/:cid
CASE_DOCUMENT_AM_URL: http://ccd-case-document-am-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal
Expand Down
Loading