diff --git a/lib/flow-typed/npm/base-64_v0.1.x.js b/lib/flow-typed/npm/base-64_v0.1.x.js new file mode 100644 index 000000000..f5b867676 --- /dev/null +++ b/lib/flow-typed/npm/base-64_v0.1.x.js @@ -0,0 +1,26 @@ +// flow-typed signature: 350bbd47d9063ecee4c33220f3f6fa99 +// flow-typed version: c6154227d1/base-64_v0.1.x/flow_>=v0.25.x <=v0.103.x + +declare module 'base-64' { + declare module.exports: { + version: string; + /** + * This function takes a byte string (the input parameter) and encodes it according to base64. + * The input data must be in the form of a string containing only characters + * in the range from U+0000 to U+00FF, each representing a binary byte with values 0x00 to 0xFF. + * The base64.encode() function is designed to be fully compatible + * with btoa() as described in the HTML Standard. + * see: https://html.spec.whatwg.org/multipage/webappapis.html#dom-windowbase64-btoa + */ + encode(input: string): string; + /** + * This function takes a base64-encoded string (the input parameter) and decodes it. + * The return value is in the form of a string containing only characters in + * the range from U+0000 to U+00FF, each representing a binary byte with values 0x00 to 0xFF. + * The base64.decode() function is designed to be fully compatible + * with atob() as described in the HTML Standard. + * see: https://html.spec.whatwg.org/multipage/webappapis.html#dom-windowbase64-atob + */ + decode(input: string): string; + }; +} diff --git a/lib/package.json b/lib/package.json index 3e2ee4eb0..89a063588 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,82 +1,83 @@ { "name": "lib", "version": "0.0.1", "type": "module", "private": true, "license": "BSD-3-Clause", "scripts": { "clean": "rm -rf node_modules/", "test": "jest" }, "devDependencies": { "@babel/core": "^7.23.7", "@babel/plugin-transform-class-properties": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-runtime": "^7.23.7", "@babel/preset-env": "^7.23.7", "@babel/preset-flow": "^7.23.3", "@babel/preset-react": "^7.23.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "babel-jest": "^29.7.0", "babel-loader": "^9.1.3", "buffer": "^6.0.3", "clean-webpack-plugin": "^4.0.0", "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^4.2.2", "flow-bin": "^0.202.1", "flow-typed": "^3.2.1", "jest": "^29.7.0", "mini-css-extract-plugin": "^2.7.2", "react-refresh": "^0.14.0", "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.6", "webpack": "^5.76.0" }, "dependencies": { "@commapp/olm": "0.1.0", "@rainbow-me/rainbowkit": "^1.1.1", + "base-64": "^0.1.0", "dateformat": "^3.0.3", "emoji-regex": "^10.2.1", "eth-ens-namehash": "^2.0.8", "ethers": "^5.7.2", "fast-json-stable-stringify": "^2.0.0", "file-type": "^12.3.0", "focus-trap-react": "^10.1.4", "idna-uts46-hx": "^2.3.1", "invariant": "^2.2.4", "just-clone": "^3.2.1", "lodash": "^4.17.21", "react": "18.1.0", "react-icomoon": "^2.5.7", "react-redux": "^7.1.1", "redux-persist": "^6.0.0", "reselect": "^4.0.0", "reselect-map": "^1.0.5", "simple-markdown": "^0.7.2", "siwe": "^1.1.6", "string-hash": "^1.1.3", "tcomb": "^3.2.29", "tinycolor2": "^1.4.1", "tokenize-text": "^1.1.3", "util-inspect": "^0.1.8", "utils-copy-error": "^1.0.1", "uuid": "^3.4.0", "viem": "^1.15.4", "wagmi": "^1.4.3" }, "jest": { "transform": { "\\.js$": [ "babel-jest", { "rootMode": "upward" } ] }, "transformIgnorePatterns": [ "/node_modules/(?!@babel/runtime)" ] } } diff --git a/lib/utils/services-utils.js b/lib/utils/services-utils.js index e1b0e74e5..7dcb88743 100644 --- a/lib/utils/services-utils.js +++ b/lib/utils/services-utils.js @@ -1,12 +1,28 @@ // @flow +import base64 from 'base-64'; + +import type { AuthMetadata } from '../shared/identity-client-context.js'; + const usingCommServicesAccessToken = false; function handleHTTPResponseError(response: Response): void { if (!response.ok) { const { status, statusText } = response; throw new Error(`Server responded with HTTP ${status}: ${statusText}`); } } -export { handleHTTPResponseError, usingCommServicesAccessToken }; +function createHTTPAuthorizationHeader(authMetadata: AuthMetadata): string { + // explicit destructure to make it future-proof + const { userID, deviceID, accessToken } = authMetadata; + const payload = JSON.stringify({ userID, deviceID, accessToken }); + const base64EncodedPayload = base64.encode(payload); + return `Bearer ${base64EncodedPayload}`; +} + +export { + handleHTTPResponseError, + usingCommServicesAccessToken, + createHTTPAuthorizationHeader, +}; diff --git a/lib/utils/services-utils.test.js b/lib/utils/services-utils.test.js new file mode 100644 index 000000000..86fc0091b --- /dev/null +++ b/lib/utils/services-utils.test.js @@ -0,0 +1,21 @@ +// @flow + +import base64 from 'base-64'; + +import { createHTTPAuthorizationHeader } from './services-utils.js'; + +describe('createHTTPAuthorizationHeader', () => { + it('should return a Bearer token with base64-encoded payload', () => { + const authMetadata = { + userID: 'foo', + deviceID: 'bar', + accessToken: 'baz', + }; + const expectedHeader = `Bearer ${base64.encode( + JSON.stringify(authMetadata), + )}`; + + const result = createHTTPAuthorizationHeader(authMetadata); + expect(result).toBe(expectedHeader); + }); +});