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.