This guide covers authentication configuration for ExploitIQ Client, including OpenShift OAuth, external identity providers, and development setups.
ExploitIQ supports multiple authentication modes via Quarkus profiles:
| Profile | Use Case | Identity Provider |
|---|---|---|
prod |
OpenShift | OpenShift OAuth |
external-idp |
External identity providers | Keycloak, Google, Azure AD, Okta |
dev |
Local development | Keycloak DevServices |
All profiles support both browser and API authentication:
| Method | Use Case | Flow |
|---|---|---|
| Browser | Web UI | Authorization Code Flow (redirects to IdP) |
| API | CLI, scripts, services | Bearer JWT token in Authorization header |
Token acquisition differs by profile:
prod(OpenShift): Useoc whoami -tor ServiceAccount tokensexternal-idp(Keycloak): Use OIDC token endpoint with password grantdev: Same asexternal-idp(DevServices Keycloak)
The default production configuration uses OpenShift's built-in OAuth server.
Create an OAuthClient resource in your OpenShift cluster:
apiVersion: oauth.openshift.io/v1
kind: OAuthClient
metadata:
name: exploit-iq-client
grantMethod: prompt
secret: <your-oauth-client-secret>
redirectURIs:
- "https://exploit-iq-client.<your-domain>"| Variable | Description | Example |
|---|---|---|
OPENSHIFT_DOMAIN |
OpenShift cluster domain | example.openshift.com |
OAUTH_CLIENT_SECRET |
Secret from OAuthClient resource | <your-secret> |
spec:
containers:
- name: exploit-iq-client
env:
- name: OPENSHIFT_DOMAIN
valueFrom:
secretKeyRef:
name: oauth-config
key: domain
- name: OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oauth-config
key: secretFor API access in OpenShift, use your user token:
# After oc login
TOKEN=$(oc whoami -t)
curl -H "Authorization: Bearer $TOKEN" https://exploit-iq-client.apps.example.com/api/v1/reportsUse the external-idp profile to integrate with external OIDC providers.
Keycloak can be used standalone or as an identity broker for GitHub, Google, and other providers.
| Variable | Description | Example |
|---|---|---|
QUARKUS_PROFILE |
Must be external-idp |
external-idp |
QUARKUS_OIDC_AUTH_SERVER_URL |
Keycloak realm URL | https://keycloak.example.com/realms/<your-realm> |
QUARKUS_OIDC_CREDENTIALS_SECRET |
OIDC client secret | <your-client-secret> |
Note: The testing script uses quarkus as the default realm name. Replace with your actual realm name in production.
Create an OIDC client in Keycloak with the following settings:
{
"clientId": "exploit-iq-client",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "<your-client-secret>",
"redirectUris": ["https://your-app-url/*"],
"webOrigins": ["https://your-app-url"],
"publicClient": false,
"standardFlowEnabled": true,
"directAccessGrantsEnabled": true
}Important: directAccessGrantsEnabled: true is required for API authentication via password grant.
Required protocol mappers (add to client scope):
preferred_username: Mapsusernametopreferred_usernameclaimemail: Mapsemailtoemailclaimupn: Mapsusernametoupnclaim (fallback)
Connect directly to Google without Keycloak.
- Go to Google Cloud Console
- Create OAuth 2.0 Client ID (Web application)
- Add authorized redirect URI:
https://your-app-url/
| Variable | Description |
|---|---|
QUARKUS_PROFILE |
external-idp |
QUARKUS_OIDC_PROVIDER |
google |
QUARKUS_OIDC_CLIENT_ID |
Google Client ID |
QUARKUS_OIDC_CREDENTIALS_SECRET |
Google Client Secret |
env:
- name: QUARKUS_PROFILE
value: "external-idp"
- name: QUARKUS_OIDC_PROVIDER
value: "google"
- name: QUARKUS_OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: google-oauth
key: client-id
- name: QUARKUS_OIDC_CREDENTIALS_SECRET
valueFrom:
secretKeyRef:
name: google-oauth
key: client-secretThe same approach works with any OIDC-compliant provider:
| Provider | Auth Server URL |
|---|---|
| Azure AD | https://login.microsoftonline.com/{tenant}/v2.0 |
| Okta | https://dev-xxxxx.okta.com/oauth2/default |
| Auth0 | https://your-domain.auth0.com |
| AWS Cognito | https://cognito-idp.{region}.amazonaws.com/{userPoolId} |
Note: GitHub does not support OIDC. Use Keycloak as an identity broker for GitHub authentication.
When using Keycloak or other OIDC providers, you can obtain tokens via the standard OIDC token endpoint. This allows CLI tools, scripts, and external services to authenticate without browser interaction.
Use the password grant to obtain a token for a specific user:
# Configuration (match your Keycloak setup)
KC_URL="http://localhost:8190" # Keycloak URL
KC_REALM="quarkus" # Realm name
CLIENT_ID="exploit-iq-client" # Client ID
CLIENT_SECRET="example-credentials" # Client secret
USERNAME="bruce" # User
PASSWORD="wayne" # Password
# Get user token (scope=openid is REQUIRED)
USER_TOKEN=$(curl -s -X POST \
"${KC_URL}/realms/${KC_REALM}/protocol/openid-connect/token" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "username=${USERNAME}" \
-d "password=${PASSWORD}" \
-d "grant_type=password" \
-d "scope=openid profile email" | jq -r '.access_token')
# Verify token was obtained
echo "Token: ${USER_TOKEN:0:50}..."Important: The scope=openid profile email parameter is required. Without openid, the UserInfo endpoint will reject the token with "Missing openid scope" error.
Use the token in the Authorization header:
# List reports
curl -H "Authorization: Bearer $USER_TOKEN" \
http://localhost:8080/api/v1/reports
# Get specific report
curl -H "Authorization: Bearer $USER_TOKEN" \
http://localhost:8080/api/v1/reports/{id}For machine-to-machine communication, use the client credentials grant:
# Get service token
SERVICE_TOKEN=$(curl -s -X POST \
"${KC_URL}/realms/${KC_REALM}/protocol/openid-connect/token" \
-d "client_id=${SERVICE_CLIENT_ID}" \
-d "client_secret=${SERVICE_SECRET}" \
-d "grant_type=client_credentials" | jq -r '.access_token')
curl -H "Authorization: Bearer $SERVICE_TOKEN" \
http://localhost:8080/api/v1/reportsNote: Requires a separate Keycloak client configured for service accounts.
The application validates JWT tokens by:
- Verifying the signature using JWKS from the IdP
- Checking token expiration
- Validating the issuer (
issclaim) - Fetching UserInfo to extract user details
Keycloak can act as an identity broker, allowing users to authenticate via external providers while maintaining centralized user management.
User → Application → Keycloak (Broker) → External IdP (GitHub/Google)
↓
Token Issuance
↓
Application
- Create GitHub OAuth App at GitHub Developer Settings
- Set callback URL:
https://<your-keycloak>/realms/<your-realm>/broker/github/endpoint - Configure in Keycloak: Identity Providers → Add GitHub
- Add mappers:
login→preferred_usernameemail→email
- Create Google OAuth Client at Google Cloud Console
- Set redirect URI:
https://<your-keycloak>/realms/<your-realm>/broker/google/endpoint - Configure in Keycloak: Identity Providers → Add Google
- Add mappers:
email→email
By default, authentication is disabled in the dev profile to simplify local development.
To enable OIDC and start Keycloak DevServices, run:
./mvnw quarkus:dev \
-Dquarkus.oidc.enabled=true \
-Dquarkus.keycloak.devservices.enabled=trueTest users are defined in src/test/resources/devservices/keycloak-realm.json, which is automatically imported.
Default users:
bruce/wayne(Admin)peter/parker(Viewer)miles/morales(Client Role Admin)gwen/stacy(Client Role Viewer)
For testing with an external Keycloak instance:
# Start Keycloak (use podman or docker)
podman run -d --name keycloak \
-p 8190:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
-e KC_HTTP_ENABLED=true \
-e KC_HOSTNAME=localhost \
quay.io/keycloak/keycloak:26.4 start-dev
# Start application (using 'quarkus' as example realm name)
./mvnw quarkus:dev \
-Dquarkus.profile=external-idp \
-Dquarkus.oidc.auth-server-url=http://localhost:8190/realms/quarkus \
-Dquarkus.oidc.credentials.secret=example-credentials \
-Dquarkus.keycloak.devservices.enabled=falseAn automated testing script is available for all authentication scenarios:
./scripts/test-auth.sh --helpThe script supports DevServices Keycloak, external Keycloak (with optional GitHub/Google brokers), and direct Google OIDC.
After running a scenario with Keycloak, test API authentication:
# 1. Get user token (uses bruce/wayne created by the script)
USER_TOKEN=$(curl -s -X POST \
"http://localhost:8190/realms/quarkus/protocol/openid-connect/token" \
-d "client_id=exploit-iq-client" \
-d "client_secret=example-credentials" \
-d "username=bruce" \
-d "password=wayne" \
-d "grant_type=password" \
-d "scope=openid profile email" | jq -r '.access_token')
# 2. Verify token obtained
[ -n "$USER_TOKEN" ] && echo "Token obtained" || echo "Failed to get token"
# 3. Call API with Bearer token
curl -H "Authorization: Bearer $USER_TOKEN" \
http://localhost:8080/api/v1/reportsThe application displays user information with this priority:
emailclaim (primary)upnclaim (User Principal Name)metadata.name(OpenShift)anonymous(fallback)
Ensure your identity provider or Keycloak is configured to include the email claim in tokens.
The application implements a Unified Role Mapping strategy, allowing you to manage permissions using either OpenShift Groups or OIDC Roles (Keycloak), depending on your environment.
The application looks for specific Target Roles (configurable via exploit-iq.security.target-roles):
exploit-iq-admin: Admin accessexploit-iq-view: Read-only accessexploit-iq-prodsec: Product Security access
In the prod profile, OpenShift Groups are automatically mapped to these roles.
- Group
exploit-iq-admin-> Mapped toexploit-iq-admin - Group
exploit-iq-view-> Mapped toexploit-iq-view - Group
exploit-iq-prodsec-> Mapped toexploit-iq-prodsec
In external-idp or dev profiles, roles are extracted from the OIDC token:
- Realm Roles:
exploit-iq-admin,exploit-iq-view - Resource Access (Client Roles): Roles defined specifically for the
exploit-iq-clientclient.
This flexibility allows you to choose the management style that fits your platform:
- OpenShift Native: Access is controlled by OpenShift Groups.
- Identity Provider: Access is controlled by Keycloak/IdP roles.
Cause: Missing protocol mappers in Keycloak.
Solution: Add email, preferred_username, and upn mappers to the client scope.
Cause: The redirect URI in the OAuth app doesn't match the application URL.
Solution:
- Ensure exact match including trailing slash:
https://your-app/ - Changes may take 5-15 minutes to propagate
Cause: Token missing openid scope or invalid token.
Solution:
- Ensure
scope=openid profile emailis included in token request - Verify token is not expired
- Check Keycloak logs for "Missing openid scope" error
Cause: Keycloak 26.x requires HTTPS by default, even in development.
Solution: For local development, set sslRequired=NONE on the realm:
# Using kcadm.sh inside container
podman exec keycloak /opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 --realm master --user admin --password admin
podman exec keycloak /opt/keycloak/bin/kcadm.sh update realms/master -s sslRequired=NONEThe testing script (test-auth.sh) handles this automatically.
Add to application.properties or set as environment variable:
quarkus.log.category."io.quarkus.oidc".level=DEBUGOr run the testing script with debug flag:
./scripts/test-auth.sh --debug