From ede9f1a4f41dbec5306c27af7927347bdbd71fd7 Mon Sep 17 00:00:00 2001 From: Paulo Cabral Date: Thu, 29 Oct 2020 10:24:24 +0000 Subject: [PATCH] Add authentication sample using client credentials --- .../client-credential-flow/.env.example | 5 ++ .../client-credential-flow/.gitignore | 3 + .../client-credential-flow/README.md | 29 +++++++ .../client-credential-flow/cc-flow.js | 64 ++++++++++++++ .../client-credential-flow/index.js | 12 +++ .../client-credential-flow/package.json | 22 +++++ .../client-credential-flow/yarn.lock | 86 +++++++++++++++++++ 7 files changed, 221 insertions(+) create mode 100644 rest-api/javascript/authentication/client-credential-flow/.env.example create mode 100644 rest-api/javascript/authentication/client-credential-flow/.gitignore create mode 100644 rest-api/javascript/authentication/client-credential-flow/README.md create mode 100644 rest-api/javascript/authentication/client-credential-flow/cc-flow.js create mode 100644 rest-api/javascript/authentication/client-credential-flow/index.js create mode 100644 rest-api/javascript/authentication/client-credential-flow/package.json create mode 100644 rest-api/javascript/authentication/client-credential-flow/yarn.lock diff --git a/rest-api/javascript/authentication/client-credential-flow/.env.example b/rest-api/javascript/authentication/client-credential-flow/.env.example new file mode 100644 index 0000000..b5bc13d --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/.env.example @@ -0,0 +1,5 @@ +# Base Url endpoint +BASE_URL='https://api-sandbox.uphold.com' + +CLIENT_ID = '' +CLIENT_SECRET = '' diff --git a/rest-api/javascript/authentication/client-credential-flow/.gitignore b/rest-api/javascript/authentication/client-credential-flow/.gitignore new file mode 100644 index 0000000..28a76fd --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules/ +package-lock.json diff --git a/rest-api/javascript/authentication/client-credential-flow/README.md b/rest-api/javascript/authentication/client-credential-flow/README.md new file mode 100644 index 0000000..4a83185 --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/README.md @@ -0,0 +1,29 @@ +# Client credentials flow + +This sample project demonstrates how to authenticate in the Uphold API using the client credentials flow. For further background, please refer to the [API documentation](https://uphold.com/en/developer/api/documentation). + +## Summary + +**Ideal for backend integrations** that do not require access to other Uphold user accounts. + +This sample project performs the following actions: + +- Get Token +- List Assets + +**Important notice:** In Uphold's production environment, client credentials authentication is only available for **business accounts**, and requires manual approval from Uphold. +Please [contact Uphold](mailto:developer@uphold.com) to obtain this permission. +For applications that use the sandbox environment, as is the case with this demo project, those requirements can be skipped. + +## Requirements + +- `node` v13.14.0 + + +## Setup + +- run `npm install` (or `yarn install`) +- create a `.env` file based on the `.env.example` file, and populate it with the required data + +## Run + +- run `node index.js` (or `npm run`) diff --git a/rest-api/javascript/authentication/client-credential-flow/cc-flow.js b/rest-api/javascript/authentication/client-credential-flow/cc-flow.js new file mode 100644 index 0000000..584440a --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/cc-flow.js @@ -0,0 +1,64 @@ +/** + * Dependencies. + */ + +import axios from "axios"; +import b64Pkg from "js-base64"; +import dotenv from "dotenv"; +import path from "path"; +import qs from "qs"; +const { encode } = b64Pkg; + +/** + * Dotenv configuration. + */ +dotenv.config({ path: path.resolve() + "/.env" }); + +/** + * Get Token. + */ +export async function getToken() { + // Base64-encoded authentication credentials + const auth = encode(process.env.CLIENT_ID + ":" + process.env.CLIENT_SECRET); + + // Options for the Axios request + const options = { + method: "POST", + headers: { + Authorization: "Basic " + auth, + "content-type": "application/x-www-form-urlencoded", + }, + data: qs.stringify({ grant_type: "client_credentials" }), + url: `${process.env.BASE_URL}/oauth2/token`, + }; + + const data = axios(options) + .then((response) => { + return response.data; + }) + .catch((error) => { + error.response.data.errors + ? console.log(JSON.stringify(error.response.data.errors, null, 2)) + : console.log(JSON.stringify(error, null, 2)); + throw error; + }); + + return data; +} + +/** + * Get assets. + */ +export async function getAssets(token) { + try { + const response = await axios.get(`${process.env.BASE_URL}/v0/assets`, { + headers: { + Authorization: `${token.token_type} ${token.access_token}`, + }, + }); + return response.data; + } catch (error) { + console.log(JSON.stringify(error, null, 2)); + throw error; + } +} diff --git a/rest-api/javascript/authentication/client-credential-flow/index.js b/rest-api/javascript/authentication/client-credential-flow/index.js new file mode 100644 index 0000000..e00b831 --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/index.js @@ -0,0 +1,12 @@ +/** + * Dependencies. + */ + +import { getAssets, getToken } from "./cc-flow.js"; + +(async () => { + // Get `bearer` token from sandbox + const token = await getToken(); + // Log the output of an API call using the token, to confirm that it works + console.log(await getAssets(token)); +})(); diff --git a/rest-api/javascript/authentication/client-credential-flow/package.json b/rest-api/javascript/authentication/client-credential-flow/package.json new file mode 100644 index 0000000..048f67c --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/package.json @@ -0,0 +1,22 @@ +{ + "name": "uphold-ccf", + "version": "0.0.1", + "description": "Uphold Client Credential Flow Example", + "license": "MIT", + "main": "index.js", + "type": "module", + "dependencies": { + "axios": "^0.20.0", + "btoa": "^1.2.1", + "dotenv": "^8.2.0", + "js-base64": "^3.5.2", + "qs": "^6.9.4", + "require": "^2.4.20" + }, + "engines": { + "node": ">=13.14" + }, + "scripts": { + "run": "node index.js " + } +} diff --git a/rest-api/javascript/authentication/client-credential-flow/yarn.lock b/rest-api/javascript/authentication/client-credential-flow/yarn.lock new file mode 100644 index 0000000..1a49cde --- /dev/null +++ b/rest-api/javascript/authentication/client-credential-flow/yarn.lock @@ -0,0 +1,86 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= + +axios@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" + integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== + dependencies: + follow-redirects "^1.10.0" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +follow-redirects@^1.10.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + +js-base64@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.5.2.tgz#3cc800e4f10812b55fb5ec53e7cabaef35dc6d3c" + integrity sha512-VG2qfvV5rEQIVxq9UmAVyWIaOdZGt9M16BLu8vFkyWyhv709Hyg4nKUb5T+Ru+HmAr9RHdF+kQDKAhbJlcdKeQ== + +optimist@~0.3.5: + version "0.3.7" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + integrity sha1-yQlBrVnkJzMokjB00s8ufLxuwNk= + dependencies: + wordwrap "~0.0.2" + +qs@^6.9.4: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + +require@^2.4.20: + version "2.4.20" + resolved "https://registry.yarnpkg.com/require/-/require-2.4.20.tgz#66cb6baaabb65de8a71d793f5c65fd184f3798b6" + integrity sha1-Zstrqqu2XeinHXk/XGX9GE83mLY= + dependencies: + std "0.1.40" + uglify-js "2.3.0" + +source-map@~0.1.7: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= + dependencies: + amdefine ">=0.0.4" + +std@0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/std/-/std-0.1.40.tgz#3678a5f65094d9e1b6b5e26edbfc0212b8342b71" + integrity sha1-Nnil9lCU2eG2teJu2/wCErg0K3E= + +uglify-js@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.3.0.tgz#2cdec16d378a8a2b6ecfb6989784cf8b7ae5491f" + integrity sha1-LN7BbTeKiituz7aYl4TPi3rlSR8= + dependencies: + async "~0.2.6" + optimist "~0.3.5" + source-map "~0.1.7" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=