Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f03a502
Update yarn.lock file
mpgxvii Dec 10, 2025
f6fea0e
Update token validator in LoginEndpoint
mpgxvii Dec 10, 2025
aa5d712
Remove unused OAuthLogin methods and unused SessionResource
mpgxvii Dec 12, 2025
b2e5f50
Restore commented out integration tests
mpgxvii Dec 12, 2025
dbf4e0c
Remove unused HeaderUtil method
mpgxvii Dec 12, 2025
f530b3b
Update projectMapperDecorator
mpgxvii Dec 18, 2025
26e1128
Remove unused ConditionalOnProperty import
mpgxvii Dec 18, 2025
8f9080a
Improve readability
mpgxvii Dec 18, 2025
b4e0335
Add separate annotations for identity and authserver configs
mpgxvii Dec 18, 2025
46cb57e
Improve readability by OAuthclient and UserService beans conditionall…
mpgxvii Dec 18, 2025
e29f839
Make hydra jwt key path configurable
mpgxvii Dec 18, 2025
63f7351
Fix UserService tests
mpgxvii Dec 19, 2025
b54cc44
Create separate TokenValidator bean
mpgxvii Dec 19, 2025
740729f
Remove unnecessary Primary annotation from OauthClientServiceConfig
mpgxvii Jan 21, 2026
c613681
Separate out internal and external tokenvalidators
mpgxvii Jan 21, 2026
0a75855
Fix OAuthClientService conditional beans
mpgxvii Jan 21, 2026
61ac443
Fix Service annottion in DefaultOauthClientService
mpgxvii Jan 21, 2026
5353eb8
Minor fixes
mpgxvii Feb 9, 2026
b2c266d
Fix: GA
mpgxvii Feb 9, 2026
c6d7ed3
Merge branch 'dev' of https://github.com/RADAR-base/ManagementPortal …
mpgxvii Feb 9, 2026
13a5c99
Improve authServer and identityServer var naming
mpgxvii Feb 9, 2026
00a939d
Fix tokenValidator jwksurl
mpgxvii Feb 9, 2026
ce19eae
Fix user activated status in DefaultUserService
mpgxvii Feb 17, 2026
5c09b00
Fix login redirects during password reset and activation
mpgxvii Feb 19, 2026
5b5fd4d
Fix researcher account creation and activation
mpgxvii Mar 19, 2026
7c34460
Fix and remove unnecessary vars
mpgxvii May 27, 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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ jobs:
id: yarn-cache-dir-path
run: echo "dir=.yarn/cache" >> $GITHUB_OUTPUT

- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
java-version: '17'

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
Expand Down
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,19 @@ for other options on overriding the default configuration.
| `MANAGEMENTPORTAL_OAUTH_KEY_STORE_PASSWORD` | `radarbase` | Password for the JWT keystore |
| `MANAGEMENTPORTAL_OAUTH_SIGNING_KEY_ALIAS` | `radarbase-managementportal-ec` | Alias in the keystore of the keypair to use for signing |
| `MANAGEMENTPORTAL_OAUTH_ENABLE_PUBLIC_KEY_VERIFIERS` | `false` | Whether to use additional verifiers using public-keys and deprecated verifier implementation. If you set this to `true`, also set `RADAR_IS_CONFIG_LOCATION` and provide yaml file with public keys. Read more at radar-auth documentation. |
| `MANAGEMENTPORTAL_CATALOGUE_SERVER_ENABLE_AUTO_IMPORT` | `false` | Whether to enable or disable auto import of sources from the catalogue server |
| `MANAGEMENTPORTAL_CATALOGUE_SERVER_SERVER_URL` | None | URL to the catalogue server |
| `MANAGEMENTPORTAL_CATALOGUESERVER_ENABLEAUTOIMPORT` | `false` | Whether to enable or disable auto import of sources from the catalogue server (maps to `managementportal.catalogueServer.enableAutoImport` in application-prod.yml). |
| `MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL` | None | URL to the catalogue server (maps to `managementportal.catalogueServer.serverUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL` | `true` | Whether to use ManagementPortal’s internal identity management (`true`) or an external identity server such as Ory Kratos (`false`). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL` | None | URL to the identity server. When using Ory Kratos externally, set this to the Kratos public URL as seen from ManagementPortal (for example `http://kratos:4433` when using Docker). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL` | None | Admin URL to the identity server. When using Ory Kratos externally, set this to the Kratos admin URL as seen from ManagementPortal (for example `http://kratos:4434`). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL` | None | Public URL of the identity server. When using Ory Kratos externally, set this to the Kratos public URL as seen from ManagementPortal (e.g. `http://kratos:4433`; maps to `managementportal.identityServer.publicUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL` | None | Admin URL to the identity server. When using Ory Kratos externally, set this to the Kratos admin URL as seen from ManagementPortal (e.g. `http://kratos:4434` or `http://kratos-admin:4434`; maps to `managementportal.identityServer.adminUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL` | None | Email-address to be linked to the admin account. |
| `MANAGEMENTPORTAL_IDENTITYSERVER_USER_ACTIVATION_FLOW_TYPE` | `verification` | Kratos self-service flow used to send activation emails. Allowed: `verification` or `recovery`. |
| `MANAGEMENTPORTAL_IDENTITYSERVER_USER_ACTIVATION_METHOD` | `code` | Kratos method used in the activation flow request payload. Allowed: `code` or `link` (depending on flow and Kratos configuration). |
| `MANAGEMENTPORTAL_AUTHSERVER_INTERNAL` | `true` | Whether to use ManagementPortal’s internal OAuth2 authorization server (`true`) or an external server such as Ory Hydra (`false`). |
| `MANAGEMENTPORTAL_AUTHSERVER_SERVERURL` | None | Base URL of the external auth server’s public endpoints, used for the token endpoint. When using Ory Hydra in Docker, this is typically `http://hydra:4444`. |
| `MANAGEMENTPORTAL_AUTHSERVER_LOGINURL` | None | Base URL of the external auth server as seen from the browser, used to construct the `/oauth2/auth` redirect URL (for example `http://localhost:4444` for Hydra’s public endpoint). |
| `MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL` | None | Admin URL of the external auth server (for example `http://hydra:4445` for the Hydra admin API), used for managing OAuth clients. |
| `MANAGEMENTPORTAL_AUTHSERVER_TOKENURL` | None | Full URL of the external auth server’s token endpoint. When using Ory Hydra, set to the token URL (e.g. `http://hydra:4444/hydra/oauth2/token`; maps to `managementportal.authServer.tokenUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_AUTHSERVER_AUTHURL` | None | Auth URL as seen from the browser, used for the authorize redirect (e.g. `http://localhost:4444/hydra/oauth2/auth`; maps to `managementportal.authServer.authUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_AUTHSERVER_ADMINURL` | None | Admin URL of the external auth server (e.g. `http://hydra:4445` for the Hydra admin API; maps to `managementportal.authServer.adminUrl` in application-prod.yml), used for managing OAuth clients. |
| `MANAGEMENTPORTAL_AUTHSERVER_JWKSURL` | None | Full JWKS URL used to verify JWTs (e.g. `http://hydra:4445/admin/keys/hydra.jwt.access-token`; maps to `managementportal.authServer.jwksUrl` in application-prod.yml). |
| `MANAGEMENTPORTAL_COMMON_BASE_URL` | None | Resolvable baseUrl of the hosted platform |
| `MANAGEMENTPORTAL_COMMON_MANAGEMENT_PORTAL_BASE_URL` | None | Resolvable baseUrl of this managementportal instance |
| `MANAGEMENTPORTAL_COMMON_PRIVACY_POLICY_URL` | None | Resolvable URL to the common privacy policy url |
Expand Down Expand Up @@ -290,13 +291,14 @@ ManagementPortal can be run either with its **internal identity and auth server*
- **External Ory identity and auth (Kratos + Hydra)**:
- Set `MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false` and `MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false`.
- Configure the external identity server (typically Ory Kratos):
- `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL` (e.g. `http://kratos:4433`)
- `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL` (e.g. `http://kratos:4434`)
- `MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL` (e.g. `http://kratos:4433`)
- `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL` (e.g. `http://kratos:4434`)
- `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL`
- Configure the external auth server (typically Ory Hydra):
- `MANAGEMENTPORTAL_AUTHSERVER_SERVERURL` (e.g. `http://hydra:4444`, reachable from ManagementPortal)
- `MANAGEMENTPORTAL_AUTHSERVER_LOGINURL` (e.g. `http://localhost:4444`, reachable from the browser)
- `MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL` (e.g. `http://hydra:4445`)
- `MANAGEMENTPORTAL_AUTHSERVER_TOKENURL` — full token URL (e.g. `http://hydra:4444/hydra/oauth2/token`, reachable from ManagementPortal)
- `MANAGEMENTPORTAL_AUTHSERVER_AUTHURL` — auth URL as seen from the browser (e.g. `http://localhost:4444/hydra/oauth2/auth`)
- `MANAGEMENTPORTAL_AUTHSERVER_ADMINURL` (e.g. `http://hydra:4445`)
- `MANAGEMENTPORTAL_AUTHSERVER_JWKSURL` (e.g. `http://hydra:4445/admin/keys/hydra.jwt.access-token`)
- Start the Ory stack using `docker-compose -f src/main/docker/ory_stack.yml up -d` and ensure the
`radar-self-enrolment-ui` service is running. The ManagementPortal login flow redirects the user to Hydra, which in
turn uses the self-enrolment UI for login and consent screens.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ constructor(
/** Maximum time that the token verifier does not need to be fetched. */
maxAge: Duration = Duration.ofDays(1),
) {
private val algorithmLoaders: List<TokenVerifierCache>?
private val algorithmLoaders: List<TokenVerifierCache>

init {
val config = CacheConfig(
Expand All @@ -38,7 +38,7 @@ constructor(
)
algorithmLoaders = verifierLoaders?.map { loader ->
CachedValue(config, supplier = loader::fetch)
}
} ?: emptyList()
}

/**
Expand Down Expand Up @@ -77,13 +77,13 @@ constructor(
suspend fun validate(token: String): RadarToken {
val result: Result<RadarToken> = consumeFirst { emit ->
val causes = algorithmLoaders
?.forkJoin { cache ->
.forkJoin { cache ->
val result = cache.verify(token)
// short-circuit to return the first successful result
if (result.isSuccess) emit(result)
result
}
?.flatMap {
.flatMap {
it.exceptionOrNull()
?.suppressedExceptions
?: emptyList()
Expand All @@ -103,7 +103,7 @@ constructor(

/** Refresh the token verifiers from cache on the next validation. */
fun refresh() {
algorithmLoaders?.forEach { it.clear() }
algorithmLoaders.forEach { it.clear() }
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class KratosSessionDTO(
val type: String? = null
)

@Serializable
data class RecoveryLinkResponse(
val recovery_link: String,
val expires_at: String? = null
)

@Serializable
data class Identity(
var id: String? = null,
Expand Down
14 changes: 7 additions & 7 deletions src/main/docker/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ services:
- SPRING_DATASOURCE_PASSWORD=radarbase
- MANAGEMENTPORTAL_COMMON_ADMINPASSWORD=admin
- MANAGEMENTPORTAL_FRONTEND_CLIENT_SECRET=secret
- MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv
- MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false # IF using external identity like ORY Kratos
- MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL=admin-email-here@radar-base.net
- MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL=http://kratos:4433
- MANAGEMENTPORTAL_IDENTITYSERVER_LOGINURL=http://localhost:3000
- MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL=http://kratos:4434
- MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL=http://kratos:4433
- MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL=http://kratos-admin:4434
- MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false # IF using external auth server like ORY Hydra
- MANAGEMENTPORTAL_AUTHSERVER_SERVERURL=http://hydra:4444
- MANAGEMENTPORTAL_AUTHSERVER_LOGINURL=http://localhost:4444
- MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL=http://hydra:4445
- MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv
- MANAGEMENTPORTAL_AUTHSERVER_TOKENURL=http://hydra:4444/hydra/oauth2/token
- MANAGEMENTPORTAL_AUTHSERVER_ADMINURL=http://hydra:4445
- MANAGEMENTPORTAL_AUTHSERVER_AUTHURL=http://localhost:4444/hydra/oauth2/auth
- MANAGEMENTPORTAL_AUTHSERVER_JWKSURL=http://hydra:4445/admin/keys/hydra.jwt.access-token
- MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL=http://catalogue-server:8080
- JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
- JAVA_OPTS=-Xmx512m # maximum heap size for the JVM running ManagementPortal, increase this as necessary
Expand Down
13 changes: 7 additions & 6 deletions src/main/docker/managementportal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ services:
- SPRING_DATASOURCE_PASSWORD=radarbase
- MANAGEMENTPORTAL_COMMON_ADMINPASSWORD=admin
- MANAGEMENTPORTAL_FRONTEND_CLIENT_SECRET=secret
- MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv
- MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false # IF using external identity like ORY Kratos
- MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL=admin-email-here@radar-base.net
- MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL=http://kratos:4433
- MANAGEMENTPORTAL_IDENTITYSERVER_LOGINURL=http://localhost:3000
- MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL=http://kratos:4434
- MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL=http://kratos:4433
- MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL=http://kratos-admin:4434
- MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false # IF using external auth server like ORY Hydra
- MANAGEMENTPORTAL_AUTHSERVER_SERVERURL=http://hydra:4444
- MANAGEMENTPORTAL_AUTHSERVER_LOGINURL=http://localhost:4444
- MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL=http://hydra:4445
- MANAGEMENTPORTAL_AUTHSERVER_TOKENURL=http://hydra:4444/hydra/oauth2/token
- MANAGEMENTPORTAL_AUTHSERVER_ADMINURL=http://hydra:4445
- MANAGEMENTPORTAL_AUTHSERVER_AUTHURL=http://localhost:4444/hydra/oauth2/auth
- MANAGEMENTPORTAL_AUTHSERVER_JWKSURL=http://hydra:4445/admin/keys/hydra.jwt.access-token
- MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL=http://catalogue-server:8080
- JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
- JAVA_OPTS=-Xmx512m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 #enables remote debugging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ data class ManagementPortalProperties @ConstructorBinding constructor(
) {
data class IdentityServer @ConstructorBinding constructor(
val internal: Boolean = true,
val serverUrl: String,
val serverAdminUrl: String,
val publicUrl: String,
val adminUrl: String,
val adminEmail: String,
val userActivationFlowType: String = "verification",
val userActivationMethod: String = "code",
)

data class AuthServer @ConstructorBinding constructor(
val internal: Boolean = true,
val serverUrl: String,
val serverAdminUrl: String,
val loginUrl: String,
val tokenUrl: String,
val adminUrl: String,
val authUrl: String,
/** Full URL to a JWKS endpoint that publishes token verification keys. */
val jwksUrl: String,
)

data class Mail @ConstructorBinding constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ class ManagementPortalSecurityConfigLoader(
return
}
logger.info("Loading additional ManagementPortal clients...")
val path = managementPortalProperties.oauth?.clientsFile
if (Objects.isNull(path) || path == "") {
val path = managementPortalProperties.oauth.clientsFile
if (path.isNullOrBlank()) {
logger.info("No OAuth clients file specified, additional clients loading aborted.")
return
}
Expand Down
Loading
Loading