diff --git a/demo/.dockerignore b/demo/.dockerignore index e7b180c..e6905a2 100644 --- a/demo/.dockerignore +++ b/demo/.dockerignore @@ -1,2 +1 @@ -node_modules/ -src/static/yarn.lock +.env* \ No newline at end of file diff --git a/demo/.env.example b/demo/.env.example index b294550..f6d79c7 100644 --- a/demo/.env.example +++ b/demo/.env.example @@ -1,4 +1,7 @@ EXAMPLE_SERVER_PORT=8080 +PORT=8080 BASE_URL=https://eu.rp.secure.iproov.me API_KEY= API_SECRET= + +NPM_ACCESS_TOKEN= \ No newline at end of file diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/demo/.prettierrc b/demo/.prettierrc deleted file mode 100644 index 0d126be..0000000 --- a/demo/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "bracketSpacing": true, - "printWidth": 120, - "quoteProps": "consistent", - "semi": false, - "singleQuote": false, - "trailingComma": "es5", - "tabWidth": 2 -} diff --git a/demo/Dockerfile b/demo/Dockerfile index 7cdf37e..b59cee5 100644 --- a/demo/Dockerfile +++ b/demo/Dockerfile @@ -1,13 +1,42 @@ -FROM node:lts-alpine +# BASE +FROM node:18-alpine as base + +ARG NPM_ACCESS_TOKEN="${NPM_ACCESS_TOKEN}" +ENV NPM_ACCESS_TOKEN $NPM_ACCESS_TOKEN + + +# INSTALLER +FROM base as installer + +WORKDIR /app + +COPY ./src ./src +COPY ./package.json ./package.json +COPY ./yarn.lock ./yarn.lock + +# 1. install server deps +RUN yarn install --ignore-scripts --frozen-lockfile + +# XXX:NOTE: There are no npm build for the FE app +# 2. install separately only web-sdk in "static" as static module +RUN npm config set '//registry.npmjs.org/:_authToken' "${NPM_ACCESS_TOKEN}" +RUN cd src/static && yarn install --ignore-scripts --frozen-lockfile + + +# BUILDER +FROM base as builder + +WORKDIR /app + +# There are no build for the server - just copy what is required +COPY --from=installer /app . + + +# RUNNER +FROM base as runner WORKDIR /app -ADD src/yarn.lock . -ADD src/package.json . -RUN yarn -ADD src/static/package.json ./static/ -RUN cd /app/static && yarn && cd /app -ADD src/ . -EXPOSE 80 +COPY --from=builder /app . -ENTRYPOINT ["node", "server.js"] +CMD yarn start \ No newline at end of file diff --git a/demo/README.md b/demo/README.md index dc911e6..2c51ec0 100644 --- a/demo/README.md +++ b/demo/README.md @@ -2,7 +2,7 @@ This demo is a self-contained NodeJS server and minimal frontend which covers: -* Running a server with a `BASE_URL`, `API_KEY` and `API_SECRET` that you can obtain from https://portal.iproov.com; +* Running a server with a `BASE_URL`, `API_KEY`, `API_SECRET` that you can obtain from https://portal.iproov.com and `NPM_ACCESS_TOKEN` that you can create once your get access to our private NPM registry (please contact support@iproov.com). * Creating tokens for Genuine Presence and Liveness transactions; * Enrolling and verifying; * Customising iProov Web with web component slots and CustomEvents. diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 0000000..f1b4b3c --- /dev/null +++ b/demo/package.json @@ -0,0 +1,21 @@ +{ + "name": "@iproov/web-demo-sample", + "version": "2.0.0", + "description": "A demo of iProov's Web SDK integration", + "main": "server.js", + "author": "iProov Team", + "license": "GPL", + "private": true, + "type": "module", + "scripts": { + "start": "node src/server.js" + }, + "dependencies": { + "@hapi/basic": "^6.0.0", + "@hapi/hapi": "^20.2.2", + "@hapi/inert": "^6.0.4", + "dotenv": "^8.6.0", + "prettier": "^2.7.1", + "superagent": "^6.1.0" + } +} diff --git a/demo/run.sh b/demo/run.sh index d93c7ba..8be81d7 100644 --- a/demo/run.sh +++ b/demo/run.sh @@ -6,6 +6,8 @@ source "$ENV_FILE" EXAMPLE_SERVER_PORT=${EXAMPLE_SERVER_PORT:=8080} +yarn install + if [ ! -r "$ENV_FILE" ] then echo "Missing environment file: $ENV_FILE" @@ -13,5 +15,5 @@ if [ ! -r "$ENV_FILE" ] exit 1 fi -docker build . -t iproov-web-example -docker run --env-file "$ENV_FILE" -e EXAMPLE_SERVER_PORT="$EXAMPLE_SERVER_PORT" -e IS_DOCKER=1 -p "0.0.0.0:$EXAMPLE_SERVER_PORT":80 --rm iproov-web-example +docker build -t iproov-web-demo --build-arg NPM_ACCESS_TOKEN=$NPM_ACCESS_TOKEN . +docker run --env-file "$ENV_FILE" -e EXAMPLE_SERVER_PORT="$EXAMPLE_SERVER_PORT" -e PORT="$PORT" -p "$EXAMPLE_SERVER_PORT":"$PORT" --rm iproov-web-demo \ No newline at end of file diff --git a/demo/src/config.js b/demo/src/config.js index 01e724b..3fb570a 100644 --- a/demo/src/config.js +++ b/demo/src/config.js @@ -5,38 +5,46 @@ * @return {{API_KEY: *, API_SECRET: *, BASE_URL: *, EXAMPLE_SERVER_PORT: *, PORT: *}} */ export function configure(env) { - const { BASE_URL, API_KEY, API_SECRET } = env - let { PORT, EXAMPLE_SERVER_PORT } = env - const errors = [] + const { BASE_URL, API_KEY, API_SECRET } = env; + let { PORT, EXAMPLE_SERVER_PORT } = env; + const errors = []; if (!BASE_URL) { - errors.push("BASE_URL is undefined. Example: https://eu.rp.secure.iproov.me.") + errors.push( + "BASE_URL is undefined. Example: https://eu.rp.secure.iproov.me." + ); } if (!API_KEY) { - errors.push("API_KEY is undefined. You can obtain one from https://portal.iproov.com.") + errors.push( + "API_KEY is undefined. You can obtain one from https://portal.iproov.com." + ); } else if (API_KEY.length < 40) { - errors.push("API_KEY seems incorrect, it should be a 40 character alphanumeric string.") + errors.push( + "API_KEY seems incorrect, it should be a 40 character alphanumeric string." + ); } if (!API_SECRET) { errors.push( "API_SECRET is undefined. It must correspond to your API_KEY. If lost, reset it at https://portal.iproov.com." - ) + ); } else if (API_SECRET.length < 40) { - errors.push("API_SECRET seems incorrect, it should be a 40 character alphanumeric string.") + errors.push( + "API_SECRET seems incorrect, it should be a 40 character alphanumeric string." + ); } + if (!PORT) { // This is the internal container port that the EXAMPLE_SERVER_PORT should map to. Best left at 80. - PORT = 80 + PORT = 80; } if (!EXAMPLE_SERVER_PORT) { - console.warn("EXAMPLE_SERVER_PORT not specified, using port 80.") - EXAMPLE_SERVER_PORT = 80 + console.warn("EXAMPLE_SERVER_PORT not specified, using port 80."); + EXAMPLE_SERVER_PORT = 80; } if (errors.length) { - console.error("Configuration problems detected:") - errors.forEach((e) => console.error(e)) - console.error("Contact support@iproov.com for assistance.") - process.exit(1) - return + console.error("Configuration problems detected:"); + errors.forEach((e) => console.error(e)); + console.error("Contact support@iproov.com for assistance."); + process.exit(1); } return { BASE_URL, @@ -44,5 +52,5 @@ export function configure(env) { API_SECRET, PORT, EXAMPLE_SERVER_PORT, - } + }; } diff --git a/demo/src/etc/.gitignore b/demo/src/etc/.gitignore new file mode 100644 index 0000000..275ece2 --- /dev/null +++ b/demo/src/etc/.gitignore @@ -0,0 +1,2 @@ +*.key +*.crt diff --git a/demo/src/package.json b/demo/src/package.json deleted file mode 100644 index 4a4f555..0000000 --- a/demo/src/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@iproov/web-example", - "version": "1.0.0", - "description": "An example of iProov's Web SDK integration", - "main": "server.js", - "author": "iProov Team", - "license": "GPL", - "private": true, - "type": "module", - "dependencies": { - "@hapi/basic": "^6.0.0", - "@hapi/hapi": "^20.2.1", - "@hapi/inert": "^6.0.4", - "dotenv": "^8.2.0", - "prettier": "^2.2.1", - "superagent": "^6.1.0" - } -} diff --git a/demo/src/platform.js b/demo/src/platform.js index 3274237..177ef0b 100644 --- a/demo/src/platform.js +++ b/demo/src/platform.js @@ -1,4 +1,4 @@ -import superagent from "superagent" +import superagent from "superagent"; /** * Lightweight Platform v2 wrapper @@ -6,14 +6,14 @@ import superagent from "superagent" */ export class PlatformAPI { constructor(logger, baseUrl, apiKey, apiSecret) { - this.logger = logger - this.baseUrl = baseUrl - this.apiKey = apiKey - this.apiSecret = apiSecret + this.logger = logger; + this.baseUrl = baseUrl; + this.apiKey = apiKey; + this.apiSecret = apiSecret; } apiUrl(endpoint) { - return `${this.baseUrl}/api/v2/${endpoint}` + return `${this.baseUrl}/api/v2/${endpoint}`; } withJsonAuth(payload) { @@ -21,14 +21,14 @@ export class PlatformAPI { api_key: this.apiKey, secret: this.apiSecret, ...payload, - } + }; } withHeaders() { return { - "accept": "json", + accept: "json", "user-agent": "superagent", - } + }; } /** @@ -38,28 +38,28 @@ export class PlatformAPI { */ async unpack(request) { try { - const response = await request - return { ...response.body, base_url: this.baseUrl } + const response = await request; + return { ...response.body, base_url: this.baseUrl }; } catch (e) { // @todo: indicate this is an error - currently we just pass through if (e.status === 400) { - const { error, error_description } = e.response.body - this.logger.error("Error %s: %s", error, error_description) + const { error, error_description } = e.response.body; + this.logger.error("Error %s: %s", error, error_description); } - return e.response.body + return e.response.body; } } async token(mode, options = {}) { - const { userId, assuranceType } = options + const { userId, assuranceType } = options; if (!userId) { - this.logger.warn(`Couldn't create a token because user_id is empty`) + this.logger.warn(`Couldn't create a token because user_id is empty`); return { error: "Missing User ID", error_description: "You must provide a user_id to create a token.", - } + }; } - this.logger.info(`Creating ${assuranceType} ${mode} token for ${userId}`) + this.logger.info(`Creating ${assuranceType} ${mode} token for ${userId}`); return this.unpack( superagent .post(this.apiUrl(`claim/${mode}/token`)) @@ -68,15 +68,15 @@ export class PlatformAPI { this.withJsonAuth({ user_id: userId, assurance_type: assuranceType, - resource: "Web SDK Example", + resource: "Web SDK Demo", }) ) - ) + ); } async validate(mode, options = { userId, token }) { - const { userId, token } = options - this.logger.info(`Validating ${mode} for ${userId} - token: ${token}`) + const { userId, token } = options; + this.logger.info(`Validating ${mode} for ${userId} - token: ${token}`); return this.unpack( superagent .post(this.apiUrl(`claim/${mode}/validate`)) @@ -89,7 +89,7 @@ export class PlatformAPI { client: "superagent", }) ) - ) + ); } } @@ -101,12 +101,12 @@ export const requestToAPIMapper = Object.freeze({ return { userId: request.payload.user_id, assuranceType: request.payload.assurance_type || "genuine_presence", - } + }; }, validate(request) { return { token: request.payload.token, userId: request.payload.user_id, - } + }; }, -}) +}); diff --git a/demo/src/server.js b/demo/src/server.js index e420866..4a17f6c 100644 --- a/demo/src/server.js +++ b/demo/src/server.js @@ -1,25 +1,31 @@ -import Hapi from "@hapi/hapi" -import Inert from "@hapi/inert" -import { fileURLToPath } from "url" -import { dirname, resolve } from "path" -import { PlatformAPI, requestToAPIMapper } from "./platform.js" +import Hapi from "@hapi/hapi"; +import Inert from "@hapi/inert"; +import { fileURLToPath } from "url"; +import { dirname, resolve } from "path"; +import { PlatformAPI, requestToAPIMapper } from "./platform.js"; -import { configure } from "./config.js" +import { configure } from "./config.js"; -const __dirname = fileURLToPath(dirname(import.meta.url)) +const __dirname = fileURLToPath(dirname(import.meta.url)); async function init() { - const { BASE_URL, API_KEY, API_SECRET, PORT, EXAMPLE_SERVER_PORT } = configure(process.env) + const { + BASE_URL, + API_KEY, + API_SECRET, + PORT, + EXAMPLE_SERVER_PORT, + } = configure(process.env); - const ALL_INTERFACES = "0.0.0.0" // listen on all interfaces so Docker can bind + const ALL_INTERFACES = "0.0.0.0"; // listen on all interfaces so Docker can bind const server = Hapi.server({ port: PORT, host: ALL_INTERFACES, - }) + }); - await server.register(Inert) + await server.register(Inert); - let platform + let platform; server.route({ method: "GET", @@ -31,41 +37,51 @@ async function init() { index: "index.html", }, }, - }) + }); server.route({ method: "POST", path: "/api/claim/{claimMode}/{claimAction}", options: { cors: { origin: ["*"] } }, handler: async (request, h) => { - const { claimMode, claimAction } = request.params + const { claimMode, claimAction } = request.params; try { - return await platform[claimAction](claimMode, requestToAPIMapper[claimAction](request)) + return await platform[claimAction]( + claimMode, + requestToAPIMapper[claimAction](request) + ); } catch (e) { - console.error("Unhandled: %s", e.message) - throw e + console.error("Unhandled: %s", e.message); + throw e; } }, - }) + }); - platform = new PlatformAPI(console, BASE_URL, API_KEY, API_SECRET) + platform = new PlatformAPI(console, BASE_URL, API_KEY, API_SECRET); - console.debug("Starting internal server: %s", server.info.uri) + console.debug("Starting internal server: %s", server.info.uri); - await server.start() + await server.start(); - console.log("iProov tokens will be created on %s with API_KEY %s", BASE_URL, API_KEY) - console.log("Web SDK example available at %s", "http://localhost:" + EXAMPLE_SERVER_PORT) + console.log( + "iProov tokens will be created on %s with API_KEY %s", + BASE_URL, + API_KEY + ); + console.log( + "Web SDK example available at %s", + "http://localhost:" + EXAMPLE_SERVER_PORT + ); } process.on("unhandledRejection", (err) => { - console.log(err) - process.exit(1) -}) + console.log(err); + process.exit(1); +}); process.on("SIGINT", () => { - console.log("Caught SIGINT - shutting down.") - process.exit(0) -}) + console.log("Caught SIGINT - shutting down."); + process.exit(0); +}); -init() +init(); diff --git a/demo/src/static/404.html b/demo/src/static/404.html index 260cc4c..2efc797 100644 --- a/demo/src/static/404.html +++ b/demo/src/static/404.html @@ -1,62 +1,58 @@ - + +
+ +Sorry, but the page you were trying to view does not exist.
- + h1 { + font-size: 1.5em; + margin: 0 0 0.3em; + } + } + + + +Sorry, but the page you were trying to view does not exist.
+