Summary
API is very similar to WCF - since both follow the internal IQuoteConnection interface.
Authentication is simpler, but still flexible.
The number of API calls should be lower than with WCF.
NetServer / CRM.web is acting as a client towards a partner provided URL.
The partner provided URL points to a REST API. The partner can implement the REST API in whatever tech they like.
The partner implements its own /Authentication endpoint as part of this API, so you decide if you need per-user access tokens or a shared secret authentication.
Comments? What do you think?
Any other pain-points we should look at in the Quote API?
Changes from WCF Connector API
Authentication is explicit rather than based on WCF certificate configuration.
Initialize is dropped from the API
Capabilities are fetched once and cached instead of being called frequently.
Use HTTP POST instead of SOAP/WCF requests.
NetServer is more clearly the client.
REST API Proposal
Follows IQuoteConnector - very similar to WCF API
Drop a couple of methods that we can simplify.
Online only - will not be available on-site, since on-site can code directly against .net APIs
- POST url/GetConfigurationFields
- POST url/TestConnection
- POST url/GetCapabilities
- POST url/GetActivePriceLists
- POST url/GetAllPriceLists
- POST url/GetAddresses
- POST url/GetNumberOfProductImages
- POST url/GetProductImage
- POST url/GetOrderState
- POST url/GetProduct
- POST url/GetProducts
- POST url/GetQuoteLinesFromProduct
- POST url/GetQuoteList
- POST url/OnAfterSaveQuote
- POST url/OnAfterSentQuoteVersion
- POST url/OnBeforeCreateQuote
- POST url/OnBeforeCreateQuoteAlternative
- POST url/OnBeforeCreateQuoteVersion
- POST url/OnBeforeDeleteQuote
- POST url/OnQuoteLineChanged
- POST url/PlaceOrder
- POST url/RecalculateQuoteAlternative
- POST url/UpdateQuoteVersionPrices
- POST url/ValidateQuoteVersion
- POST url/FindProduct
- POST url/GetSearchableFields
- POST url/GetSearchResults
Authentication
Must be valid HTTPS, same rules as webhooks target. That means no self-signed certs, no invalid or expired certs.
- Shared secret or signed JWT
- Partner creates shared secret when creating connection.
- Define shared secret in REST connection configuration
- Stored in fileset db as connection property
- Signed JWT: created automatically if no shared secret defined in connection.
- NetServer generates JWT with claims
- NetServer signs JWT with SuperOffice private key
- Partner verifies JWT signature and returns access token.
Shared secret means all users share the same authentication to the partner's Quote Connector API.
Signed JWT means that each user will have their own authentication to the partner's QuoteConnector API.
Authentication with Shared Secret
Fields: URL + AppId+ secret
Click TEST in SuperOffice admin GUI
- NetServer: Verify that AppId is valid for this tenant
- NetServer calls URL/Authenticate w secret as authentication
- POST url/Authenticate
Authorization: BEARER secret- Partner verifies secret and returns access token (which could just be the secret)
- POST url/TestConnection
Authorization: BEARER secret
Accept: application/json
Content-type: application/json
{ … json… }- Partner's Quote Connector Server looks up secret to find right config.
- POST url/Authenticate
Authentication with JWT
Fields: URL + AppId
- Secret = blank --> use JWT
Click TEST in SuperOfficeadmin GUI
- NetServer: Verify that AppId is valid for this tenant
- NetServer calls URL/Authenticate w signed JWT as authentication
- POST url/Authenticate
Authorization: BEARER { signed-jwt }- Partner verifies JWT signature and returns access token
- POST url/TestConnection
Authorization: BEARER access-token
Accept: application/json
Content-type: application/json
{ … json… }- Partner's Quote Connector Server looks up access token to find right config.
- POST url/Authenticate
Sequence diagram
The user clicks the CREATE QUOTE button in CRM.web
The CRM.web app calls CreateAndSaveQuote on NetServer
NetServer finds the right quote connector, initializes it with the parameters from its configuration, and calls OnBeforeSaveQuote on it.
The REST Quote Connector checks itself: do I have an shared secret defined in the configuration?
If there is a shared secret defined in the connection configuration:
- Call the Authentication REST API endpoint with the Shared Secret.
- The REST API endpoint verifies the shared secret - it needs to be unique for each customer - and returns the shared secret back as the access token.
- The Quote Connector calls the REST API endpoint OnBeforeCreateQuote with the shared secret.
- The REST API endpoint verifies the shared secret again, and returns the quote object.
If there is no shared secret in the connection configuration:
- Generate a JWT with the customer id, serial number, user's name, id, e-mail address
- Sign the JWT with the SuperOffice private key
- Call the Authentication REST API endpoint with the signed JWT.
- The REST API endpoint verifies the signature on the JWT
- if the signature is ok, then extract the claims
- Dig out the settings for the customer based on the claims
- Map the user id/e-mail from the claims to a corresponding ERP user if you want
- Generate an access token for the user
- Return the access token
- The Quote Connector calls the REST API endpoint OnBeforeCreateQuote with the access token.
- The REST API endpoint verifies the access token and maps it to the customer, and returns the quote object.
NetServer calls OnAfterSaveQuote on the REST Quote Connector
OnAfterSaveQuote does not need to authenticate first - since we have a access token / secret from earlier.
The QuoteConnector calls the REST endpoint for OnAfterSaveQuote with the access token.
The REST API verifies that the access token is ok, and the returns the quote object.
The Quote object is passed back up to the UI.
API Contract
- Swagger/OpenAPI definition for easy implementation by partner.
- Also provide sample implementations, one for shared secret, one for JWT authentication.
REST API Configuration Parameters
What you would see in the Quote Admin Connection dialog:
- URL: https://partner.com/quotecon/ (required)
- Secret: xyzzy1234 (optional)
- App Id: 12345def (optional)
- Custom fields as defined by partner URL
This is pretty similar to the WCF connector, but with the additional shared secret field.
Get all capabilities up front, then cache
NetServer should call REST API once to get values:
- POST /api/GetCapabilities
["cap1", "cap2", "cap3" ]
returns { "cap1" = true, "cap2" = false, "cap3"=true }
Then cache the result and use it until user logs out.
Should cut number of REST calls by 90% compared with WCF
Reauthenticate automatically on 401
If partner returns 401 then we must re-authenticate
- call Partner API /Authenticate again to get fresh bearer token.
Update session cache with new token
Remove API bits you don't want to support.
- 404 Error = not supported.
- We cache these, so we don't call the missing endpoint again.
Questions?
Comments? What do you think?
What other pain-points are there in the Quote API?