Requirements

natPortal proposes that forms be defined by the external service which requests the form data.

  • natPortal will render forms as defined in API requests.

  • natPortal will collect and send, but not store data from these forms.

  • natPortal will return the data from these forms to a callback URI.

System Design

To illustrate the requirements, if some service wishes to collect some information from a natPortal user, then the following sequence shall be observed:

sequenceDiagram
		actor User
    participant natPortal
    participant Service
    
    User ->>+ natPortal: accessForm(form_id, session_id)
    par Redirect user to form page
    	natPortal --) User: redirect(form_id)
    and Retrieve form schema from matching endpoint
      natPortal ->> natPortal: generateJWT(session_id)
	    natPortal ->>+ Service: requestFormSchema(form_id, JWT)
      critical Determine if user has the permissions to access form schema
	      Service ->>+ natPortal: getUser(JWT)
	      natPortal --)- Service: returnUser(user)
	      note right of Service: Service can<br/>evaluate any<br/>user data API.
      	Service ->> Service: Evaluate authorization to access form schema
      option Fail to retrieve data
      	Service --) natPortal: response 500
        note over natPortal,Service: Internal server error
        natPortal --) User: return_internal_error()
      option No form with given id
      	Service --) natPortal: response 404
        note over natPortal,Service: Not found.<br/>This means that the id is wrong.
        natPortal --) User: return_not_found()
      option Unauthorized form schema access
      	Service --) natPortal: response 403
        note over natPortal,Service: Forbidden
        natPortal --) User: return_not_found()
      end
		end

    Service --)- natPortal: returnFormSchema(formSchema)
    natPortal --)- User: renderForm(formSchema)
    note right of Service: Form schema<br/>may be cached

natPortal keeps a key:value store of each form’s unique form ID and its corresponding form endpoint. A user request for a form will trigger natPortal to request the form schema from the corresponding endpoint stored in Appwrite. Therefore, forms must be registered in Appwrite before forms are accessible through natPortal.

While the sequence is await-heavy, this allows any external service requiring user form input to collect it with any specified schema, questions, or format.

We opt to pass a JWT along with both the request for the form schema and the corresponding user-supplied form data, as the external service requires that the user be authorized to both access and submit the form. The external service may use Appwrite’s APIs, with a supplied read-only API key, to determine the eligibility of a given request.

natPortal proposes that services serve an API route which returns a response conforming to the JSON Schema specification[1], utilized by the library svelte-jsonschema-form[2]. natPortal subsequently returns the result, by the same JSON Schema standard described by svelte-jsonschema-form[2], to the same endpoint.

Security

natPortal must verify that the request for some form comes from an authenticated user. This is implemented through Appwrite. All server-side requests require that the user have an active authentication session.

The external service must verify that the request for a form schema comes from an active session, and that the owner of that session is eligible to access that form. natPortal passes the JWT to the external service. This is a short-lived token with only the permissions allowed to the user’s account. From the data accessible to that user, the external service may use Appwrite’s APIs to fetch relevant data and determine the eligibility of that user.

References

[1] JSON Schema specification

[2] svelte-jsonschema-form library repository

[3] Appwrite’s JWT authentication example