# JMAP Standards Compliance Improvements **Date:** August 15, 2025 **Based on:** JMAP Crash Course and RFC 8620 ## Summary of Improvements Made After reviewing the JMAP crash course, I've implemented several key improvements to make our JCHAT server more standards-compliant with RFC 8620. ## Core Improvements ### 1. Enhanced Request/Response Structure Validation **Previous:** - Basic validation of `using` and `methodCalls` - Limited error handling **Improved:** ```erlang % Now validates complete JMAP request structure validate_jmap_request(#{<<"using">> := Using, <<"methodCalls">> := MethodCalls}) -> case validate_using_array(Using) of ok -> validate_method_calls_array(MethodCalls); Error -> Error end. % Ensures method call IDs are unique within request validate_unique_call_ids(MethodCalls) -> CallIds = [CallId || [_, _, CallId] <- MethodCalls], case length(CallIds) =:= length(lists:usort(CallIds)) of true -> ok; false -> {error, "Method call IDs must be unique"} end. ``` ### 2. Proper Content-Type Handling **Previous:** - Loose content type checking **Improved:** ```erlang validate_content_type(Req) -> case cowboy_req:header(<<"content-type">>, Req) of <<"application/json", _/binary>> -> ok; _ -> {error, invalid_content_type} end. ``` **Now returns proper content-type:** ``` Content-Type: application/json; charset=utf-8 ``` ### 3. Comprehensive Error Response Format **Previous:** - Basic error types **Improved:** - All standard JMAP error types from RFC 8620: ```erlang format_error(invalid_arguments) -> #{type => <<"invalidArguments">>}; format_error(account_not_found) -> #{type => <<"accountNotFound">>}; format_error(forbidden) -> #{type => <<"forbidden">>}; format_error(invalid_result_reference) -> #{type => <<"invalidResultReference">>}; format_error(anchor_not_found) -> #{type => <<"anchorNotFound">>}; format_error(unsupported_sort) -> #{type => <<"unsupportedSort">>}; format_error(unsupported_filter) -> #{type => <<"unsupportedFilter">>}; format_error(cannot_calculate_changes) -> #{type => <<"cannotCalculateChanges">>}; format_error(too_large) -> #{type => <<"tooLarge">>}; format_error(rate_limited) -> #{type => <<"rateLimited">>}; format_error(state_mismatch) -> #{type => <<"stateMismatch">>}; % ... and more ``` ### 4. Authentication Framework **Previous:** - Hardcoded account ID **Improved:** ```erlang % Extract Bearer token from Authorization header extract_auth_token(<<"Bearer ", Token/binary>>) -> {ok, Token}; % Session endpoint now handles authentication authenticate_session_request(<<"Bearer ", _Token/binary>>) -> {ok, <<"demo@example.com">>}; % In production, validate token % Account ID determination from auth context determine_account_id(_AuthHeader) -> <<"default">>. % In production, extract from validated JWT token ``` ### 5. Method Call Validation **Added comprehensive method call structure validation:** ```erlang validate_method_call([Method, Args, CallId]) when is_binary(Method), is_map(Args), is_binary(CallId) -> case validate_method_name(Method) of true -> ok; false -> {error, invalid_method_name} end. validate_method_name(Method) -> case binary:split(Method, <<"/">>) of [Type, Operation] when byte_size(Type) > 0, byte_size(Operation) > 0 -> validate_method_chars(Method); _ -> false end. ``` ### 6. Session Object Improvements **Previous:** - Static session state - Fixed username **Improved:** ```erlang build_session_object(Username) -> #{ <<"capabilities">> => #{...}, <<"accounts">> => #{ <<"default">> => #{ <<"name">> => Username, % Dynamic username <<"isPersonal">> => true, <<"isReadOnly">> => false, <<"accountCapabilities">> => #{...} } }, <<"username">> => Username, <<"state">> => jchat_utils:generate_id() % Unique session state }. ``` ## Standards Compliance Checklist ### ✅ Now Compliant 1. **Request Structure Validation** - ✅ Validates `using` array is non-empty and contains only strings - ✅ Validates `methodCalls` array structure - ✅ Ensures method call IDs are unique within request - ✅ Proper method name format validation (`Type/operation`) 2. **Content-Type Handling** - ✅ Validates `application/json` content type - ✅ Returns `application/json; charset=utf-8` in responses 3. **Error Handling** - ✅ All standard JMAP error types implemented - ✅ Proper error response structure - ✅ URN-based error type identifiers 4. **Session Endpoint** - ✅ Authentication framework in place - ✅ Dynamic session state generation - ✅ Proper CORS handling - ✅ Method validation (GET/OPTIONS only) 5. **HTTP Method Handling** - ✅ Proper OPTIONS preflight support - ✅ Method not allowed responses - ✅ Authorization header extraction ### ⚠️ Still Needs Work 1. **Real Authentication** - Currently returns demo session regardless of auth - Need JWT token validation implementation - Need user lookup from token 2. **Account Management** - Still using hardcoded "default" account - Need proper account ID extraction from auth context - Need multi-account support 3. **State Management** - Session state is generated but not tracked - Need persistent state tracking per account/object type - Need state validation in method calls 4. **Binary Data** - Upload/download endpoints defined but not implemented - Need blob storage system - Need proper multipart handling ## Example: Standards-Compliant Request Flow ### 1. Client Session Request ```http GET /jmap/session HTTP/1.1 Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... ``` ### 2. Server Session Response ```json { "capabilities": { "urn:ietf:params:jmap:core": { "maxSizeUpload": 50000000, "maxConcurrentRequests": 4, "maxCallsInRequest": 16 }, "urn:ietf:params:jmap:chat": { "maxMessageLength": 10000, "maxParticipantsPerConversation": 100 } }, "accounts": { "default": { "name": "user@example.com", "isPersonal": true, "isReadOnly": false, "accountCapabilities": { "urn:ietf:params:jmap:core": {}, "urn:ietf:params:jmap:chat": {} } } }, "primaryAccounts": { "urn:ietf:params:jmap:chat": "default" }, "username": "user@example.com", "apiUrl": "http://localhost:8080/jmap/api", "state": "b3e91c7a-8f2d-4c1e-9a5b-7d3e2f1c8b9a" } ``` ### 3. Client API Request ```http POST /jmap/api HTTP/1.1 Content-Type: application/json; charset=utf-8 Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... { "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:chat"], "methodCalls": [ ["Conversation/query", {"accountId": "default"}, "c1"], ["Message/query", {"accountId": "default", "filter": {"conversationId": "#c1"}}, "c2"] ] } ``` ### 4. Server API Response ```json { "methodResponses": [ ["Conversation/query", {"accountId": "default", "ids": ["conv1", "conv2"]}, "c1"], ["Message/query", {"accountId": "default", "ids": ["msg1", "msg2", "msg3"]}, "c2"] ], "sessionState": "b3e91c7a-8f2d-4c1e-9a5b-7d3e2f1c8b9a" } ``` ## Testing the Improvements ### Manual Testing ```bash # Test session endpoint with proper headers curl -X GET http://localhost:8080/jmap/session \ -H "Authorization: Bearer test-token" # Test API endpoint with proper content type curl -X POST http://localhost:8080/jmap/api \ -H "Content-Type: application/json; charset=utf-8" \ -H "Authorization: Bearer test-token" \ -d '{ "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:chat"], "methodCalls": [["Core/echo", {"test": "data"}, "c1"]] }' # Test error handling with invalid content type curl -X POST http://localhost:8080/jmap/api \ -H "Content-Type: text/plain" \ -d '{"invalid": "request"}' ``` ### Expected Error Response ```json { "type": "urn:ietf:params:jmap:error:notJSON", "status": 400, "detail": "Content-Type must be application/json" } ``` ## Next Steps for Full Compliance 1. **Implement Real Authentication** - JWT token validation - User database lookup - Session management 2. **Add Binary Data Support** - Upload endpoint implementation - Download endpoint implementation - Blob storage in Mnesia 3. **Enhanced State Management** - Per-account state tracking - State validation in method calls - Proper change detection 4. **Method Enhancements** - Better property filtering - Pagination support - Advanced query features These improvements move us significantly closer to full JMAP RFC 8620 compliance while maintaining our clean architecture and extensibility.