Distributed James Server — jmap.properties

JMAP is intended to be a new standard for email clients to connect to mail stores. It therefore intends to primarily replace IMAP + SMTP submission. It is also designed to be more generic. It does not replace MTA-to-MTA SMTP transmission.

Consult this example to get some examples and hints.

Table 1. jmap.properties content
Property name explanation

enabled

true/false. Governs whether JMAP should be enabled

jmap.port

Optional. Defaults to 80. The port this server will be listening on. This value must be a valid port, ranging between 1 and 65535 (inclusive)

tls.keystoreURL

Keystore to be used for generating authentication tokens for password authentication mechanism. This should not be the same keystore than the ones used by TLS based protocols.

tls.secret

Password used to read the keystore

jwt.publickeypem.url

Optional. Coma separated list of RSA public keys URLs to validate JWT tokens allowing requests to bypass authentication. Defaults to an empty list.

url.prefix

Optional. Configuration urlPrefix for JMAP routes. Default value: http://localhost.

websocket.url.prefix

Optional. URL for JMAP WebSocket route. Default value: ws://localhost

email.send.max.size

Optional. Configuration max size for message created in both JMAP Draft amd RFC-8621. Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).

max.size.attachments.per.mail

Optional. Defaults to 20MB. RFC-8621 maxSizeAttachmentsPerEmail advertised to JMAP client as part of the urn:ietf:params:jmap:mail capability. This needs to be at least 33% lower than email.send.max.size property (in order to account for text body, headers, base64 encoding and MIME structures). JMAP clients would use this property in order not to create too big emails. Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).

upload.max.size

Optional. Configuration max size for each upload file in new JMAP-RFC-8621. Default value: 30M. Supported units are B (bytes) K (KB) M (MB) G (GB).

upload.quota.limit

Optional. Configure JMAP upload quota for total existing uploads' size per user. User exceeding the upload quota would result in old uploads being cleaned up. Default value: 200M. Supported units are B (bytes) K (KB) M (MB) G (GB).

view.email.query.enabled

Optional boolean. Defaults to false. Should simple Email/query be resolved against a Cassandra projection, or should we resolve them against OpenSearch? This enables a higher resilience, but the projection needs to be correctly populated.

user.provisioning.enabled

Optional boolean. Defaults to true. Governs whether authenticated users that do not exist locally should be created in the users repository.

authentication.strategy.draft

Optional List[String] with delimiter , . Specify which authentication strategies system admin want to use for JMAP draft server. The implicit package name is org.apache.james.jmap.http. If you have a custom authentication strategy outside this package, you have to specify its FQDN. If no authentication strategy is specified, JMAP draft server will fallback to default strategies: AccessTokenAuthenticationStrategy, JWTAuthenticationStrategy, QueryParameterAccessTokenAuthenticationStrategy

authentication.strategy.rfc8621

Optional List[String] with delimiter , . Specify which authentication strategies system admin want to use for JMAP RFC-8621 server. The implicit package name is org.apache.james.jmap.http. If you have a custom authentication strategy outside this package, you have to specify its FQDN. If no authentication strategy is specified, JMAP RFC-8621 server will fallback to default strategies: JWTAuthenticationStrategy, BasicAuthenticationStrategy.

jmap.version.default

Optional string. Defaults to draft. Allowed values: draft, rfc-8621. Which version of the JMAP protocol should be served when none supplied in the Accept header. Defaults to draft for legacy reasons (avoid breaking changes) but setting the value to rfc-8621 allow compatibility with other third party apps.

dynamic.jmap.prefix.resolution.enabled

Optional boolean. Defaults to false. Supported Jmap session endpoint returns dynamic prefix in response. When its config is true, and the HTTP request to Jmap session endpoint has a X-JMAP-PREFIX header with the value http://new-domain/prefix, then apiUrl, downloadUrl, uploadUrl, eventSourceUrl, webSocketUrl in response will be changed with a new prefix. Example: The apiUrl will be "http://new-domain/prefix/jmap". If the HTTP request to Jmap session endpoint has the X-JMAP-WEBSOCKET-PREFIX header with the value ws://new-domain/prefix, then capabilities."urn:ietf:params:jmap:websocket".url in response will be "ws://new-domain/prefix/jmap/ws".

webpush.prevent.server.side.request.forgery

Optional boolean. Prevent server side request forgery by preventing calls to the private network ranges. Defaults to true, can be disabled for testing.

cassandra.filter.projection.activated

Optional boolean. Defaults to false. Casandra backends only. Whether to use or not the Cassandra projection for JMAP filters. This projection optimizes reads, but needs to be correctly populated. Turning it on on systems with filters already defined would result in those filters to be not read.

delay.sends.enabled

Optional boolean. Defaults to false. Whether to support or not the delay send with JMAP protocol.

disabled.capabilities

Optional, defaults to empty. Coma separated list of JMAP capabilities to reject. This allows to prevent users from using some specific JMAP extensions.

Wire tapping

Enabling TRACE on org.apache.james.jmap.wire enables reactor-netty wiretap, logging of all incoming and outgoing requests, outgoing requests. This will log also potentially sensible information like authentication credentials.

OIDC set up

The use of XUserAuthenticationStrategy allow delegating the authentication responsibility to a third party system, which could be used to set up authentication against an OIDC provider.

We do supply an example of such a setup. It combines the Keycloack OIDC provider with the Krackend API gateway, but usage of similar technologies is definitely doable.

Generating a JWT key pair

Apache James can alternatively be configured to check the validity of JWT tokens itself. No revocation mechanism is supported in such a setup, and the sub claim is used to identify the user. The key configuration is static.

This requires the JWTAuthenticationStrategy authentication strategy to be used.

The Distributed server enforces the use of RSA-SHA-256.

One can use OpenSSL to generate a JWT key pair :

# private key
openssl genrsa -out rs256-4096-private.rsa 4096
# public key
openssl rsa -in rs256-4096-private.rsa -pubout > rs256-4096-public.pem

The private key can be used to generate JWT tokens, for instance using jwtgen:

jwtgen -a RS256 -p rs256-4096-private.rsa 4096 -c "sub=bob@domain.tld" -e 3600 -V

This token can then be passed as Bearer of the Authorization header :

curl -H "Authorization: Bearer $token" -XPOST http://127.0.0.1:80/jmap -d '...'

The public key can be referenced as jwt.publickeypem.url of the jmap.properties configuration file.

Annotated specification

The [annotated documentation](https://github.com/apache/james-project/tree/master/server/protocols/jmap-rfc-8621/doc/specs/spec) presents the limits of the JMAP RFC-8621 implementation part of the Apache James project. We furthermore implement [JSON Meta Application Protocol (JMAP) Subprotocol for WebSocket](https://tools.ietf.org/html/rfc8887).

Some methods / types are not yet implemented, some implementations are naive, and the PUSH is not supported yet.

Users are invited to read these limitations before using actively the JMAP RFC-8621 implementation, and should ensure their client applications only uses supported operations.

Contributions enhancing support are furthermore welcomed.

The list of tested JMAP clients are:

JMAP auto-configuration

RFC-8620 defining JMAP core RFC defines precisely service location.

James already redirects http://jmap.domain.tld/.well-known/jmap to the JMAP session.

You can further help your clients by publishing extra SRV records.

Eg:

_jmap._tcp.domain.tld. 3600        IN    SRV    0 1 443 jmap.domain.tld.

JMAP reverse-proxy set up

James implementation adds the value of X-Real-IP header as part of the logging MDC.

This allows for reverse proxies to cary other the IP address of the client down to the JMAP server for diagnostic purpose.