https://jmap.io/[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 link:{sample-configuration-prefix-url}/jmap.properties[example]
to get some examples and hints.

.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

| websocket.ping.interval
| Optional. Configure the duration of the interval between consecutive ping messages (as specified in RFC6455) sent by the server to the client over a WebSocket connection.
The supported unit is seconds (e.g: `3s` for a 3-second interval). Default is empty, this feature is disabled.

| email.send.max.size
| Optional. Configuration max size for message created in 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 {backend-name} 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.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 `rfc-8621`. Allowed values: rfc-8621
Which version of the JMAP protocol should be served when none supplied in the Accept header.

| 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.

| email.get.full.max.size
| Optional, default value is 5. The max number of items for EmailGet full reads.

| get.max.size
| Optional, default value is 500. The max number of items for /get methods.

| set.max.size
| Optional, default value is 500. The max number of items for /set methods.

| authentication.strategy.rfc8621.xUser.secret
| Optional. List[String] with delimiter ",". Disabled by default. Secret-value used to validate the X-User-Secret header when using the XUserAuthenticationStrategy. Use of this configuration property is highly advised.

|===

In cassandra we can set an expiry for preview in an attempt to mitigate disk space usage. In `jvm.properties`:

....
# Previews takes roughly 10% DB space though can easily be recomputed. Storing preview for historical preview is
# also needless. This property allows setting an expiry for previews.
# james.jmap.preview.ttl=30d
....

== 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 link:https://github.com[example] of such a setup. It combines the link:https://www.keycloak.org/[Keycloack]
OIDC provider with the link:https://www.krakend.io/[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 {server-name} 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 link:https://github.com/vandium-io/jwtgen[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:

 - Experiments had been run on top of [LTT.RS](https://github.com/iNPUTmice/lttrs-android). Version in the Accept
 headers needs to be explicitly set to `rfc-8621`. [Read more](https://github.com/linagora/james-project/pull/4089).

== JMAP auto-configuration

link:https://datatracker.ietf.org/doc/html/rfc8620[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.