OCM webapp offers
Outbound OCM webapp offers
Outbound webapp offers are disabled by default. Existing WebDAV offers continue to work. Enable the provider-level offer only after the receiving deployment advertises the required OCM capabilities and has a working token endpoint.
Configuration
Set these keys under grpc.services.ocmshareprovider:
[grpc.services.ocmshareprovider]
offer_webapp = true
webapp_name = "JupyterLab"
webapp_endpoint = "https://provider.example/services/ocm/open"
offer_webapp defaults to false. When it is disabled, webapp_name and
webapp_endpoint may be omitted. When it is enabled:
webapp_namemust not be empty or whitespace-only.webapp_endpointmust be an absolute http or https URL with a non-empty host and no userinfo.- The configured application name is preserved exactly on offered shares.
Invalid enabled configuration fails startup with a bad-request error rather than silently offering an incomplete webapp.
Receiver configuration
Token exchange identifies the receiving provider by its host-only
provider_domain. ScienceMesh and every ocmreceived driver must share
this host-only FQDN.
[http.services.sciencemesh]
provider_domain = "receiver.example"
[grpc.services.storageprovider.drivers.ocmreceived]
provider_domain = "receiver.example"
[http.services.dataprovider.drivers.ocmreceived]
provider_domain = "receiver.example"
Receiver network policy
Received-share discovery, WebDAV access and token exchange use the configured
public-only transports described in
OCM network policy. ScienceMesh /webapp-launch
uses its own service configuration for both sender discovery and token
exchange.
Private federation requires the same intended allowed_federation_cidrs on
each participating receiver service and each ocmreceived driver instance;
configuring one does not configure the others.
An empty list denies private destinations. CIDR exceptions do not disable TLS
verification or the TLS 1.2 floor and do not admit plain HTTP or loopback.
ScienceMesh keeps loopback and environment proxy disabled. Receiver OCM and
ocmreceived proxy options are separate opt-ins; direct mode is required when
the dial guard must enforce the destination CIDR. A selected proxy changes
the connection the guard sees and is not proof of target-range enforcement.
The browser posts the launcher form to the application URL itself. Server network policy applies to Reva’s discovery, token exchange and WebDAV requests; it is not a browser application-origin allowlist. Launch still requires HTTPS for the application URI and token endpoint.
Served launcher page
POST /sciencemesh/webapp-launch is a protected route and the only received
webapp launch entry point. It takes file and an optional
expired_session_redirect_uri, validates the stored offer and its
permissions, exchanges the shared secret for an access token, joins the file
path onto the app URL, and returns an HTML page that submits access_token
to the validated app URL. The server keeps no handle or store.
expired_session_redirect_uri is forwarded only when its origin is exactly
https://<provider_domain>; any other value is dropped.
Operator notes:
- The page sets its own
Content-Security-Policy(nonce-basedscript-src,form-actionset to the exact validated app origin; error pages useform-action 'none'). Internationalized app hosts are posted to in their ASCII form, and an app host that cannot be written as a CSP host source is refused before any token exchange. The reverse-proxy edge must not stamp its own CSP on this path, or the inline submit script and the form post will be blocked. - Inbound, the launcher request fields are read by two strategies. The
credential strategy bearer reads
access_tokenand authenticates it with the gateway as an IdP token; it is in the defaultcredential_chain, so a web OIDC token works as long as a deployment that sets its own chain keepsbearerin it. The token strategy bearer readsaccess-tokenand accepts it only if it is already a Reva token; it is not in the defaulttoken_strategy_chain(["header"]), so add it only for clients that send Reva tokens. Outbound, the served launcher form posts a differentaccess_token, the exchanged token, to the validated app URL. The inbound and outboundaccess_tokenfields are separate and unrelated.
Offer gate
The provider offers a webapp method only when all four conditions hold:
offer_webappis enabled.- The request includes a webapp access candidate.
- The remote resource advertises
webapp-receivewith theblanktarget. - The remote provider advertises
exchange-token.
Otherwise, webapp methods are removed while WebDAV and other requested methods
remain. The provider records one fixed omission reason that carries no DTO,
URL, application name, secret, or token. The gate is evaluated before ignored
webapp candidates are validated, and the provider never synthesizes a
candidate, adds a file or folder branch, requires an iframe, or uses the
well-known enable_webapp setting as the offer gate.
When the gate holds, the provider rejects nil or typed-nil webapp options and rejects two or more eligible webapp candidates so a JSON map overwrite cannot select one. The failure happens before the share is created or stored.
Opener endpoint
webapp_endpoint is the complete opener endpoint:
https://provider.example/services/ocm/open
The provider does not append /lab or a share opaque ID. The opener obtains
share identity from the access token.
Requirement normalization
Offered webapp requirements are normalized before the share is wired or persisted, so the stored share and the outgoing request carry the same methods.
- Empty webapp requirements receive an independent copy of the default
webapp requirements, which contain
must-exchange-token. - Supplied non-empty webapp requirements must contain
must-exchange-tokenand only known, non-blank, unpadded, unique values. Their order is preserved. - An outbound
must-use-mfarequest is recognized and never erased; the receiving server’s MFA admission policy is not applied to sending. - For mixed webapp and WebDAV offers, an empty retained WebDAV requirements list defaults to a separate exchange-requirement copy. Supplied non-empty WebDAV lists must pass their vocabulary and agree as sets with the webapp list; conflicts are rejected rather than weakening the requirements. A requested webapp MFA requirement cannot combine with the current WebDAV vocabulary.
Normalization errors return a gRPC invalid-argument error with fixed safe text and perform no remote POST or store call.
Token exchange
The token endpoint accepts only the authorization_code grant. A missing
grant, an unknown grant, and the legacy ocm_share grant are rejected
with unsupported_grant_type before any gateway or share lookup.
For authorization_code, the form client_id is the receiving
provider’s host-only DNS FQDN. That identity is checked before the code
is looked up or authenticated. A missing or invalid identity returns
invalid_request and the response does not include the rejected value.
A valid FQDN with an empty code returns invalid_grant.
The exchanged code is the only share lookup key. Authentication binds
the form client_id to the share’s stored recipient, compared without
regard to case. The check does not use the sender, the creator, the HTTP
Host, forwarded headers, or the request address. An invalid, empty, or
different stored recipient is rejected before the accepted user is
resolved and before a token is minted.
The minted JWT client_id claim is the resolved outgoing share opaque
id. It is not the form receiver domain and it is not the shared secret.
Token verification re-derives that share id from the OCM share scope and
rejects the token when a present client_id claim disagrees. Legacy
direct-secret tokens and other non-OCM tokens omit the claim and still
work. The endpoint returns that minted access token unchanged.
Deployment checks
The well-known service’s enable_webapp setting controls discovery
advertisements only. It is not the provider’s outbound offer gate.
enable_code_flow advertises token exchange support. Before enabling offers,
check the effective local advertisements and the token endpoint in the
deployment. Also verify the hub spawner’s default_url matches the selected
application deployment. For the JupyterLab deployment, that value is /lab;
/lab remains a spawner setting and is not part of webapp_endpoint.
Received webapp shares
Incoming webapp offers are screened before discovery, conversion, or persistence. The same requirement policy is used for the generic protocol validator and the received-webapp check:
must-exchange-tokenis mandatory.- Blank, padded, and unknown requirements are rejected.
must-use-mfais rejected by the defaultmfa_policy = "reject". Settingmfa_policy = "off"under[http.services.wellknown.ocmprovider]skips that admission check for both ingest and launch. It does not verify a session proof or advertiseenforce-mfa. The rejection text is: “protocol webapp requirement must-use-mfa cannot be satisfied by this receiver”.
A webapp is kept only when discovery for that share shows exchange-token
and a usable token endpoint. The endpoint may be absolute http or https, or
a path-relative reference resolved against the discovery endpoint. A missing
capability or a bad endpoint is rejected before the share is stored.
The webapp URI that is stored must already be absolute http or https, with a hostname and no userinfo. Relative, network-path, and malformed webapp URIs are rejected. They are not resolved against the sender. WebDAV relative paths are unchanged. Accepting an absolute http URI at ingest does not change launch: the launcher still requires https for the application URI and the token endpoint.
Empty and padded appName values are stored as received. The only receive
target this service can open is blank.
Parser failures return a fixed invalid-request response. The response and its log line do not include the request body or the decoder’s cause.
Discovery gates
OCM discovery advertises apiVersion 1.4.0. That version is the document
this service publishes. It is not proof that every OCM 1.4 behavior is
implemented. Every User-Agent receives that same document.
enable_webapp is discovery-only and is distinct from offer_webapp.
An invalid or empty discovery base leaves discovery disabled and publishes
no receive targets. Otherwise:
- Webapp off and code flow off: no webapp send, no webapp receive, and no token endpoint.
- Webapp on and code flow off: no webapp send, webapp receive with
blank, and no token endpoint. - Webapp off and code flow on: no webapp send, no webapp receive, and a
validated token endpoint with
exchange-token. - Webapp on and code flow on: webapp send, webapp receive with
blank, and a validated token endpoint withexchange-token.
The token endpoint is advertised only when it can be built and the result is an absolute http or https URL with a hostname and no userinfo. The receive targets in that document are the same targets published for local share acceptance. A nil local override uses those published targets. An explicit empty override accepts no webapp share.
Specification scope
This service does not implement the whole webapp profile that an
apiVersion of 1.4.0 might suggest:
must-use-mfais rejected by default; the off policy skips admission without proving MFA.iframeis not a usable receive target.- Discovery does not advertise
enforce-mfa, HTTP request signatures, orjwksUri. - The browser application URI has no additional origin allowlist beyond the HTTPS and URI validation. Server discovery and exchange enforce the configured OCM network policy.
expired_session_redirect_uriis only forwarded by the served launcher page, and only for the receiver’s own origin; session-redirect completion is not implemented and not advertised in discovery.