diff --git a/README.md b/README.md index 415b35b..db801c3 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,25 @@ -# examples +# Examples -Some examples of how to integrate with transcend in various languages. See [our documentation](https://docs.transcend.io/) for further information. +Some examples of how to integrate with Transcend in various languages. See [our documentation](https://docs.transcend.io/) for further information. ## What are these examples -These examples are meant to show you what the integration will look like when you connect your internal systems to transcend. +These examples are meant to show you what the integration will look like when you connect your internal systems to Transcend. Each language has an example of how to: -- Verify a webhook that is sent from transcend ([docs](https://docs.transcend.io/docs/receiving-webhooks)) -- How to perform identity enrichment by responding to a webhook ([docs](https://docs.transcend.io/docs/identity-enrichment)) -- How to respond to ACCESS/ERASURE and other DSR webhooks ([docs](https://docs.transcend.io/docs/responding-to-dsrs)) +- Verify a webhook that is sent from Transcend ([docs](https://docs.transcend.io/docs/receiving-webhooks)) +- Respond to ACCESS/ERASURE and other DSR webhooks ([docs](https://docs.transcend.io/docs/responding-to-dsrs)) + +The JavaScript folder also has an example of how to: + +- Perform identity enrichment by responding to a webhook ([docs](https://docs.transcend.io/docs/identity-enrichment)) Using [ngrok](https://ngrok.com/) you can actually [connect these examples](https://app.transcend.io/data-map/silos?integrationName=server) to your [datamap](https://app.transcend.io/data-map). ## Try it in your environment -To configure these example, you will need to create a file named `.env` and fill it out with your configuration +To configure these examples, you will need to create a file named `.env` and fill it out with your configuration. ```sh TRANSCEND_API_KEY="" @@ -32,27 +35,28 @@ API keys must be scoped to certain operations or data silos. ### SOMBRA_API_KEY -This API key authenticates you to to your sombra gateway. +This API key authenticates you to to your Sombra gateway. -- If you are self hosting sombra, you would have generated this at time of setup and stored it securely. -- If transcend is hosting the gateway on your behalf, you should have received this via some secure channel +- If you're using multi-tenant Sombra (most common) you don't need to set this. +- If you are self-hosting Sombra, you would have generated this at the time of setup and stored it securely. +- If Transcend is hosting the gateway on your behalf in a single-tenant instance, you will receive this via a secure channel. ### ORGANIZATION_URI -This is the unique uri of your organization on transcend found [here](https://app.transcend.io/settings#OrganizationSettings). +This is the unique URI of your organization on Transcend, found [here](https://app.transcend.io/settings#OrganizationSettings). ### SOMBRA_URL -This is the URL of your sombra gateway. +This is the URL of your Sombra gateway. -- If you are self hosting, you assign this value -- If transcend is hosting, it is often at `https://.sombra.transcend.io` +- If you're using multi-tenant Sombra (most common), this is `https://multi-tenant.sombra.transcend.io`. +- If you are self-hosting Sombra, you assign this value. +- If Transcend is hosting the gateway on your behalf in a single-tenant instance, this is `https://.sombra.transcend.io`. ## Languages Check out your language of choice for further details -- [javascript](./javascript) -- [python](./python) -- [ruby](./ruby) -- [typescript](./typescript) +- [JavaScript](./javascript) +- [Python](./python) +- [Ruby](./ruby) diff --git a/javascript/README.md b/javascript/README.md index 54f8b08..1a956b5 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -1,4 +1,4 @@ -# Javascript +# JavaScript Internal system integrations in JavaScript. Check out the parent [README](../README.md) for more context. @@ -14,23 +14,10 @@ yarn yarn start ``` -## Open the browser - -Go to [https://localhost:4445](https://localhost:4445) - -## Add to your datamap - -You can test against this example live by adding it to [your datamap](https://app.transcend.io/data-map/silos?integrationName=server) and using [ngrok](https://ngrok.com/) to map your localhost to a live domain. - -```sh -ngrok http -hostname=test-javascript.ngrok.io 4445 -``` - -## Transcend developer notes +## Transcend-internal developer notes - This code is actively used for live demos. - This is associated with the demo account, [eShopIt](https://e-shop-it.trsnd.co). - It is hosted on [Render](http://render.com/) at . - A design choice was made not to put webhook verification on an Express middleware. It's a nice refactor, but it can be esoteric to readers. - -- Use ngrok +- Use ngrok in dev. diff --git a/javascript/src/constants.js b/javascript/src/constants.js index e617ffe..e0f2189 100644 --- a/javascript/src/constants.js +++ b/javascript/src/constants.js @@ -1,11 +1,17 @@ const { TRANSCEND_API_KEY, ENRICHMENT_SIGNING_KEY, + ORGANIZATION_URI, + SOMBRA_API_KEY, + SOMBRA_URL, PORT, } = process.env; module.exports = { TRANSCEND_API_KEY, ENRICHMENT_SIGNING_KEY, + ORGANIZATION_URI, + SOMBRA_API_KEY, + SOMBRA_URL: SOMBRA_URL || 'https://multi-tenant.sombra.transcend.io', PORT: PORT || 8081, } diff --git a/javascript/src/handleDSRWebhook.js b/javascript/src/handleDSRWebhook.js index f37ac67..87a9853 100644 --- a/javascript/src/handleDSRWebhook.js +++ b/javascript/src/handleDSRWebhook.js @@ -39,6 +39,7 @@ module.exports = asyncHandler(async (req, res, next) => { case 'ERASURE': // Respond with an early "no user found" signal. + // TODO: show an erasure PUT request in this example https://docs.transcend.io/docs/responding-to-dsrs#fulfill-an-erasure-request-dser res.sendStatus(204); break; diff --git a/javascript/src/helpers/createEnricherJwt.js b/javascript/src/helpers/createEnricherJwt.js index 6fe4ecb..d2ebab6 100644 --- a/javascript/src/helpers/createEnricherJwt.js +++ b/javascript/src/helpers/createEnricherJwt.js @@ -4,6 +4,7 @@ const jwt = require('jsonwebtoken'); // Constants const { ENRICHMENT_SIGNING_KEY, + ORGANIZATION_URI, } = require('../constants'); // In this example, the signing key is stored as a base64-encoded env var @@ -22,7 +23,7 @@ module.exports = function createEnricherJwt(content) { algorithm: 'ES384', expiresIn: '1d', // organization URI from https://app.transcend.io/settings#OrganizationSettings - audience: 'e-shop-it', + audience: ORGANIZATION_URI, } ) }; diff --git a/javascript/src/helpers/verifyAndExtractWebhook.js b/javascript/src/helpers/verifyAndExtractWebhook.js index 83c1673..0a50af4 100644 --- a/javascript/src/helpers/verifyAndExtractWebhook.js +++ b/javascript/src/helpers/verifyAndExtractWebhook.js @@ -5,6 +5,7 @@ const jwt = require('jsonwebtoken'); // Constants const { TRANSCEND_API_KEY, + SOMBRA_API_KEY, } = require('../constants'); // Global to cache the webhook signing public key @@ -23,9 +24,10 @@ module.exports = async function verifyAndExtractWebhook(signedToken) { if (!cachedPublicKey) { try { const response = await got.get( - 'https://multi-tenant.sombra.transcend.io/public-keys/sombra-general-signing-key', { + `${SOMBRA_URL}/public-keys/sombra-general-signing-key`, { headers: { authorization: `Bearer ${TRANSCEND_API_KEY}`, + 'x-sombra-authorization': SOMBRA_API_KEY ? `Bearer ${SOMBRA_API_KEY}` : undefined, }, }, ); diff --git a/javascript/src/scheduleAccessRequest.js b/javascript/src/scheduleAccessRequest.js index 6b7417b..69cf5a9 100644 --- a/javascript/src/scheduleAccessRequest.js +++ b/javascript/src/scheduleAccessRequest.js @@ -11,6 +11,8 @@ const pipeline = promisify(stream.pipeline); // Constants const { TRANSCEND_API_KEY, + SOMBRA_API_KEY, + SOMBRA_URL, } = require('./constants'); // Helpers @@ -32,9 +34,10 @@ module.exports = async function scheduleAccessRequest(userIdentifier, nonce, req const userData = await lookUpUser(userIdentifier); // Upload in bulk to datapoints, with a JSON payload - const bulkUpload = got.post('https://multi-tenant.sombra.transcend.io/v1/data-silo', { + const bulkUpload = got.post(`${SOMBRA_URL}/v1/data-silo`, { headers: { authorization: `Bearer ${TRANSCEND_API_KEY}`, + 'x-sombra-authorization': SOMBRA_API_KEY ? `Bearer ${SOMBRA_API_KEY}` : undefined, 'x-transcend-nonce': nonce, accept: 'application/json', 'user-agent': undefined, @@ -46,9 +49,10 @@ module.exports = async function scheduleAccessRequest(userIdentifier, nonce, req // Upload a file to a datapoint const readFile = fs.createReadStream(path.join(__dirname, 'media/big_buck_bunny.mp4')); - const fileUpload = got.stream.post('https://multi-tenant.sombra.transcend.io/v1/datapoint', { + const fileUpload = got.stream.post(`${SOMBRA_URL}/v1/datapoint`, { headers: { authorization: `Bearer ${TRANSCEND_API_KEY}`, + 'x-sombra-authorization': SOMBRA_API_KEY ? `Bearer ${SOMBRA_API_KEY}` : undefined, 'x-transcend-nonce': nonce, accept: 'application/json', 'user-agent': undefined, diff --git a/python/main.py b/python/main.py index c36c59c..b52e658 100644 --- a/python/main.py +++ b/python/main.py @@ -60,6 +60,7 @@ """ Get the sombra public key, used to verify the coreIdentifier +TODO: add authentication headers for multi-tenant Sombra in this example """ def get_transcend_public_key(): res = requests.get(SOMBRA_URL + '/public-keys/sombra-general-signing-key', verify = not TRUST_SELF_SIGNED_CERT) @@ -88,6 +89,7 @@ def get_core_identifier(headers): """ Construct the headers to respond to webhooks with +TODO: make x-sombra-authorization optional in this example """ def response_headers(response_jwt_token): return { @@ -208,8 +210,8 @@ def do_POST(self): def main(): # Create https server httpd = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler) - httpd.socket = ssl.wrap_socket (httpd.socket, - keyfile="ssl/private.key", + httpd.socket = ssl.wrap_socket (httpd.socket, + keyfile="ssl/private.key", certfile='ssl/certificate.pem', server_side=True) # Run diff --git a/ruby/server.rb b/ruby/server.rb index 6523626..a16dafc 100644 --- a/ruby/server.rb +++ b/ruby/server.rb @@ -111,6 +111,7 @@ def perform_access(user, nonce) } # Signal that it has been completed. + # TODO: make x-sombra-authorization optional in this example resp = Faraday.post($DATA_SILO_PATH) do |req| req.headers['Content-Type'] = 'application/json' req.headers['accept'] = 'application/json' @@ -134,6 +135,7 @@ def perform_erasure(user, nonce) } # Signal that it has been completed. + # TODO: make x-sombra-authorization optional in this example resp = Faraday.put($DATA_SILO_PATH) do |req| req.headers['Content-Type'] = 'application/json' req.headers['accept'] = 'application/json' @@ -145,6 +147,7 @@ def perform_erasure(user, nonce) end # Retrieve public key from Sombra +# TODO: add authentication headers for multi-tenant Sombra in this example def transcend_public_key response = Faraday.get($PUBLIC_KEY_URL) OpenSSL::PKey.read(response.body) diff --git a/typescript/README.md b/typescript/README.md deleted file mode 100644 index a33628c..0000000 --- a/typescript/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Typescript - -Internal system integrations in typescript. Check out the parent [README](../README.md) for more context. - -## Installation - -```sh -yarn -``` - -## Start - -```sh -yarn start -``` - -## Open the browser - -Go to [https://localhost:4446](https://localhost:4446) - -## Add to your datamap - -You can test against this example live by adding it to [your datamap](https://app.transcend.io/data-map/silos?integrationName=server) and using [ngrok](https://ngrok.com/) to map your localhost to a live domain. - -```sh -ngrok http -hostname=test-javascript.ngrok.io 4446 -```