OAuth2 and SmartWeb Proxy Configuration

OAuth2 and SmartWeb Proxy Configuration

There are two OAuth2-related deployments in the SmartWeb platform:

  • OAuth2 token endpoint — a REST endpoint embedded in the main SmartWeb process that exchanges D2000 credentials for an OAuth2 access / refresh token from an upstream identity provider (Keycloak, Azure AD, …). Useful for native applications that already authenticate against D2000 but need to call upstream APIs protected by OAuth2.

  • SmartWeb Proxy — a separate Spring Cloud Gateway process deployed in front of the main SmartWeb. It performs the OAuth2 Authorization Code flow with the IdP on behalf of the browser, keeps the OAuth2 tokens on the server side in an encrypted cookie and injects a bearer token into every forwarded request. The browser never sees the OAuth2 tokens.

[!NOTE] The main SmartWeb process does not validate incoming OAuth2 JWT bearer tokens on API requests (/api/rest/**, /api/odata/**, /api/open/**, gRPC) — server-to-server clients authenticate with an API key (X-API-Key) or HTTP Basic authentication. Logging a browser user in through an external identity provider (OIDC/IAM) is handled by the AUTH_OIDC_LOCALLY mode. Both are described in Configuration of Authentication.

OAuth2 token endpoint

Exposure path: /api/oauth2 (secured the same way as the server-to-server REST API — API key, JWT or Basic auth).

Method

Path

Description

Method

Path

Description

POST

/api/oauth2/v0/oauth2/token

Exchange D2000 credentials for an upstream OAuth2 token

Request

{ "username": "operator1", "password": "secret", "realm": "my-keycloak-realm", "scope": "openid profile" }

Successful response

The full token response from the upstream IdP is forwarded as is (RFC 6749):

{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "abc123...", "scope": "openid profile" }

Error response

{ "error": "invalid_grant", "error_description": "Invalid D2000 credentials" }

Configuration

smartweb: authentication: oauth2: realms: - name: "my-keycloak-realm" tokenUrl: "https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token" clientId: "smartweb-client" clientSecret: "client-secret-value" scope: "openid profile" authMode: D2000 # D2000 | EXTERNAL - name: "azure-ad" tokenUrl: "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" clientId: "app-client-id" clientSecret: "app-secret" scope: "https://graph.microsoft.com/.default" authMode: D2000

Property

Description

Property

Description

name

Identifier of the realm that the client sends in the realm field of the token request

tokenUrl

OAuth2 token endpoint of the upstream IdP

clientId / clientSecret

Confidential client credentials registered in the IdP

scope

Space-separated list of OAuth2 scopes

authMode

D2000 — the credentials presented by the caller are first verified against D2000; EXTERNAL — the credentials are passed straight to the IdP without local verification

SmartWeb Proxy

SmartWeb Proxy is a standalone Spring Boot application packaged as smartweb-proxy.jar. It runs in front of the main SmartWeb instance and acts as a confidential OAuth2 client (token-mediating backend). The browser sessions carry only an encrypted cookie holding the OAuth2 refresh context; the access and refresh tokens never leave the server.

Browser → SmartWeb Proxy → SmartWeb (web-jar) → D2000 └── OAuth2 Authorization Code + Refresh flows with the IdP

Starting the proxy

java -jar smartweb-proxy.jar \ --spring.config.location=file:/opt/smartweb-proxy/config/application.yaml

Full configuration example (/opt/smartweb-proxy/config/application.yaml)

# Context root of the proxy (also the base path for cookies and redirects) spring.webflux.base-path: /edc-mobile # Truststore for outbound TLS connections to the IdP and the backend SmartWeb javax.net.ssl.trustStore: ./config/truststore.jks smartweb: proxy: auth: # Cookie-based authentication for the token-mediating backend exposed at # {spring.webflux.base-path}/auth/token token-mediating-backend: enabled: true persistence-cookie: name: REMEMBER_ME_COOKIE domain: localhost path: "${spring.webflux.base-path:}/login" ttl: 30D secure: false same-site: Strict # AES-GCM symmetric encryption key for the refresh-context cookie (hex, 32 bytes) encryption-secret: 6698ccc5a0469f58ff1e12de02eff22792968678dfca597e709fb34bd67d81ce encryption-salt: f40173ec5eed0e0346621f32a3a774dc auth-success-redirect-uri: "${spring.webflux.base-path:}/index.html" client-registration-id: edc-web-mobile-client spring: application: name: smartweb-proxy security: oauth2: # OAuth2 resource server for the /api/** endpoints resourceserver: jwt: issuer-uri: https://iam-test-edc.okte.sk/auth/realms/edc-external # OAuth2 client for the token-mediating-backend endpoint client: registration: edc-web-mobile-client: provider: edc-web-mobile-provider client-id: edc-core-ui client-secret: V4gwRiLcv4RP9zj2BJ1oAzMLS3kFhpMu authorization-grant-type: authorization_code # For long-lived sessions add "offline_access" scope: openid,profile,offline_access provider: edc-web-mobile-provider: issuer-uri: https://iam-test-edc.okte.sk/auth/realms/edc-external cloud: # Where to send forwarded requests. Multiple instances enable round-robin load balancing. discovery: client: simple: instances: smartweb-frontend-service: - uri: http://localhost:5173 smartweb-backend-service: - uri: http://localhost:80/edccore # Spring Cloud Gateway routes gateway: server: webflux: routes: - id: api uri: lb://smartweb-backend-service predicates: - Path=/api/** filters: - UsernameHeader # adds X-SmartWeb-User from the OAuth2 principal - RemoveRequestHeader=Authorization - SetRequestHeader=X-Api-Key, <smartweb-api-key> - RewritePath=/edc-mobile/?(?<segment>.*), /edccore/$\{segment} - id: swagger uri: lb://smartweb-backend-service/ predicates: - Path=/swagger/** filters: - RewritePath=/edc-mobile/?(?<segment>.*), /edccore/$\{segment} - id: resources uri: lb://smartweb-frontend-service/ predicates: - Path=/** metrics: enabled: true loadbalancer: eager-load: clients: - smartweb-backend-service - smartweb-frontend-service health-check: enabled: false smartweb-backend-service: - path: /do.healthcheck?format=simple interval: 5s refetch-instances: true smartweb-frontend-service: - path: / interval: 5s refetch-instances: true

Key properties

Property

Description

Property

Description

spring.webflux.base-path

Context root under which the proxy serves all paths (login, gateway routes, cookies)

javax.net.ssl.trustStore

Truststore for outbound HTTPS to the IdP and the backend SmartWeb

smartweb.proxy.auth.token-mediating-backend.persistence-cookie.encryption-secret

Hex 32-byte AES-GCM key for encrypting the refresh-context cookie

smartweb.proxy.auth.token-mediating-backend.client-registration-id

Name of the Spring Security OAuth2 client registration that drives the OAuth2 flow

spring.security.oauth2.client.registration.<id>

OAuth2 client credentials (client ID/secret, grant type, scopes)

spring.security.oauth2.client.provider.<id>.issuer-uri

Discovery URL of the IdP (automatically discovers token_endpoint, jwks_uri, …)

spring.cloud.gateway.server.webflux.routes

Gateway routes — forward to the backend SmartWeb, the frontend service, etc. The UsernameHeader filter injects the authenticated user into a header (by default X-SmartWeb-User).

spring.cloud.discovery.client.simple.instances

Static service registry — for production multi-instance deployments replace it with a real discovery server

[!NOTE] The proxy is a standalone process separate from the main smartweb.jar. It must be deployed and operated independently — typically on a different host or in a different container. The backend SmartWeb must trust the proxy: define an API key that the gateway injects via the SetRequestHeader=X-Api-Key, ... filter (see Configuration of Authentication).